diff --git a/course/02-KVM.md b/course/02-KVM.md index 0349bf906aa347d730faedccda9c27eb2b824e6b..5ab71fae88a257f1fe5251fb6e72765ca791ce7e 100644 --- a/course/02-KVM.md +++ b/course/02-KVM.md @@ -1111,9 +1111,9 @@ outb(0x3C5, 0x0F); - Each device generates a different interrupt - Whenever the CPU receives an interrupt, it must handle it, i.e. execute some particular code, e.g.: - keyboard: read the pressed key and store it in an internal buffer - - mouse: read the new position and display the cursor at the new position + - mouse: read the mouse position and display the cursor at the new position - etc. -- When the CPU finishes handling the interrupt, it must send a specific command (EOI = End Of Interrupts) to the interrupt controller to indicate it has finished handling the interrupt +- When the CPU finishes handling the interrupt, it must send a specific command (EOI = End Of Interrupts) to the Programmable Interrupt Controller (PIC) to indicate it has finished handling the interrupt - otherwise that interrupt will never be fired again! [//]: # ---------------------------------------------------------------- @@ -1148,19 +1148,18 @@ outb(0x3C5, 0x0F); - \textcolor{myred}{important: must be created \textbf{before} creating any vCPU!} 1. Configure the PIC to automatically issue End Of Interrupts (EOI) - allow to simplify the guest OS code -1. Create a file descriptor for event notification -1. Link this file descriptor to a KVM interrupt request object, specifying the hardware interrupt number -1. Then, **to trigger a hardware interrupt in the guest, simply write to this file descriptor** +1. Trigger a hardware interrupt in the guest by using the `KVM_IRQ_LINE` ioctl on the VM file descriptor + - emulate a *level-triggered* interrupt by writing to the interrupt line twice, first up, then down [//]: # ---------------------------------------------------------------- ## Injection VMM side: (1) create virtual PIC -A PIC is created using the following `ioctl` call on the VM file descriptor: +A Programmable Interrupt Controller (PIC) is created by using the following `ioctl` call on the VM file descriptor: \vspace{.3cm} ```{.verysmall .c} if (ioctl(vmfd, KVM_CREATE_IRQCHIP, 0) < 0) { - err(1, "KVM_CREATE_IRQCHIP failed"); + // Error... } ``` @@ -1176,16 +1175,52 @@ struct kvm_irqchip irqchip = { }; // Retrieve the PIC registers -ioctl(vmfd, KVM_GET_IRQCHIP, &irqchip); +if (ioctl(vmfd, KVM_GET_IRQCHIP, &irqchip) < 0) { + // Error... +} -// Configure the PIC registers so that the Guest OS doesn't need -// to acknowledge the hardware interrupt by issuing an EOI. +// Configure the PIC so that the Guest doesn't need to +// acknowledge the hardware interrupt by issuing an EOI irqchip.chip.pic.auto_eoi = 1; // Set the PIC registers -ioctl(vmfd, KVM_SET_IRQCHIP, &irqchip); +if (ioctl(vmfd, KVM_SET_IRQCHIP, &irqchip) < 0) { + // Error... +} +``` + +[//]: # ---------------------------------------------------------------- +## Injection VMM side: (3) trigger hardware interrupt + +Example that triggers a *level-triggered* interrupt, here hardware interrupt 5: + +```{.verysmall .c} +struct kvm_irq_level irq_level; +irq_level.irq = 5; + +// Drive the signal to the active level (1) +irq_level.level = 1; +if (ioctl(vmfd, KVM_IRQ_LINE, &irq_level) < 0) { + // Error... +} + +// Drive the signal down (0) +irq_level.level = 0; +if (ioctl(vmfd, KVM_IRQ_LINE, &irq_level) < 0) { + // Error... +} ``` +<!-- +[//]: # ---------------------------------------------------------------- +## Hardware interrupts injection - alternative VMM side + +Alternative way of triggering a hardware interrupt in the guest: + +1. Create a file descriptor for event notification +1. Link this file descriptor to a KVM interrupt request object, specifying the hardware interrupt number +1. Then, **to trigger a hardware interrupt in the guest, write to this file descriptor** + [//]: # ---------------------------------------------------------------- ## Injection VMM side: (3) fd for even notification @@ -1229,6 +1264,7 @@ To trigger a hardware interrupt in the guest, simply write any 64 bits value to uint64_t dummy_val = 0; write(fd, &dummy_val, sizeof(uint64_t)); ``` +--> [//]: # ---------------------------------------------------------------- ## Hardware interrupts injection - guest OS side @@ -1240,10 +1276,27 @@ Perform the same steps as on a real physical system[^4]: - implement ISRs for all potential hardware interrupts that may be triggered 1. Unmask hardware interrupts so that they will be received -\textcolor{myred}{Receiving a hardware interrupts for which there is no properly initialized IVT entry will result in a shutdown/reboot of the guest (as would on a physical machine)} +\textcolor{myred}{Receiving a hardware interrupts for which there is no properly initialized IVT entry will result in a shutdown/reboot of the guest (as it would on a physical machine)} [^4]: \scriptsize With one exception: in the ISR, no need to send an EOI command to the PIC +[//]: # ---------------------------------------------------------------- +## Guest OS, masking/unmasking hardware interrupts + +On x86, hardware interrupts are masked (disabled) using the `cli` machine instruction: +```{.verysmall .c} +static inline void cli() { + asm volatile("cli"); +} +``` + +Conversely, `sti` unmasks (enables) hardware interrupts: +```{.verysmall .c} +static inline void sti() { + asm volatile("sti"); +} +``` + [//]: # ---------------------------------------------------------------- # Miscellaneous