Skip to content

Commit 4fd496f

Browse files
mdouchapevik
authored andcommitted
KVM: Add helper functions for accessing GDT/LDT
The fragmented structure of GDT and LDT requires helper functions to work with descriptors. Add the necessary structure definitions, constants and functions. Also increase GDT size to 32 descriptors for later use. Acked-by: Petr Vorel <[email protected]> Acked-by: Cyril Hrubis <[email protected]> Signed-off-by: Martin Doucha <[email protected]>
1 parent 36d5c3f commit 4fd496f

File tree

5 files changed

+169
-2
lines changed

5 files changed

+169
-2
lines changed

doc/kvm-test-api.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,26 @@ Developer's Manual, Volume 3, Chapter 4 for explanation of the fields.
343343
the known position in page table hierarchy and `entry->page_type`. Returns
344344
zero if the `entry` does not reference any memory page.
345345

346+
- `void kvm_set_segment_descriptor(struct segment_descriptor *dst, uint64_t baseaddr, uint32_t limit, unsigned int flags)` -
347+
Fill the `dst` segment descriptor with given values. The maximum value
348+
of `limit` is `0xfffff` (inclusive) regardless of `flags`.
349+
350+
- `void kvm_parse_segment_descriptor(struct segment_descriptor *src, uint64_t *baseaddr, uint32_t *limit, unsigned int *flags)` -
351+
Parse data in the `src` segment descriptor and copy them to variables
352+
pointed to by the other arguments. Any parameter except the first one can
353+
be `NULL`.
354+
355+
- `int kvm_find_free_descriptor(const struct segment_descriptor *table, size_t size)` -
356+
Find the first segment descriptor in `table` which does not have
357+
the `SEGFLAG_PRESENT` bit set. The function handles double-size descriptors
358+
correctly. Returns index of the first available descriptor or -1 if all
359+
`size` descriptors are taken.
360+
361+
- `unsigned int kvm_create_stack_descriptor(struct segment_descriptor *table, size_t tabsize, void *stack_base)` -
362+
Convenience function for registering a stack segment descriptor. It'll
363+
automatically find a free slot in `table` and fill the necessary flags.
364+
The `stack_base` pointer must point to the bottom of the stack.
365+
346366
- `void kvm_get_cpuid(unsigned int eax, unsigned int ecx,
347367
struct kvm_cpuid *buf)` – Executes the CPUID instruction with the given
348368
`eax` and `ecx` arguments and stores the results in `buf`.

testcases/kernel/kvm/bootstrap_x86.S

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
.set KVM_TEXIT, 0xff
99
.set RESULT_ADDRESS, 0xfffff000
10+
.set KVM_GDT_SIZE, 32
1011

1112
/*
1213
* This section will be allocated at address 0x1000 and
@@ -44,7 +45,7 @@ kvm_gdt:
4445
.8byte 0
4546
gdt32_entry type=0x1a l=0 d=1 /* Code segment protected_mode, 32bits */
4647
gdt32_entry type=0x12 /* Data segment, writable */
47-
.skip 16 /* Stack and TSS segment descriptors */
48+
.skip (KVM_GDT_SIZE-3)*8 /* Stack, TSS and other segment descriptors */
4849

4950
.Lgdt_end:
5051
.global kvm_gdt_desc

testcases/kernel/kvm/bootstrap_x86_64.S

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
.set KVM_TCONF, 32
99
.set KVM_TEXIT, 0xff
1010
.set RESULT_ADDRESS, 0xfffff000
11+
.set KVM_GDT_SIZE, 32
1112

1213
/*
1314
* This section will be allocated at address 0x1000 and
@@ -478,7 +479,7 @@ kvm_pgtable_l4:
478479
kvm_gdt:
479480
.8byte 0
480481
gdt32_entry type=0x1a l=1 limit=0 g=0 /* Code segment long mode */
481-
.skip 16 /* TSS segment descriptor */
482+
.skip (KVM_GDT_SIZE-2)*8 /* TSS and other segment descriptors */
482483

483484
.Lgdt_end:
484485
.global kvm_gdt_desc

testcases/kernel/kvm/include/kvm_x86.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
#include "kvm_test.h"
1212

13+
#define PAGESIZE 0x1000
14+
#define KVM_GDT_SIZE 32
15+
1316
/* Interrupts */
1417
#define X86_INTR_COUNT 256
1518

@@ -38,6 +41,26 @@
3841
#define INTR_SECURITY_ERROR 30
3942

4043

44+
/* Segment descriptor flags */
45+
#define SEGTYPE_LDT 0x02
46+
#define SEGTYPE_TSS 0x09
47+
#define SEGTYPE_TSS_BUSY 0x0b
48+
#define SEGTYPE_CALL_GATE 0x0c
49+
#define SEGTYPE_INTR_GATE 0x0e
50+
#define SEGTYPE_TRAP_GATE 0x0f
51+
#define SEGTYPE_RODATA 0x10
52+
#define SEGTYPE_RWDATA 0x12
53+
#define SEGTYPE_STACK 0x16
54+
#define SEGTYPE_CODE 0x1a
55+
#define SEGTYPE_MASK 0x1f
56+
57+
#define SEGFLAG_NSYSTEM 0x10
58+
#define SEGFLAG_PRESENT 0x80
59+
#define SEGFLAG_CODE64 0x200
60+
#define SEGFLAG_32BIT 0x400
61+
#define SEGFLAG_PAGE_LIMIT 0x800
62+
63+
4164
/* CPUID constants */
4265
#define CPUID_GET_INPUT_RANGE 0x80000000
4366
#define CPUID_GET_EXT_FEATURES 0x80000001
@@ -91,6 +114,25 @@ struct intr_descriptor {
91114
#endif /* defined(__x86_64__) */
92115
} __attribute__((__packed__));
93116

117+
struct segment_descriptor {
118+
unsigned int limit_lo : 16;
119+
unsigned int baseaddr_lo : 24;
120+
unsigned int flags_lo : 8;
121+
unsigned int limit_hi : 4;
122+
unsigned int flags_hi : 4;
123+
unsigned int baseaddr_hi : 8;
124+
} __attribute__((__packed__));
125+
126+
struct segment_descriptor64 {
127+
unsigned int limit_lo : 16;
128+
unsigned int baseaddr_lo : 24;
129+
unsigned int flags_lo : 8;
130+
unsigned int limit_hi : 4;
131+
unsigned int flags_hi : 4;
132+
uint64_t baseaddr_hi : 40;
133+
uint32_t reserved;
134+
} __attribute__((__packed__));
135+
94136
struct page_table_entry_pae {
95137
unsigned int present: 1;
96138
unsigned int writable: 1;
@@ -118,10 +160,21 @@ struct kvm_cregs {
118160

119161
extern struct page_table_entry_pae kvm_pagetable[];
120162
extern struct intr_descriptor kvm_idt[X86_INTR_COUNT];
163+
extern struct segment_descriptor kvm_gdt[KVM_GDT_SIZE];
121164

122165
/* Page table helper functions */
123166
uintptr_t kvm_get_page_address_pae(const struct page_table_entry_pae *entry);
124167

168+
/* Segment descriptor table functions */
169+
void kvm_set_segment_descriptor(struct segment_descriptor *dst,
170+
uint64_t baseaddr, uint32_t limit, unsigned int flags);
171+
void kvm_parse_segment_descriptor(struct segment_descriptor *src,
172+
uint64_t *baseaddr, uint32_t *limit, unsigned int *flags);
173+
int kvm_find_free_descriptor(const struct segment_descriptor *table,
174+
size_t size);
175+
unsigned int kvm_create_stack_descriptor(struct segment_descriptor *table,
176+
size_t tabsize, void *stack_base);
177+
125178
/* Functions for querying CPU info and status */
126179
void kvm_get_cpuid(unsigned int eax, unsigned int ecx, struct kvm_cpuid *buf);
127180
void kvm_read_cregs(struct kvm_cregs *buf);

testcases/kernel/kvm/lib_x86.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,98 @@ uintptr_t kvm_get_page_address_pae(const struct page_table_entry_pae *entry)
110110
return entry->address << 12;
111111
}
112112

113+
#ifdef __x86_64__
114+
static void kvm_set_segment_descriptor64(struct segment_descriptor64 *dst,
115+
uint64_t baseaddr, uint32_t limit, unsigned int flags)
116+
{
117+
118+
dst->baseaddr_lo = baseaddr & 0xffffff;
119+
dst->baseaddr_hi = baseaddr >> 24;
120+
dst->limit_lo = limit & 0xffff;
121+
dst->limit_hi = limit >> 16;
122+
dst->flags_lo = flags & 0xff;
123+
dst->flags_hi = (flags >> 8) & 0xf;
124+
dst->reserved = 0;
125+
}
126+
#endif
127+
128+
void kvm_set_segment_descriptor(struct segment_descriptor *dst,
129+
uint64_t baseaddr, uint32_t limit, unsigned int flags)
130+
{
131+
if (limit >> 20)
132+
tst_brk(TBROK, "Segment limit out of range");
133+
134+
#ifdef __x86_64__
135+
/* System descriptors have double size in 64bit mode */
136+
if (!(flags & SEGFLAG_NSYSTEM)) {
137+
kvm_set_segment_descriptor64((struct segment_descriptor64 *)dst,
138+
baseaddr, limit, flags);
139+
return;
140+
}
141+
#endif
142+
143+
if (baseaddr >> 32)
144+
tst_brk(TBROK, "Segment base address out of range");
145+
146+
dst->baseaddr_lo = baseaddr & 0xffffff;
147+
dst->baseaddr_hi = baseaddr >> 24;
148+
dst->limit_lo = limit & 0xffff;
149+
dst->limit_hi = limit >> 16;
150+
dst->flags_lo = flags & 0xff;
151+
dst->flags_hi = (flags >> 8) & 0xf;
152+
}
153+
154+
void kvm_parse_segment_descriptor(struct segment_descriptor *src,
155+
uint64_t *baseaddr, uint32_t *limit, unsigned int *flags)
156+
{
157+
if (baseaddr) {
158+
*baseaddr = (((uint64_t)src->baseaddr_hi) << 24) |
159+
src->baseaddr_lo;
160+
}
161+
162+
if (limit)
163+
*limit = (((uint32_t)src->limit_hi) << 16) | src->limit_lo;
164+
165+
if (flags)
166+
*flags = (((uint32_t)src->flags_hi) << 8) | src->flags_lo;
167+
}
168+
169+
int kvm_find_free_descriptor(const struct segment_descriptor *table,
170+
size_t size)
171+
{
172+
const struct segment_descriptor *ptr;
173+
size_t i;
174+
175+
for (i = 0, ptr = table; i < size; i++, ptr++) {
176+
if (!(ptr->flags_lo & SEGFLAG_PRESENT))
177+
return i;
178+
179+
#ifdef __x86_64__
180+
/* System descriptors have double size in 64bit mode */
181+
if (!(ptr->flags_lo & SEGFLAG_NSYSTEM)) {
182+
ptr++;
183+
i++;
184+
}
185+
#endif
186+
}
187+
188+
return -1;
189+
}
190+
191+
unsigned int kvm_create_stack_descriptor(struct segment_descriptor *table,
192+
size_t tabsize, void *stack_base)
193+
{
194+
int ret = kvm_find_free_descriptor(table, tabsize);
195+
196+
if (ret < 0)
197+
tst_brk(TBROK, "Descriptor table is full");
198+
199+
kvm_set_segment_descriptor(table + ret, 0,
200+
(((uintptr_t)stack_base) - 1) >> 12, SEGTYPE_STACK |
201+
SEGFLAG_PRESENT | SEGFLAG_32BIT | SEGFLAG_PAGE_LIMIT);
202+
return ret;
203+
}
204+
113205
void kvm_get_cpuid(unsigned int eax, unsigned int ecx, struct kvm_cpuid *buf)
114206
{
115207
asm (

0 commit comments

Comments
 (0)