diff --git a/course/02-KVM.md b/course/02-KVM.md index 5ab71fae88a257f1fe5251fb6e72765ca791ce7e..9e1a659aced0fed977bcc7ee775eb143631f8adf 100644 --- a/course/02-KVM.md +++ b/course/02-KVM.md @@ -168,20 +168,30 @@ pandoc-latex-fontsize: \vspace{.3cm} ```{.c .verysmall} int kvmfd = open("/dev/kvm", O_RDWR | O_CLOEXEC); - if (kvmfd < 0) err(1, "%s", "/dev/kvm"); + if (kvmfd < 0) err(1, "Failed opening kvm device: "); int version = ioctl(kvmfd, KVM_GET_API_VERSION, NULL); - if (version < 0) err(1, "KVM_GET_API_VERSION"); - if (version != KVM_API_VERSION) err(1, "Unsupported version of the KVM API"); + if (version < 0) err(1, "KVM_GET_API_VERSION failed: "); + if (version != KVM_API_VERSION) errx(1, "Error: KVM_GET_API_VERSION %d, expected %d!", version, KVM_API_VERSION); ``` +[//]: # ---------------------------------------------------------------- +## Abort handling: err vs errx + +- Both `err` and `errx` functions print an error message to `stderr` and abort the current process +- `err` must be used when the function that fails sets `errno` (typically a system call) +- `err` appends an error message (based on `errno`) to the text passed in argument +- `errx` must be used when the function that fails does not set `errno` +- Both functions accept variable parameters (like `printf`) + [//]: # ---------------------------------------------------------------- ## (2) Create a VM - To obtain a file descriptor (here, `vmfd`) on a newly created VM: ```{.c .verysmall} - int vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0); - if (vmfd < 0) err(1, "KVM_CREATE_VM"); + #define MACHINE_TYPE 0 + int vmfd = ioctl(kvmfd, KVM_CREATE_VM, MACHINE_TYPE); + if (vmfd < 0) err(1, "Error: KVM_CREATE_VM failed: "); ``` - This file descriptor allows us to: - defines the VM's memory address space @@ -206,7 +216,7 @@ pandoc-latex-fontsize: // Alloc 4KB for the guest u_int ram_size = 4096; uint8_t *mem = mmap(NULL, ram_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); - if (!mem) err(1, "Allocating guest memory"); + if (!mem) err(1, "Error: failed allocating guest memory: "); ``` [//]: # ---------------------------------------------------------------- @@ -224,7 +234,7 @@ pandoc-latex-fontsize: .userspace_addr = (uint64_t)mem, .flags = 0 }; - if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg) < 0) err(1, "KVM_SET_USER_MEMORY_REGION"); + if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg) < 0) err(1, "Error: KVM_SET_USER_MEMORY_REGION: "); ``` <!-- @@ -245,7 +255,7 @@ pandoc-latex-fontsize: .userspace_addr = (uint64_t)mmio, .flags = KVM_MEM_READONLY // Mandatory for MMIO! }; - if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg) < 0) err(1, "KVM_SET_USER_MEMORY_REGION"); + if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg) < 0) err(1, "Error: KVM_SET_USER_MEMORY_REGION: "); ``` --> @@ -269,14 +279,14 @@ pandoc-latex-fontsize: \vspace{.3cm} ```{.c .tiny} int vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); - if (vcpufd < 0) err(1, "KVM_CREATE_VCPU"); + if (vcpufd < 0) err(1, "Error: KVM_CREATE_VCPU: "); int vcpu_mmap_sz = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, NULL); - if (vcpu_mmap_sz < 0) err(1, "KVM_GET_VCPU_MMAP_SIZE"); - if (vcpu_mmap_sz < sizeof(struct kvm_run)) err(1, "KVM_GET_VCPU_MMAP_SIZE unexpectedly small"); + if (vcpu_mmap_sz < 0) err(1, "Error: KVM_GET_VCPU_MMAP_SIZE: "); + if (vcpu_mmap_sz < sizeof(struct kvm_run)) err(1, "Error: KVM_GET_VCPU_MMAP_SIZE unexpectedly small: "); struct kvm_run *run = mmap(NULL, vcpu_mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); - if (!run) err(1, "mmap vcpu"); + if (!run) err(1, "Error: Failed to mmap vCPU: "); ``` [//]: # ---------------------------------------------------------------- @@ -286,12 +296,12 @@ pandoc-latex-fontsize: \vspace{.3cm} ```{.c .verysmall} struct kvm_sregs sregs; - if (ioctl(vcpufd, KVM_GET_SREGS, &sregs) < 0) err(1, "KVM_GET_SREGS"); + if (ioctl(vcpufd, KVM_GET_SREGS, &sregs) < 0) err(1, "Error: KVM_GET_SREGS: "); sregs.cs.base = 0; sregs.cs.selector = 0; sregs.ds.base = 0; sregs.ds.selector = 0; sregs.es.base = 0; sregs.es.selector = 0; sregs.ss.base = 0; sregs.ss.selector = 0; - if (ioctl(vcpufd, KVM_SET_SREGS, &sregs) < 0) err(1, "KVM_SET_SREGS"); + if (ioctl(vcpufd, KVM_SET_SREGS, &sregs) < 0) err(1, "Error: KVM_SET_SREGS: "); ``` [//]: # ---------------------------------------------------------------- @@ -307,7 +317,7 @@ pandoc-latex-fontsize: regs.rip = 0; regs.rsp = ram_size; regs.rflags = 0x2; - if (ioctl(vcpufd, KVM_SET_REGS, ®s) < 0) err(1, "KVM_SET_REGS"); + if (ioctl(vcpufd, KVM_SET_REGS, ®s) < 0) err(1, "Error: KVM_SET_REGS: "); ``` [//]: # ---------------------------------------------------------------- @@ -322,7 +332,7 @@ pandoc-latex-fontsize: bool done = false; while (!done) { // Runs the vCPU until encoutering a VM_EXIT (blocking call) - if (ioctl(vcpufd, KVM_RUN, NULL) < 0) err(1, "KVM_RUN"); + if (ioctl(vcpufd, KVM_RUN, NULL) < 0) err(1, "Error: KVM_RUN: "); switch (run->exit_reason) { // See struct kvm_run in "(6) Create a vCPU" case KVM_EXIT_IO: // Encountered a PMIO VMexit break; // This VMexit should be handled here... @@ -1097,11 +1107,12 @@ outb(0x3C5, 0x0F); ## Why hardware interrupts? - To avoid constantly polling devices -- Polling must be avoided: +- Polling should be avoided: - polling frequently: low latency, but high CPU usage - polling infrequently: low CPU usage, but high latency - To handle timers - A timer offers a time base independent of the CPU frequency + - necessary to mesure time accurately - necessary to implement `sleep` or similar functionalities [//]: # ---------------------------------------------------------------- @@ -1158,11 +1169,13 @@ A Programmable Interrupt Controller (PIC) is created by using the following `ioc \vspace{.3cm} ```{.verysmall .c} -if (ioctl(vmfd, KVM_CREATE_IRQCHIP, 0) < 0) { +if (ioctl(vmfd, KVM_CREATE_IRQCHIP, NULL) < 0) { // Error... } ``` +Make sure to create it **\textcolor{myred}{before}** creating any vCPU! + [//]: # ---------------------------------------------------------------- ## Injection VMM side: (2) configure virtual PIC @@ -1231,7 +1244,7 @@ Create a file descriptor for event notification by using the `eventfd` function: // Create a file descriptor for event (hardware interrupts) notification int fd = eventfd(0, 0); if (fd == -1) { - err(1, "eventfd failed"); + err(1, "eventfd failed: "); } ``` @@ -1250,7 +1263,7 @@ if (fd == -1) { }; if (ioctl(vm->vmfd, KVM_IRQFD, &irqfd) < 0) { - err(1, "KVM_IRQFD error"); + err(1, "KVM_IRQFD error: "); } ``` @@ -1276,7 +1289,7 @@ 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 it would on a physical machine)} +\textcolor{myred}{Receiving a hardware interrupts for which there is no properly initialized IVT entry will result in a 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