Skip to content
Snippets Groups Projects
Commit 1b77583a authored by Florent Gluck's avatar Florent Gluck
Browse files

udpated course/02-KVM.md and added lab-virtual_game_machine.md

parent 8a47f534
No related branches found
No related tags found
No related merge requests found
...@@ -389,16 +389,23 @@ while (!done) { ...@@ -389,16 +389,23 @@ while (!done) {
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
## Memory-Mapped I/O devices (MMIO) ## Memory-Mapped I/O devices (MMIO)
\small
- Device registers are **mapped** into the CPU **physical address space** - Device registers are **mapped** into the CPU **physical address space**
- **RAM and devices registers share the same address space!** - **RAM and devices registers share the same address space!**
- Called **MMIO**: Memory-Mapped Input/Output - Called **MMIO**: Memory-Mapped Input/Output
- Read/write from/to these devices happen exactly like memory (RAM) - Read/write from/to these devices happen exactly like memory (RAM)
- All CPU instructions dealing with memory operands can interact with these devices - All CPU instructions dealing with memory operands can interact with these devices
- e.g. `mov` instruction (x86) - \footnotesize e.g. `mov` instruction (x86)
```{.verysmall .assembler} ```{.verysmall .assembler}
mov al,[42] ; reads 8-bits at MMIO address 42 mov al,[42] ; reads 32-bits at MMIO address 42
; and stores it into al register ; and stores it into al register
``` ```
```{.verysmall .assembler}
mov ebx,42 ; store 42 into ebx register
mov byte[ebx],17 ; writes 8-bits value 17
; to MMIO address 42
```
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
## Port-Mapped I/O devices (PMIO) ## Port-Mapped I/O devices (PMIO)
...@@ -583,22 +590,24 @@ void read_sector(int n, uint8_t *data) { ...@@ -583,22 +590,24 @@ void read_sector(int n, uint8_t *data) {
\definecolor{palechestnut}{rgb}{0.87, 0.68, 0.69} \definecolor{palechestnut}{rgb}{0.87, 0.68, 0.69}
\setlength{\fboxsep}{6pt} \setlength{\fboxsep}{6pt}
\fcolorbox{black}{palechestnut!50}{\parbox{10cm}{How to use these VMexits, to either \textbf{paravirtualize} or \textbf{emulate} a device?}} \fcolorbox{black}{palechestnut!50}{\parbox{10cm}{How to use these VMexits, to either \textbf{paravirtualize} or \textbf{emulate} a device?}}
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
# Device paravirtualisation # Device paravirtualization
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
## From device emulation to device paravirtualization ## Paravirtualization
::: incremental ::: ::: incremental
- Device emulation can be complex and difficult to implement - The idea behind paravirtualization is simple: the guest OS sends a service request to the VMM
- Device drivers that trigger many `VMexits` have poor performance - Conceptually very similar to a user app requesting a service from the OS
- each `VMexit` triggers a context switch - this mechanism is called a **syscall**
- many context switches lead to poor performance - \textcolor{myblue}{In paravirtualization, guest requests a service from the VMM}
- Paravirtualized devices designed to be simple and efficient with few `VMexits` - \textcolor{myblue}{this mechanism is called an \textbf{hypercall}}
- How? - Device paravirtualization is simpler and much easier to implement than emulation, hence we describe it first
\vspace{.3cm}
- **\textcolor{mygreen}{How to implement paravirtualization?}**
::: :::
...@@ -607,11 +616,11 @@ void read_sector(int n, uint8_t *data) { ...@@ -607,11 +616,11 @@ void read_sector(int n, uint8_t *data) {
- Mechanism for the guest OS to request the help of the VMM - Mechanism for the guest OS to request the help of the VMM
- The VMM exposes an "API" of what functions are available to the guest OS - The VMM exposes an "API" of what functions are available to the guest OS
- typically used to access devices - typically used to access and control devices
- Examples of hypercalls: - Hypercall examples:
- guest OS wants to read a disk sector - guest OS requests to read a disk sector
- guest OS wants to display something - guest OS requests to display something
- guest OS wants to send a network packet - guest OS requests to send a network packet
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
## Hypercalls vs system calls ## Hypercalls vs system calls
...@@ -680,22 +689,45 @@ void read_sector(int n, uint8_t *data) { ...@@ -680,22 +689,45 @@ void read_sector(int n, uint8_t *data) {
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
## Hypercalls: parameters ## Hypercalls: parameters
```{.verysmall} \small
typedef struct {
uint8_t x; - **\textcolor{mygreen}{Idea}**: hypercall parameters are serialized/deserialized into a structure, e.g.
int32_t val; ```{.verysmall .c}
uint64_t msg; // a pointer typedef struct {
} __attribute__((packed)) params_t; uint8_t val;
``` uint64_t ptr; // a pointer
} __attribute__((packed)) params_t;
```
- Fields can be used to store both inputs and/or outputs
- Structure must be **`packed`** as it might be padded differently between VMM and guest (it depends on how code was compiled!)
- Pointers must be declared as the **largest storage size** among VMM and guest
- \footnotesize otherwise risk of overflow if architectures are of different size (e.g. 32-bits guest and 64-bits VMM)
IMPORTANT: if VMM and guest architecture are of different size, e.g. guest is 32-bits and VMM is 64, the above structure must use the **largest** data storage type! [//]: # ----------------------------------------------------------------
## Hypercalls parameters: guest
How does guest OS prepare an hypercall's parameters?
- Initialize structure with required parameters
- Copy (shallow) structure's content to shared hypercall buffer
- Issue hypercall by writing the desired number to the dedicated hypercall address
[//]: # ----------------------------------------------------------------
## Hypercalls parameters: VMM
How does VMM retrieve an hypercall's parameters?
- Detect `VMExit` dedicated to an hypercall, then retrieve its number
- Execute function corresponding to the retrieved hypercall number
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
## Writing/read to/from shared buffer ## Hypercalls: rebasing pointers
- Compiler optimization: - Remember that a pointer is just... an address
- use memcpy if VMM must write something that must be read back by the guest OS! - This address is relative to the process' address space
- otherwise, compiler won't read the value again (will use the existing value as it doesn't know the value was modified in another thread/process) - \textcolor{myred}{Guest OS address space $\neq$ VMM address space!}
- VMM must convert every guest address into the address relative to the VMM's address space
- This process is called **pointer rebasing**
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
# VMExits: retrieving values # VMExits: retrieving values
...@@ -969,6 +1001,18 @@ outb(0x3C5, 0x0F); ...@@ -969,6 +1001,18 @@ outb(0x3C5, 0x0F);
::: :::
:::::: ::::::
<!--
[//]: # ----------------------------------------------------------------
## Performance consideration: emulation vs paravirtualization
- Emulation typically triggers lots of `VMexits`
- Device drivers that trigger many `VMexits` have poor performance
- each `VMexit` triggers a context switch
- many context switches lead to poor performance
- Paravirtualized devices designed to be simple and efficient with only few `VMexits`
- leads to **much better performance** than emulation
-->
[//]: # ---------------------------------------------------------------- [//]: # ----------------------------------------------------------------
# VMM software architecture # VMM software architecture
...@@ -990,7 +1034,7 @@ outb(0x3C5, 0x0F); ...@@ -990,7 +1034,7 @@ outb(0x3C5, 0x0F);
- Dedicate a thread for every vCPU - Dedicate a thread for every vCPU
- If using the SDL library to manage the display, **make sure** that only the main thread calls SDL functions[^8] - If using the SDL library to manage the display, **make sure** that only the main thread calls SDL functions[^8]
- Dedicate a thread to interact with the user - Dedicate a thread to interact with the user
- if using SDL, handle it in the same thread (main) as the one calling SDL functions - if using SDL, handle it in the same thread (`main` function) as the one calling SDL functions
- If VMM emulates a timer, have it run in a dedicated thread - If VMM emulates a timer, have it run in a dedicated thread
[^8]: \scriptsize [\textcolor{myblue}{https://documentation.help/SDL/thread.html}](https://documentation.help/SDL/thread.html) [^8]: \scriptsize [\textcolor{myblue}{https://documentation.help/SDL/thread.html}](https://documentation.help/SDL/thread.html)
......
...@@ -336,7 +336,7 @@ Le petit programme **`bin2array`**, dont le code source [se trouve ici](https:// ...@@ -336,7 +336,7 @@ Le petit programme **`bin2array`**, dont le code source [se trouve ici](https://
## Consignes à propos de l'implémentation ## Consignes à propos de l'implémentation
- Gérez toujours les erreurs potentielles\ : testez toujours le retour de fonctions qui peuvent échouer, en particulier celles qui dépendent de l'utilisateur (ex. `open`). - Gérez toujours les erreurs potentielles\ : testez toujours le retour de fonctions qui peuvent échouer, en particulier celles qui dépendent de l'utilisateur (ex. `open`).
- Assurez-vous que votre guest n'a aucun moyen de faire crasher[^1] votre VMM\ ; si c'est le cas, c'est que votre VMM n'est pas robuste (p.ex. exécuter un guest bidon ou volontairement buggé). - Assurez-vous que votre guest n'a aucun moyen de faire crasher[^1] votre VMM\ ; si c'est le cas, c'est que votre VMM n'est pas robuste (p.ex. exécuter un guest bidon ou volontairement buggé, n° d'hypercalls invalides, etc.).
- Modularisez votre code afin de ne pas avoir tout le code dans un même fichier source. - Modularisez votre code afin de ne pas avoir tout le code dans un même fichier source.
- Utilisez des noms de variables qui ont du sens et qui aident à la compréhension du code. - Utilisez des noms de variables qui ont du sens et qui aident à la compréhension du code.
- Ecrivez des fonctions courtes, lisibles et réutilisables. - Ecrivez des fonctions courtes, lisibles et réutilisables.
......
...@@ -25,7 +25,10 @@ mov ss,ax ...@@ -25,7 +25,10 @@ mov ss,ax
mov es,ax mov es,ax
call guest_main ; call guest C code entrypoint call guest_main ; call guest C code entrypoint
@forever:
hlt ; halt the CPU hlt ; halt the CPU
jmp @forever
; Setup a GDT table to identity map the full 4GB address space (32 bits) ; Setup a GDT table to identity map the full 4GB address space (32 bits)
gdt_start: ; the GDT table starts here gdt_start: ; the GDT table starts here
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment