|
| 1 | +LTP KVM Test API |
| 2 | +================ |
| 3 | + |
| 4 | +Testing KVM is more complex than other Linux features. Some KVM bugs allow |
| 5 | +userspace code running inside the virtual machine to bypass (emulated) hardware |
| 6 | +access restrictions and elevate its privileges inside the guest operating |
| 7 | +system. The worst types of KVM bugs may even allow the guest code to crash or |
| 8 | +compromise the physical host. KVM tests therefore need to be split into two |
| 9 | +components – a KVM controller program running on the physical host and a guest |
| 10 | +payload program running inside the VM. The cooperation of these two components |
| 11 | +allows testing even of bugs that somehow cross the virtualization boundary. |
| 12 | + |
| 13 | +NOTE: See also |
| 14 | + https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines[Test Writing Guidelines], |
| 15 | + https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial[C Test Case Tutorial], |
| 16 | + https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API]. |
| 17 | + |
| 18 | +1. Basic KVM test structure |
| 19 | +--------------------------- |
| 20 | + |
| 21 | +KVM tests are simple C source files containing both the KVM controller code |
| 22 | +and the guest payload code separated by `#ifdef COMPILE_PAYLOAD` preprocessor |
| 23 | +condition. The file will be compiled twice. Once to compile the payload part, |
| 24 | +once to compile the KVM controller part and embed the payload binary inside. |
| 25 | +The result is a single self-contained binary that'll execute the embedded |
| 26 | +payload inside a KVM virtual machine and print results in the same format as |
| 27 | +a normal LTP test. |
| 28 | + |
| 29 | +A KVM test source should start with `#include "kvm_test.h"` instead of the |
| 30 | +usual `tst_test.h`. The `kvm_test.h` header file will include the other basic |
| 31 | +headers appropriate for the current compilation pass. Everything else in the |
| 32 | +source file should be enclosed in `#ifdef COMPILE_PAYLOAD ... #else ... #endif` |
| 33 | +condition, including any other header file includes. Note that the standard |
| 34 | +LTP headers are not available in the payload compilation pass, only the KVM |
| 35 | +guest library headers can be included. |
| 36 | + |
| 37 | +.Example KVM test |
| 38 | +[source,c] |
| 39 | +------------------------------------------------------------------------------- |
| 40 | +#include "kvm_test.h" |
| 41 | + |
| 42 | +#ifdef COMPILE_PAYLOAD |
| 43 | + |
| 44 | +/* Guest payload code */ |
| 45 | + |
| 46 | +void main(void) |
| 47 | +{ |
| 48 | + tst_res(TPASS, "Hello, world!"); |
| 49 | +} |
| 50 | + |
| 51 | +#else /* COMPILE_PAYLOAD */ |
| 52 | + |
| 53 | +/* KVM controller code */ |
| 54 | + |
| 55 | +static struct tst_test test = { |
| 56 | + .test_all = tst_kvm_run, |
| 57 | + .setup = tst_kvm_setup, |
| 58 | + .cleanup = tst_kvm_cleanup, |
| 59 | +}; |
| 60 | + |
| 61 | +#endif /* COMPILE_PAYLOAD */ |
| 62 | +------------------------------------------------------------------------------- |
| 63 | + |
| 64 | +The KVM controller code is a normal LTP test and needs to define an instance |
| 65 | +of `struct tst_test` with metadata and the usual setup, cleanup, and test |
| 66 | +functions. Basic implementation of all three functions is provided by the KVM |
| 67 | +host library. |
| 68 | + |
| 69 | +On the other hand, the payload is essentially a tiny kernel that'll run |
| 70 | +on bare virtual hardware. It cannot access any files, Linux syscalls, standard |
| 71 | +library functions, etc. except for the small subset provided by the KVM guest |
| 72 | +library. The payload code must define a `void main(void)` function which will |
| 73 | +be the VM entry point of the test. |
| 74 | + |
| 75 | +2. KVM host library |
| 76 | +------------------- |
| 77 | + |
| 78 | +The KVM host library provides helper functions for creating and running |
| 79 | +a minimal KVM virtual machine. |
| 80 | + |
| 81 | +2.1 Data structures |
| 82 | +~~~~~~~~~~~~~~~~~~~ |
| 83 | + |
| 84 | +[source,c] |
| 85 | +------------------------------------------------------------------------------- |
| 86 | +struct tst_kvm_instance { |
| 87 | + int vm_fd, vcpu_fd; |
| 88 | + struct kvm_run *vcpu_info; |
| 89 | + size_t vcpu_info_size; |
| 90 | + struct kvm_userspace_memory_region ram[MAX_KVM_MEMSLOTS]; |
| 91 | + struct tst_kvm_result *result; |
| 92 | +}; |
| 93 | +------------------------------------------------------------------------------- |
| 94 | + |
| 95 | +`struct tst_kvm_instance` holds the file descriptors and memory buffers |
| 96 | +of a single KVM virtual machine: |
| 97 | + |
| 98 | +- `int vm_fd` is the main VM file descriptor created by `ioctl(KVM_CREATE_VM)` |
| 99 | +- `int vcpu_fd` is the virtual CPU filedescriptor created by |
| 100 | + `ioctl(KVM_CREATE_VCPU)` |
| 101 | +- `struct kvm_run *vcpu_info` is the VCPU state structure created by |
| 102 | + `mmap(vcpu_fd)` |
| 103 | +- `size_t vcpu_info_size` is the size of `vcpu_info` buffer |
| 104 | +- `struct kvm_userspace_memory_region ram[MAX_KVM_MEMSLOTS]` is the list |
| 105 | + of memory slots defined in this VM. Unused memory slots have zero |
| 106 | + in the `userspace_addr` field. |
| 107 | +- `struct tst_kvm_result *result` is a buffer for passing test result data |
| 108 | + from the VM to the controller program, mainly `tst_res()`/`tst_brk()` flags |
| 109 | + and messages. |
| 110 | + |
| 111 | +[source,c] |
| 112 | +------------------------------------------------------------------------------- |
| 113 | +struct tst_kvm_result { |
| 114 | + int32_t result; |
| 115 | + int32_t lineno; |
| 116 | + uint64_t file_addr; |
| 117 | + char message[0]; |
| 118 | +}; |
| 119 | +------------------------------------------------------------------------------- |
| 120 | + |
| 121 | +`struct tst_kvm_result` is used to pass test results and synchronization data |
| 122 | +between the KVM guest and the controller program. Most often, it is used |
| 123 | +to pass `tst_res()` and `tst_brk()` messages from the VM, but special values |
| 124 | +can also be used to send control flow requests both ways. |
| 125 | + |
| 126 | +- `int32_t result` is the message type, either one of the `TPASS`, `TFAIL`, |
| 127 | + `TWARN`, `TBROK`, `TINFO` flags or a special control flow value. Errno flags |
| 128 | + are not supported. |
| 129 | +- `int32_t lineno` is the line number for `tst_res()`/`tst_brk()` messages. |
| 130 | +- `uint64_t file_addr` is the VM address of the filename string for |
| 131 | + `tst_res()`/`tst_brk()` messages. |
| 132 | +- `char message[0]` is the buffer for arbitrary message data, most often used |
| 133 | + to pass `tst_res()`/`tst_brk()` message strings. |
| 134 | + |
| 135 | +2.2 Working with virtual machines |
| 136 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 137 | + |
| 138 | +The KVM host library provides default implementation of the setup, cleanup |
| 139 | +and test functions for `struct tst_test` in cases where you do not need |
| 140 | +to customize the VM configuration. You can either assign these functions |
| 141 | +to the `struct tst_test` instance directly or call them from your own function |
| 142 | +that does some additional steps. All three function must be used together. |
| 143 | + |
| 144 | +- `void tst_kvm_setup(void)` |
| 145 | +- `void tst_kvm_run(void)` |
| 146 | +- `void tst_kvm_cleanup(void)` |
| 147 | + |
| 148 | +Note: `tst_kvm_run()` calls `tst_free_all()`. Calling it will free all |
| 149 | +previously allocated guarded buffers. |
| 150 | + |
| 151 | +- `void tst_kvm_validate_result(int value)` – Validate whether the value |
| 152 | + returned in `struct tst_kvm_result.result` can be safely passed |
| 153 | + to `tst_res()` or `tst_brk()`. If the value is not valid, the controller |
| 154 | + program will be terminated with error. |
| 155 | + |
| 156 | +- `uint64_t tst_kvm_get_phys_address(const struct tst_kvm_instance *inst, |
| 157 | + uint64_t addr)` – Converts pointer value (virtual address) from KVM virtual |
| 158 | + machine `inst` to the corresponding physical address. Returns 0 if |
| 159 | + the virtual address is not mapped to any physical address. If virtual memory |
| 160 | + mapping is not enabled in the VM or not available on the arch at all, this |
| 161 | + function simply returns `addr` as is. |
| 162 | + |
| 163 | +- `int tst_kvm_find_phys_memslot(const struct tst_kvm_instance *inst, |
| 164 | + uint64_t paddr)` – Returns index of the memory slot in KVM virtual machine |
| 165 | + `inst` which contains the physical address `paddr`. If the address is not |
| 166 | + backed by a memory buffer, returns -1. |
| 167 | + |
| 168 | +- `int tst_kvm_find_memslot(const struct tst_kvm_instance *inst, |
| 169 | + uint64_t addr)` – Returns index of the memory slot in KVM virtual machine |
| 170 | + `inst` which contains the virtual address `addr`. If the virtual address |
| 171 | + is not mapped to a valid physical address backed by a memory buffer, |
| 172 | + returns -1. |
| 173 | + |
| 174 | +- `void *tst_kvm_get_memptr(const struct tst_kvm_instance *inst, |
| 175 | + uint64_t addr)` – Converts pointer value (virtual address) from KVM virtual |
| 176 | + machine `inst` to host-side pointer. |
| 177 | + |
| 178 | +- `void *tst_kvm_alloc_memory(struct tst_kvm_instance *inst, unsigned int slot, |
| 179 | + uint64_t baseaddr, size_t size, unsigned int flags)` – Allocates a guarded |
| 180 | + buffer of given `size` in bytes and installs it into specified memory `slot` |
| 181 | + of the KVM virtual machine `inst` at base address `baseaddr`. The buffer |
| 182 | + will be automatically page aligned at both ends. See the kernel |
| 183 | + documentation of `KVM_SET_USER_MEMORY_REGION` ioctl for list of valid |
| 184 | + `flags`. Returns pointer to page-aligned beginning of the allocated buffer. |
| 185 | + The actual requested `baseaddr` will be located at |
| 186 | + `ret + baseaddr % pagesize`. |
| 187 | + |
| 188 | +- `struct kvm_cpuid2 *tst_kvm_get_cpuid(int sysfd)` – Get a list of supported |
| 189 | + virtual CPU features returned by `ioctl(KVM_GET_SUPPORTED_CPUID)`. |
| 190 | + The argument must be an open file descriptor returned by `open("/dev/kvm")`. |
| 191 | + |
| 192 | +- `void tst_kvm_create_instance(struct tst_kvm_instance *inst, |
| 193 | + size_t ram_size)` – Creates and fully initializes a new KVM virtual machine |
| 194 | + with at least `ram_size` bytes of memory. The VM instance info will be |
| 195 | + stored in `inst`. |
| 196 | + |
| 197 | +- `void tst_kvm_run_instance(struct tst_kvm_instance *inst)` – Executes |
| 198 | + the program installed in KVM virtual machine `inst`. Any result messages |
| 199 | + returned by the VM will be automatically printed to controller program output. |
| 200 | + |
| 201 | +- `void tst_kvm_destroy_instance(struct tst_kvm_instance *inst)` – Deletes |
| 202 | + the KVM virtual machine `inst`. Note that the guarded buffers assigned |
| 203 | + to the VM by `tst_kvm_create_instance()` or `tst_kvm_alloc_memory()` will |
| 204 | + not be freed. |
| 205 | + |
| 206 | +The KVM host library does not provide any way to reset a VM instance back |
| 207 | +to initial state. Running multiple iterations of the test requires destroying |
| 208 | +the old VM instance and creating a new one. Otherwise the VM will exit |
| 209 | +without reporting any results on the second iteration and the test will fail. |
| 210 | +The `tst_kvm_run()` function handles this issue correctly. |
| 211 | + |
| 212 | +3. KVM guest library |
| 213 | +-------------------- |
| 214 | + |
| 215 | +The KVM guest library provides a minimal implementation of both the LTP |
| 216 | +test library and the standard C library functions. Do not try to include |
| 217 | +the usual LTP or C headers in guest payload code, it will not work. |
| 218 | + |
| 219 | +3.1 Standard C functions |
| 220 | +~~~~~~~~~~~~~~~~~~~~~~~~ |
| 221 | + |
| 222 | +`#include "kvm_test.h"` |
| 223 | + |
| 224 | +The functions listed below are implemented according to the C standard: |
| 225 | + |
| 226 | +- `void *memset(void *dest, int val, size_t size)` |
| 227 | +- `void *memzero(void *dest, size_t size)` |
| 228 | +- `void *memcpy(void *dest, const void *src, size_t size)` |
| 229 | +- `char *strcpy(char *dest, const char *src)` |
| 230 | +- `char *strcat(char *dest, const char *src)` |
| 231 | +- `size_t strlen(const char *str)` |
| 232 | + |
| 233 | +3.2 LTP library functions |
| 234 | +~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 235 | + |
| 236 | +`#include "kvm_test.h"` |
| 237 | + |
| 238 | +The KVM guest library currently provides the LTP functions for reporting test |
| 239 | +results. All standard result flags except for `T*ERRNO` are supported |
| 240 | +with the same rules as usual. However, the printf-like formatting is not |
| 241 | +implemented yet. |
| 242 | + |
| 243 | +- `void tst_res(int result, const char *message)` |
| 244 | +- `void tst_brk(int result, const char *message) __attribute__((noreturn))` |
| 245 | + |
| 246 | +A handful of useful macros is also available: |
| 247 | + |
| 248 | +- `TST_TEST_TCONF(message)` – Generates a test program that will simply print |
| 249 | + a `TCONF` message and exit. This is useful when the real test cannot be |
| 250 | + built due to missing dependencies or arch limitations. |
| 251 | + |
| 252 | +- `ARRAY_SIZE(arr)` – Returns the number of items in statically allocated |
| 253 | + array `arr`. |
| 254 | + |
| 255 | +- `LTP_ALIGN(x, a)` – Aligns integer `x` to be a multiple of `a`, which |
| 256 | + must be a power of 2. |
| 257 | + |
| 258 | +3.3 Arch independent functions |
| 259 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 260 | + |
| 261 | +`#include "kvm_test.h"` |
| 262 | + |
| 263 | +Memory management in KVM guest library currently uses only primitive linear |
| 264 | +buffer for memory allocation. There are no checks whether the VM can allocate |
| 265 | +more memory and the already allocated memory cannot be freed. |
| 266 | + |
| 267 | +- `void *tst_heap_alloc(size_t size)` – Allocates a block of memory on the heap. |
| 268 | + |
| 269 | +- `void *tst_heap_alloc_aligned(size_t size, size_t align)` – Allocates |
| 270 | + a block of memory on the heap with the starting address aligned to given |
| 271 | + value. |
| 272 | + |
| 273 | +3.4 x86 specific functions |
| 274 | +~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 275 | + |
| 276 | +`#include "kvm_test.h"` + |
| 277 | +`#include "kvm_x86.h"` |
| 278 | + |
| 279 | +- `struct kvm_interrupt_frame` – Opaque arch-dependent structure which holds |
| 280 | + interrupt frame information. Use the functions below to get individual values: |
| 281 | + |
| 282 | +- `uintptr_t kvm_get_interrupt_ip(const struct kvm_interrupt_frame *ifrm)` – |
| 283 | + Get instruction pointer value from interrupt frame structure. This may be |
| 284 | + the instruction which caused an interrupt or the one immediately after, |
| 285 | + depending on the interrupt vector semantics. |
| 286 | + |
| 287 | +- `int (*tst_interrupt_callback)(void *userdata, |
| 288 | + struct kvm_interrupt_frame *ifrm, unsigned long errcode)` – Interrupt handler |
| 289 | + callback prototype. When an interrupt occurs, the assigned callback function |
| 290 | + will be passed the `userdata` pointer that was given |
| 291 | + to `tst_set_interrupt_callback()`, interrupt frame `ifrm` and the error |
| 292 | + code `errcode` defined by the interrupt vector semantics. If the interrupt |
| 293 | + vector does not generate an error code, `errcode` will be set to zero. |
| 294 | + The callback function must return 0 if the interrupt was successfully |
| 295 | + handled and test execution should resume. Non-zero return value means that |
| 296 | + the interrupt could not be handled and the test will terminate with error. |
| 297 | + |
| 298 | +- `void tst_set_interrupt_callback(unsigned int vector, |
| 299 | + tst_interrupt_callback func, void *userdata)` – Register new interrupt |
| 300 | + handler callback function `func` for interrupt `vector`. The `userdata` |
| 301 | + argument is an arbitrary pointer that will be passed to `func()` every time |
| 302 | + it gets called. The previous interrupt handler callback will be removed. |
| 303 | + Setting `func` to `NULL` will remove any existing interrupt handler |
| 304 | + from `vector` and the interrupt will become fatal error. |
| 305 | + |
| 306 | +[source,c] |
| 307 | +------------------------------------------------------------------------------- |
| 308 | +struct page_table_entry_pae { |
| 309 | + unsigned int present: 1; |
| 310 | + unsigned int writable: 1; |
| 311 | + unsigned int user_access: 1; |
| 312 | + unsigned int write_through: 1; |
| 313 | + unsigned int disable_cache: 1; |
| 314 | + unsigned int accessed: 1; |
| 315 | + unsigned int dirty: 1; |
| 316 | + unsigned int page_type: 1; |
| 317 | + unsigned int global: 1; |
| 318 | + unsigned int padding: 3; |
| 319 | + uint64_t address: 40; |
| 320 | + unsigned int padding2: 7; |
| 321 | + unsigned int prot_key: 4; |
| 322 | + unsigned int noexec: 1; |
| 323 | +} __attribute__((__packed__)); |
| 324 | + |
| 325 | +struct kvm_cpuid { |
| 326 | + unsigned int eax, ebx, ecx, edx; |
| 327 | +}; |
| 328 | + |
| 329 | +struct kvm_cregs { |
| 330 | + unsigned long cr0, cr2, cr3, cr4; |
| 331 | +}; |
| 332 | +------------------------------------------------------------------------------- |
| 333 | + |
| 334 | +`struct page_table_entry_pae` is the page table entry structure for PAE and |
| 335 | +64bit paging modes. See Intel(R) 64 and IA-32 Architectures Software |
| 336 | +Developer's Manual, Volume 3, Chapter 4 for explanation of the fields. |
| 337 | + |
| 338 | +- `uintptr_t kvm_get_page_address_pae(const struct page_table_entry_pae *entry)` |
| 339 | + – Returns the physical address of the memory page referenced by the given |
| 340 | + page table `entry`. Depending on memory mapping changes done by the test, |
| 341 | + the physical address may not be a valid pointer. The caller must determine |
| 342 | + whether the address points to another page table entry or a data page, using |
| 343 | + the known position in page table hierarchy and `entry->page_type`. Returns |
| 344 | + zero if the `entry` does not reference any memory page. |
| 345 | + |
| 346 | +- `void kvm_get_cpuid(unsigned int eax, unsigned int ecx, |
| 347 | + struct kvm_cpuid *buf)` – Executes the CPUID instruction with the given |
| 348 | + `eax` and `ecx` arguments and stores the results in `buf`. |
| 349 | + |
| 350 | +- `void kvm_read_cregs(struct kvm_cregs *buf)` – Copies the current values |
| 351 | + of control registers to `buf`. |
| 352 | + |
| 353 | +- `uint64_t kvm_rdmsr(unsigned int msr)` – Returns the current value |
| 354 | + of model-specific register `msr`. |
| 355 | + |
| 356 | +- `void kvm_wrmsr(unsigned int msr, uint64_t value)` – Stores `value` |
| 357 | + into model-specific register `msr`. |
| 358 | + |
| 359 | +- `void kvm_exit(void) __attribute__((noreturn))` – Terminate the test. |
| 360 | + Similar to calling `exit(0)` in a regular LTP test, although `kvm_exit()` |
| 361 | + will terminate only one iteration of the test, not the whole host process. |
| 362 | + |
| 363 | +See Intel(R) 64 and IA-32 Architectures Software Developer's Manual |
| 364 | +for documentation of standard and model-specific x86 registers. |
| 365 | + |
| 366 | +4. KVM guest environment |
| 367 | +------------------------ |
| 368 | + |
| 369 | +KVM guest payload execution begins with bootstrap code which will perform |
| 370 | +the minimal guest environment setup required for running C code: |
| 371 | + |
| 372 | +- Activate the appropriate CPU execution mode (IA-32 protected mode |
| 373 | + on 32-bit x86 or the 64-bit mode on x86_64). |
| 374 | +- Create indentity mapping (virtual address = physical address) of the lower |
| 375 | + 2GB memory region, even if parts of the region are not backed by any host |
| 376 | + memory buffers. The memory region above 2GB threshold is left unmapped |
| 377 | + except for one memory page reserved for the `struct tst_kvm_result` buffer. |
| 378 | +- Initialize 8KB stack. |
| 379 | +- Install default interrupt handlers for standard CPU exception vectors. |
| 380 | + |
| 381 | +When the environment setup is complete, bootstrap will call `void main(void)` |
| 382 | +function implemented by the test program. To finish execution of guest payload, |
| 383 | +the test can either return from the `main()` function or call `kvm_exit()` |
| 384 | +at any point. |
0 commit comments