Skip to content

Commit c5b8b34

Browse files
mdouchametan-ucw
authored andcommitted
Add documentation for KVM testing
Reviewed-by: Martin Doucha <[email protected]> Signed-off-by: Cyril Hrubis <[email protected]>
1 parent bf96787 commit c5b8b34

File tree

1 file changed

+384
-0
lines changed

1 file changed

+384
-0
lines changed

doc/kvm-test-api.txt

Lines changed: 384 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,384 @@
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

Comments
 (0)