@@ -450,6 +450,236 @@ boot information.
450
450
Yes. Because 'vdev->ops' and 'vdev->ops->init' can not be guaranteed to be
451
451
not NULL. If the VM called ``partition_mode_vpci_deinit `` twice, it may be NULL.
452
452
453
+
454
+ Module Level Configuration Design Guidelines
455
+ ********************************************
456
+
457
+ Design Goals
458
+ ============
459
+
460
+ There are two goals for module level configuration design, as shown below:
461
+
462
+ a) In order to make the hypervisor more flexible, one source code and binary
463
+ is preferred for different platforms with different configurations;
464
+
465
+ b) If one module is not used by a specific project, the module source code is
466
+ treated as dead code. The effort to configure it in/out shall be minimized.
467
+
468
+
469
+ Hypervisor Operation Modes
470
+ ==========================
471
+
472
+ The hypervisor operation modes are shown in
473
+ :numref: `hypervisor_operation_modes ` below.
474
+
475
+ .. table :: Hypervisor Operation Modes
476
+ :align: center
477
+ :widths: 10 10 50
478
+ :name: hypervisor_operation_modes
479
+
480
+ +-------------+-----------+------------------------------------------------------------------------------+
481
+ | Operation | Sub-modes | Description |
482
+ | Modes | | |
483
+ +=============+===========+==============================================================================+
484
+ | INIT mode | DETECT | The hypervisor detects firmware, detects hardware resource, and reads |
485
+ | | mode | configuration data. |
486
+ | +-----------+------------------------------------------------------------------------------+
487
+ | | STARTUP | The hypervisor initializes hardware resources, creates virtual resources like|
488
+ | | mode | VCPU and VM, and executes VMLAUNCH instruction(the very first VM entry). |
489
+ +-------------+-----------+------------------------------------------------------------------------------+
490
+ | OPERATIONAL | N/A | After the first VM entry, the hypervisor runs in VMX root mode and guest OS |
491
+ | mode | | runs in VMX non-root mode. |
492
+ +-------------+-----------+------------------------------------------------------------------------------+
493
+ | TERMINATION | N/A | If any fatal error is detected, the hypervisor will enter TERMINATION mode. |
494
+ | mode | | In this mode, a default fatal error handler will be invoked to handle the |
495
+ | | | fatal error. |
496
+ +-------------+-----------+------------------------------------------------------------------------------+
497
+
498
+
499
+ Configurable Module Properties
500
+ ==============================
501
+
502
+ The properties of configurable modules are shown below:
503
+
504
+ - The functionality of the module depends on platform configurations;
505
+ - Corresponding platform configurations can be detected in DETECT mode;
506
+ - The module APIs shall be configured in DETECT mode;
507
+ - The module APIs shall be used in modes other than DETECT mode.
508
+
509
+ Platform configurations include:
510
+
511
+ - Features depending on hardware or firmware
512
+ - Configuration data provided by firmware
513
+ - Configuration data provided by BSP
514
+
515
+
516
+ Design Rules
517
+ ============
518
+
519
+ The module level configuration design rules are shown below:
520
+
521
+ 1. The platform configurations shall be detectable by hypervisor in DETECT mode;
522
+
523
+ 2. Configurable module APIs shall be abstracted as operations which are
524
+ implemented through a set of function pointers in the operations data
525
+ structure;
526
+
527
+ 3. Every function pointer in the operations data structure shall be instantiated
528
+ as one module API in DETECT mode and the API is allowed to be implemented as
529
+ empty function for some specific configurations;
530
+
531
+ 4. The operations data structure shall be read-only in STARTUP mode, OPERATIONAL
532
+ mode, and TERMINATION mode;
533
+
534
+ 5. The configurable module shall only be accessed via APIs in the operations
535
+ data structure in STARTUP mode or OPERATIONAL mode;
536
+
537
+ 6. In order to guarantee that the function pointer in the operations data
538
+ structure is dereferenced after it has been instantiated, the pre-condition
539
+ shall be added for the function which deferences the function pointer,
540
+ instead of checking the pointer for NULL.
541
+
542
+ .. note :: The third rule shall be double checked during code review.
543
+
544
+ Use Cases
545
+ =========
546
+
547
+ The following table shows some use cases of module level configuration design:
548
+
549
+ .. list-table :: Module Level Configuration Design Use Cases
550
+ :widths: 10 25 20
551
+ :header-rows: 1
552
+
553
+ * - **Platform Configuration **
554
+ - **Configurable Module **
555
+ - **Prerequisite **
556
+
557
+ * - Features depending on hardware or firmware
558
+ - This module is used to virtualize part of LAPIC functionalities.
559
+ It can be done via APICv or software emulation depending on CPU
560
+ capabilities.
561
+ For example, KBL NUC doesn't support virtual-interrupt delivery, while
562
+ other platforms support it.
563
+ - If a function pointer is used, the prerequisite is
564
+ "hv_operation_mode == OPERATIONAL".
565
+
566
+ * - Configuration data provided by firmware
567
+ - This module is used to interact with firmware (UEFI or SBL), and the
568
+ configuration data is provided by firmware.
569
+ For example, UP2 uses SBL and KBL NUC uses UEFI.
570
+ - If a function pointer is used, the prerequisite is
571
+ "hv_operation_mode != DETECT".
572
+
573
+ * - Configuration data provided by BSP
574
+ - This module is used to virtualize LAPIC, and the configuration data is
575
+ provided by BSP.
576
+ For example, some VMs use LAPIC pass-through and the other VMs use
577
+ vLAPIC.
578
+ - If a function pointer is used, the prerequisite is
579
+ "hv_operation_mode == OPERATIONAL".
580
+
581
+ .. note :: Prerequisite is used to guarantee that the function pointer used for
582
+ configuration is dereferenced after it has been instantiated.
583
+
584
+
585
+ Examples
586
+ ========
587
+
588
+ Take the module for parsing boot information as an example to illustrate the
589
+ idea of module level configuration design.
590
+
591
+ .. figure :: images/boot_information_parsing_module.png
592
+ :align: center
593
+ :scale: 70 %
594
+ :name: boot_information_parsing_module
595
+
596
+ Boot Information Parsing Module
597
+
598
+
599
+ As shown in the source code below, 'struct firmware_operations' is an operations
600
+ data structure that contains a set of function pointers.
601
+ Different firmware may have different implementations:
602
+
603
+ - 'firmware_uefi_ops' is for UEFI platform;
604
+ - 'firmware_sbl_ops' is for SBL platform.
605
+
606
+
607
+ .. code-block :: c
608
+
609
+ struct firmware_operations {
610
+ void (*init)(void);
611
+ uint64_t (*get_ap_trampoline)(void);
612
+ void *(*get_rsdp)(void);
613
+ void (*init_irq)(void);
614
+ int32_t (*init_vm_boot_info)(struct acrn_vm *vm);
615
+ };
616
+
617
+ static struct firmware_operations firmware_uefi_ops = {
618
+ .init = uefi_init,
619
+ .get_ap_trampoline = uefi_get_ap_trampoline,
620
+ .get_rsdp = uefi_get_rsdp,
621
+ .init_irq = uefi_init_irq,
622
+ .init_vm_boot_info = uefi_init_vm_boot_info,
623
+ };
624
+
625
+ static struct firmware_operations firmware_sbl_ops = {
626
+ .init = sbl_init,
627
+ .get_ap_trampoline = sbl_get_ap_trampoline,
628
+ .get_rsdp = sbl_get_rsdp,
629
+ .init_irq = sbl_init_irq,
630
+ .init_vm_boot_info = sbl_init_vm_boot_info,
631
+ };
632
+
633
+
634
+ 'firmware_ops' is the operations set that is dereferenced and takes effect.
635
+
636
+ 'init_firmware_operations' is called when the hypervisor is in DETECT mode and
637
+ 'firmware_ops' is instantiated here to either 'firmware_uefi_ops' or
638
+ 'firmware_sbl_ops' depending on the platform.
639
+
640
+ .. note :: All the other exported interfaces using 'firmware_ops' shall be called
641
+ after the instantiation.
642
+
643
+
644
+ .. code-block :: c
645
+
646
+ static struct firmware_operations *firmware_ops;
647
+
648
+ struct firmware_operations* uefi_get_firmware_operations(void)
649
+ {
650
+ return &firmware_uefi_ops;
651
+ }
652
+
653
+ struct firmware_operations* sbl_get_firmware_operations(void)
654
+ {
655
+ return &firmware_sbl_ops;
656
+ }
657
+
658
+ void init_firmware_operations(void)
659
+ {
660
+ if (is_firmware_sbl()) {
661
+ firmware_ops = sbl_get_firmware_operations();
662
+ } else {
663
+ firmware_ops = uefi_get_firmware_operations();
664
+ }
665
+ }
666
+
667
+
668
+ For example, when the hypervisor needs to initialize the VM boot information,
669
+ it calls 'firmware_init_vm_boot_info' and 'firmware_ops->init_vm_boot_info' is
670
+ dereferenced here with correct API being called.
671
+
672
+ .. code-block :: c
673
+
674
+ /**
675
+ * @pre firmware_ops->init_vm_boot_info != NULL
676
+ */
677
+ int32_t firmware_init_vm_boot_info(struct acrn_vm *vm)
678
+ {
679
+ return firmware_ops->init_vm_boot_info(vm);
680
+ }
681
+
682
+
453
683
References
454
684
**********
455
685
0 commit comments