doc: update software design guidelines

This patch adds the guidelines for 'Module Level
Configuration Design'.

Signed-off-by: Shiqing Gao <shiqing.gao@intel.com>
This commit is contained in:
Shiqing Gao 2019-03-19 08:44:47 +08:00 committed by David Kinder
parent efad496352
commit 79582b996f
2 changed files with 230 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -450,6 +450,236 @@ boot information.
Yes. Because 'vdev->ops' and 'vdev->ops->init' can not be guaranteed to be
not NULL. If the VM called ``partition_mode_vpci_deinit`` twice, it may be NULL.
Module Level Configuration Design Guidelines
********************************************
Design Goals
============
There are two goals for module level configuration design, as shown below:
a) In order to make the hypervisor more flexible, one source code and binary
is preferred for different platforms with different configurations;
b) If one module is not used by a specific project, the module source code is
treated as dead code. The effort to configure it in/out shall be minimized.
Hypervisor Operation Modes
==========================
The hypervisor operation modes are shown in
:numref:`hypervisor_operation_modes` below.
.. table:: Hypervisor Operation Modes
:align: center
:widths: 10 10 50
:name: hypervisor_operation_modes
+-------------+-----------+------------------------------------------------------------------------------+
| Operation | Sub-modes | Description |
| Modes | | |
+=============+===========+==============================================================================+
| INIT mode | DETECT | The hypervisor detects firmware, detects hardware resource, and reads |
| | mode | configuration data. |
| +-----------+------------------------------------------------------------------------------+
| | STARTUP | The hypervisor initializes hardware resources, creates virtual resources like|
| | mode | VCPU and VM, and executes VMLAUNCH instruction(the very first VM entry). |
+-------------+-----------+------------------------------------------------------------------------------+
| OPERATIONAL | N/A | After the first VM entry, the hypervisor runs in VMX root mode and guest OS |
| mode | | runs in VMX non-root mode. |
+-------------+-----------+------------------------------------------------------------------------------+
| TERMINATION | N/A | If any fatal error is detected, the hypervisor will enter TERMINATION mode. |
| mode | | In this mode, a default fatal error handler will be invoked to handle the |
| | | fatal error. |
+-------------+-----------+------------------------------------------------------------------------------+
Configurable Module Properties
==============================
The properties of configurable modules are shown below:
- The functionality of the module depends on platform configurations;
- Corresponding platform configurations can be detected in DETECT mode;
- The module APIs shall be configured in DETECT mode;
- The module APIs shall be used in modes other than DETECT mode.
Platform configurations include:
- Features depending on hardware or firmware
- Configuration data provided by firmware
- Configuration data provided by BSP
Design Rules
============
The module level configuration design rules are shown below:
1. The platform configurations shall be detectable by hypervisor in DETECT mode;
2. Configurable module APIs shall be abstracted as operations which are
implemented through a set of function pointers in the operations data
structure;
3. Every function pointer in the operations data structure shall be instantiated
as one module API in DETECT mode and the API is allowed to be implemented as
empty function for some specific configurations;
4. The operations data structure shall be read-only in STARTUP mode, OPERATIONAL
mode, and TERMINATION mode;
5. The configurable module shall only be accessed via APIs in the operations
data structure in STARTUP mode or OPERATIONAL mode;
6. In order to guarantee that the function pointer in the operations data
structure is dereferenced after it has been instantiated, the pre-condition
shall be added for the function which deferences the function pointer,
instead of checking the pointer for NULL.
.. note:: The third rule shall be double checked during code review.
Use Cases
=========
The following table shows some use cases of module level configuration design:
.. list-table:: Module Level Configuration Design Use Cases
:widths: 10 25 20
:header-rows: 1
* - **Platform Configuration**
- **Configurable Module**
- **Prerequisite**
* - Features depending on hardware or firmware
- This module is used to virtualize part of LAPIC functionalities.
It can be done via APICv or software emulation depending on CPU
capabilities.
For example, KBL NUC doesn't support virtual-interrupt delivery, while
other platforms support it.
- If a function pointer is used, the prerequisite is
"hv_operation_mode == OPERATIONAL".
* - Configuration data provided by firmware
- This module is used to interact with firmware (UEFI or SBL), and the
configuration data is provided by firmware.
For example, UP2 uses SBL and KBL NUC uses UEFI.
- If a function pointer is used, the prerequisite is
"hv_operation_mode != DETECT".
* - Configuration data provided by BSP
- This module is used to virtualize LAPIC, and the configuration data is
provided by BSP.
For example, some VMs use LAPIC pass-through and the other VMs use
vLAPIC.
- If a function pointer is used, the prerequisite is
"hv_operation_mode == OPERATIONAL".
.. note:: Prerequisite is used to guarantee that the function pointer used for
configuration is dereferenced after it has been instantiated.
Examples
========
Take the module for parsing boot information as an example to illustrate the
idea of module level configuration design.
.. figure:: images/boot_information_parsing_module.png
:align: center
:scale: 70 %
:name: boot_information_parsing_module
Boot Information Parsing Module
As shown in the source code below, 'struct firmware_operations' is an operations
data structure that contains a set of function pointers.
Different firmware may have different implementations:
- 'firmware_uefi_ops' is for UEFI platform;
- 'firmware_sbl_ops' is for SBL platform.
.. code-block:: c
struct firmware_operations {
void (*init)(void);
uint64_t (*get_ap_trampoline)(void);
void *(*get_rsdp)(void);
void (*init_irq)(void);
int32_t (*init_vm_boot_info)(struct acrn_vm *vm);
};
static struct firmware_operations firmware_uefi_ops = {
.init = uefi_init,
.get_ap_trampoline = uefi_get_ap_trampoline,
.get_rsdp = uefi_get_rsdp,
.init_irq = uefi_init_irq,
.init_vm_boot_info = uefi_init_vm_boot_info,
};
static struct firmware_operations firmware_sbl_ops = {
.init = sbl_init,
.get_ap_trampoline = sbl_get_ap_trampoline,
.get_rsdp = sbl_get_rsdp,
.init_irq = sbl_init_irq,
.init_vm_boot_info = sbl_init_vm_boot_info,
};
'firmware_ops' is the operations set that is dereferenced and takes effect.
'init_firmware_operations' is called when the hypervisor is in DETECT mode and
'firmware_ops' is instantiated here to either 'firmware_uefi_ops' or
'firmware_sbl_ops' depending on the platform.
.. note:: All the other exported interfaces using 'firmware_ops' shall be called
after the instantiation.
.. code-block:: c
static struct firmware_operations *firmware_ops;
struct firmware_operations* uefi_get_firmware_operations(void)
{
return &firmware_uefi_ops;
}
struct firmware_operations* sbl_get_firmware_operations(void)
{
return &firmware_sbl_ops;
}
void init_firmware_operations(void)
{
if (is_firmware_sbl()) {
firmware_ops = sbl_get_firmware_operations();
} else {
firmware_ops = uefi_get_firmware_operations();
}
}
For example, when the hypervisor needs to initialize the VM boot information,
it calls 'firmware_init_vm_boot_info' and 'firmware_ops->init_vm_boot_info' is
dereferenced here with correct API being called.
.. code-block:: c
/**
* @pre firmware_ops->init_vm_boot_info != NULL
*/
int32_t firmware_init_vm_boot_info(struct acrn_vm *vm)
{
return firmware_ops->init_vm_boot_info(vm);
}
References
**********