From 3211be5ff46abb597492a9d16746c51111e3b257 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Sat, 16 Dec 2023 15:45:11 +0100 Subject: [PATCH] x86: replace magic numbers in GDT descriptors Linus suggested replacing the magic numbers in the GDT descriptors using preprocessor macros. This patch isn't precisely what was asked for; rationale as follows: Designing the interface properly is actually pretty hard -- there are several constraints: - you want the final expressions to be readable at a glance; something like GDT_ENTRY_FLAGS(5, 1, 0, 1, 0, 1, 1, 0) isn't because you need to visit the definition to understand what each parameter represents and then match up parameters in the user and the definition (which is hard when there are so many of them) - you want the final expressions to be fairly short/information-dense; something like GDT_ENTRY_PRESENT | GDT_ENTRY_DATA_WRITABLE | GDT_ENTRY_SYSTEM | GDT_ENTRY_DB | GDT_ENTRY_GRANULARITY_4K is a bit too verbose to write out every time and is actually hard to read as well because of all the repetition - you may want to assume defaults for some things (e.g. entries are DPL-0 a.k.a. kernel segments by default) and allow the user to override the default -- but this works best if you can OR in the override; if you want DPL-3 by default and override with DPL-0 you would need to start masking off bits instead of OR-ing them in and that just becomes harder to read - you may want to parameterize some things (e.g. CODE vs. DATA or KERNEL vs. USER) since both values are used and you don't really want prefer either one by default -- or DPL, which is always some value that is always specified This patch tries to balance these requirements and has two layers of definitions -- low-level and high-level: - the low-level defines are the mapping between human-readable names and the actual bit numbers - the high-level defines are the mapping from high-level intent to combinations of low-level flags, representing roughly a tuple (data/code, 64/32/16-bits) plus an override for DPL-3, since that's relatively rare but still very important to mark properly for those segments. Things that are still not so good in this patch are: - some lines overflow 80 columns, although I've heard that coule be fine in 2023 - we have *_BIOS variants for 32-bit code and data segments that don't have the G flag set and give the limit in terms of bytes instead of pages While preparing the patch I also came across some things that are unclear to me: - why do we want some segments with the A (accessed) bit set and some with it cleared -- is there an actual reason for the difference, or could we just set it for all of them? - why does setup_percpu_segment() want the DB (size) flag clear? This seems to indicate that it's a 16-bit segment -- is this correct? I took the liberty to remove some comments from the PNPBIOS/APMBIOS entries that are now superfluous since what they were expressing is now directly readable from the code. I used this to find all the existing users of the GDT_ENTRY*() macros: $ git grep -P 'GDT_ENTRY(_INIT)?\(' Testing: I added a selftest that verifies that the new symbolic values are the same as the old numeric ones, although it's more of a test that this commit doesn't change anything as opposed to a true self test; run it with: $ make -C tools/testing/selftests/x86 desc $ tools/testing/selftests/x86/desc_64 Link: https://lore.kernel.org/all/CAHk-=wib5XLebuEra7y2YH96wxdk=8vJnA8XoVq0FExpzVvN=Q@mail.gmail.com/ Signed-off-by: Vegard Nossum --- arch/x86/boot/pm.c | 7 +- arch/x86/include/asm/desc_defs.h | 61 +++++++++-- arch/x86/kernel/apm_32.c | 2 +- arch/x86/kernel/cpu/common.c | 50 ++++----- arch/x86/kernel/head64.c | 6 +- arch/x86/kernel/setup_percpu.c | 4 +- arch/x86/platform/pvh/head.S | 7 +- arch/x86/realmode/rm/reboot.S | 3 +- drivers/firmware/efi/libstub/x86-5lvl.c | 4 +- drivers/pnp/pnpbios/bioscalls.c | 2 +- tools/testing/selftests/x86/Makefile | 2 +- tools/testing/selftests/x86/desc.c | 134 ++++++++++++++++++++++++ 12 files changed, 228 insertions(+), 54 deletions(-) create mode 100644 tools/testing/selftests/x86/desc.c diff --git a/arch/x86/boot/pm.c b/arch/x86/boot/pm.c index 40031a614712..ab35b52d2c4b 100644 --- a/arch/x86/boot/pm.c +++ b/arch/x86/boot/pm.c @@ -11,6 +11,7 @@ */ #include "boot.h" +#include #include /* @@ -67,13 +68,13 @@ static void setup_gdt(void) being 8-byte unaligned. Intel recommends 16 byte alignment. */ static const u64 boot_gdt[] __attribute__((aligned(16))) = { /* CS: code, read/execute, 4 GB, base 0 */ - [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff), + [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(DESC_CODE32 | _DESC_ACCESSED, 0, 0xfffff), /* DS: data, read/write, 4 GB, base 0 */ - [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff), + [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(DESC_DATA32 | _DESC_ACCESSED, 0, 0xfffff), /* TSS: 32-bit tss, 104 bytes, base 4096 */ /* We only have a TSS here to keep Intel VT happy; we don't actually use it for anything. */ - [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103), + [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(DESC_TSS32, 4096, 103), }; /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead of the gdt_ptr contents. Thus, make it static so it will diff --git a/arch/x86/include/asm/desc_defs.h b/arch/x86/include/asm/desc_defs.h index f7e7099af595..8843332b0975 100644 --- a/arch/x86/include/asm/desc_defs.h +++ b/arch/x86/include/asm/desc_defs.h @@ -8,6 +8,49 @@ * archs. */ +/* + * Low-level interface mapping flags/field names to bits + */ + +#define _DESC_ACCESSED 0x0001 +#define _DESC_CODE_READABLE 0x0002 +#define _DESC_DATA_WRITABLE 0x0002 +#define _DESC_DATA_DIRECTION_DOWN 0x0004 +#define _DESC_CODE_CONFORMING 0x0004 +#define _DESC_EXECUTABLE 0x0008 +#define _DESC_SYSTEM 0x0010 +#define _DESC_DPL(dpl) ((dpl) << 5) +#define _DESC_PRESENT 0x0080 + +#define _DESC_LONG_CODE 0x2000 +#define _DESC_DB 0x4000 /* "size flag": 0 = 16-bit, 1 = 32-bit */ +#define _DESC_GRANULARITY_4K 0x8000 + +/* + * High-level interface mapping intended usage to low-level combinations + * of flags + */ + +#define _DESC_DATA (_DESC_PRESENT | _DESC_DATA_WRITABLE | _DESC_SYSTEM) +#define _DESC_CODE (_DESC_PRESENT | _DESC_CODE_READABLE | _DESC_SYSTEM | _DESC_EXECUTABLE) + +#define DESC_DATA16 (_DESC_DATA) +#define DESC_CODE16 (_DESC_CODE) + +#define DESC_DATA32 (_DESC_DATA | _DESC_GRANULARITY_4K | _DESC_DB) +#define DESC_DATA32_BIOS (_DESC_DATA | _DESC_DB) + +#define DESC_CODE32 (_DESC_CODE | _DESC_GRANULARITY_4K | _DESC_DB) +#define DESC_CODE32_BIOS (_DESC_CODE | _DESC_DB) + +/* don't ask why, this is just how you specify a 32-bit TSS descriptor */ +#define DESC_TSS32 (_DESC_PRESENT | _DESC_EXECUTABLE | _DESC_ACCESSED) + +#define DESC_DATA64 (_DESC_DATA | _DESC_GRANULARITY_4K | _DESC_DB) +#define DESC_CODE64 (_DESC_CODE | _DESC_GRANULARITY_4K | _DESC_LONG_CODE) + +#define DESC_USER (_DESC_DPL(3)) + #ifndef __ASSEMBLY__ #include @@ -27,14 +70,14 @@ struct desc_struct { .base0 = (u16) (base), \ .base1 = ((base) >> 16) & 0xFF, \ .base2 = ((base) >> 24) & 0xFF, \ - .type = (flags & 0x0f), \ - .s = (flags >> 4) & 0x01, \ - .dpl = (flags >> 5) & 0x03, \ - .p = (flags >> 7) & 0x01, \ - .avl = (flags >> 12) & 0x01, \ - .l = (flags >> 13) & 0x01, \ - .d = (flags >> 14) & 0x01, \ - .g = (flags >> 15) & 0x01, \ + .type = ((flags) & 0x0f), \ + .s = ((flags) >> 4) & 0x01, \ + .dpl = ((flags) >> 5) & 0x03, \ + .p = ((flags) >> 7) & 0x01, \ + .avl = ((flags) >> 12) & 0x01, \ + .l = ((flags) >> 13) & 0x01, \ + .d = ((flags) >> 14) & 0x01, \ + .g = ((flags) >> 15) & 0x01, \ } enum { @@ -94,6 +137,7 @@ struct gate_struct { typedef struct gate_struct gate_desc; +#ifndef _SETUP static inline unsigned long gate_offset(const gate_desc *g) { #ifdef CONFIG_X86_64 @@ -108,6 +152,7 @@ static inline unsigned long gate_segment(const gate_desc *g) { return g->segment; } +#endif struct desc_ptr { unsigned short size; diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index 5934ee5bc087..76a5ced278c2 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -420,7 +420,7 @@ static DEFINE_MUTEX(apm_mutex); * This is for buggy BIOS's that refer to (real mode) segment 0x40 * even though they are called in protected mode. */ -static struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(0x4092, +static struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(DESC_DATA32_BIOS, (unsigned long)__va(0x400UL), PAGE_SIZE - 0x400 - 1); static const char driver_version[] = "1.16ac"; /* no spaces */ diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index b14fc8c1c953..32934a0656af 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -188,45 +188,37 @@ DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = { * TLS descriptors are currently at a different place compared to i386. * Hopefully nobody expects them at a fixed place (Wine?) */ - [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff), - [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff), - [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(0xc093, 0, 0xfffff), - [GDT_ENTRY_DEFAULT_USER32_CS] = GDT_ENTRY_INIT(0xc0fb, 0, 0xfffff), - [GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(0xc0f3, 0, 0xfffff), - [GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(0xa0fb, 0, 0xfffff), + [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(DESC_CODE32 | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(DESC_CODE64 | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(DESC_DATA64 | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_DEFAULT_USER32_CS] = GDT_ENTRY_INIT(DESC_CODE32 | DESC_USER | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(DESC_DATA64 | DESC_USER | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(DESC_CODE64 | DESC_USER | _DESC_ACCESSED, 0, 0xfffff), #else - [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xc09a, 0, 0xfffff), - [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(0xc092, 0, 0xfffff), - [GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(0xc0fa, 0, 0xfffff), - [GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(0xc0f2, 0, 0xfffff), + [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(DESC_CODE32, 0, 0xfffff), + [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(DESC_DATA32, 0, 0xfffff), + [GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(DESC_CODE32 | DESC_USER, 0, 0xfffff), + [GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(DESC_DATA32 | DESC_USER, 0, 0xfffff), /* * Segments used for calling PnP BIOS have byte granularity. * They code segments and data segments have fixed 64k limits, * the transfer segment sizes are set at run time. */ - /* 32-bit code */ - [GDT_ENTRY_PNPBIOS_CS32] = GDT_ENTRY_INIT(0x409a, 0, 0xffff), - /* 16-bit code */ - [GDT_ENTRY_PNPBIOS_CS16] = GDT_ENTRY_INIT(0x009a, 0, 0xffff), - /* 16-bit data */ - [GDT_ENTRY_PNPBIOS_DS] = GDT_ENTRY_INIT(0x0092, 0, 0xffff), - /* 16-bit data */ - [GDT_ENTRY_PNPBIOS_TS1] = GDT_ENTRY_INIT(0x0092, 0, 0), - /* 16-bit data */ - [GDT_ENTRY_PNPBIOS_TS2] = GDT_ENTRY_INIT(0x0092, 0, 0), + [GDT_ENTRY_PNPBIOS_CS32] = GDT_ENTRY_INIT(DESC_CODE32_BIOS, 0, 0xffff), + [GDT_ENTRY_PNPBIOS_CS16] = GDT_ENTRY_INIT(DESC_CODE16, 0, 0xffff), + [GDT_ENTRY_PNPBIOS_DS] = GDT_ENTRY_INIT(DESC_DATA16, 0, 0xffff), + [GDT_ENTRY_PNPBIOS_TS1] = GDT_ENTRY_INIT(DESC_DATA16, 0, 0), + [GDT_ENTRY_PNPBIOS_TS2] = GDT_ENTRY_INIT(DESC_DATA16, 0, 0), /* * The APM segments have byte granularity and their bases * are set at run time. All have 64k limits. */ - /* 32-bit code */ - [GDT_ENTRY_APMBIOS_BASE] = GDT_ENTRY_INIT(0x409a, 0, 0xffff), - /* 16-bit code */ - [GDT_ENTRY_APMBIOS_BASE+1] = GDT_ENTRY_INIT(0x009a, 0, 0xffff), - /* data */ - [GDT_ENTRY_APMBIOS_BASE+2] = GDT_ENTRY_INIT(0x4092, 0, 0xffff), - - [GDT_ENTRY_ESPFIX_SS] = GDT_ENTRY_INIT(0xc092, 0, 0xfffff), - [GDT_ENTRY_PERCPU] = GDT_ENTRY_INIT(0xc092, 0, 0xfffff), + [GDT_ENTRY_APMBIOS_BASE] = GDT_ENTRY_INIT(DESC_CODE32_BIOS, 0, 0xffff), + [GDT_ENTRY_APMBIOS_BASE+1] = GDT_ENTRY_INIT(DESC_CODE16, 0, 0xffff), + [GDT_ENTRY_APMBIOS_BASE+2] = GDT_ENTRY_INIT(DESC_DATA32_BIOS, 0, 0xffff), + + [GDT_ENTRY_ESPFIX_SS] = GDT_ENTRY_INIT(DESC_DATA32, 0, 0xfffff), + [GDT_ENTRY_PERCPU] = GDT_ENTRY_INIT(DESC_DATA32, 0, 0xfffff), #endif } }; EXPORT_PER_CPU_SYMBOL_GPL(gdt_page); diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 05a110c97111..00dbddfdfece 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -71,9 +71,9 @@ EXPORT_SYMBOL(vmemmap_base); * GDT used on the boot CPU before switching to virtual addresses. */ static struct desc_struct startup_gdt[GDT_ENTRIES] __initdata = { - [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff), - [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff), - [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(0xc093, 0, 0xfffff), + [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(DESC_CODE32 | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(DESC_CODE64 | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(DESC_DATA64 | _DESC_ACCESSED, 0, 0xfffff), }; /* diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 2c97bf7b56ae..f2583de97a64 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c @@ -106,8 +106,8 @@ void __init pcpu_populate_pte(unsigned long addr) static inline void setup_percpu_segment(int cpu) { #ifdef CONFIG_X86_32 - struct desc_struct d = GDT_ENTRY_INIT(0x8092, per_cpu_offset(cpu), - 0xFFFFF); + struct desc_struct d = GDT_ENTRY_INIT(DESC_DATA32 & ~_DESC_DB, + per_cpu_offset(cpu), 0xFFFFF); write_gdt_entry(get_cpu_gdt_rw(cpu), GDT_ENTRY_PERCPU, &d, DESCTYPE_S); #endif diff --git a/arch/x86/platform/pvh/head.S b/arch/x86/platform/pvh/head.S index c4365a05ab83..7c6a1089ce1c 100644 --- a/arch/x86/platform/pvh/head.S +++ b/arch/x86/platform/pvh/head.S @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -148,11 +149,11 @@ SYM_DATA_END(gdt) SYM_DATA_START_LOCAL(gdt_start) .quad 0x0000000000000000 /* NULL descriptor */ #ifdef CONFIG_X86_64 - .quad GDT_ENTRY(0xa09a, 0, 0xfffff) /* PVH_CS_SEL */ + .quad GDT_ENTRY(DESC_CODE64, 0, 0xfffff) /* PVH_CS_SEL */ #else - .quad GDT_ENTRY(0xc09a, 0, 0xfffff) /* PVH_CS_SEL */ + .quad GDT_ENTRY(DESC_CODE32, 0, 0xfffff) /* PVH_CS_SEL */ #endif - .quad GDT_ENTRY(0xc092, 0, 0xfffff) /* PVH_DS_SEL */ + .quad GDT_ENTRY(DESC_DATA32, 0, 0xfffff) /* PVH_DS_SEL */ SYM_DATA_END_LABEL(gdt_start, SYM_L_LOCAL, gdt_end) .balign 16 diff --git a/arch/x86/realmode/rm/reboot.S b/arch/x86/realmode/rm/reboot.S index f10515b10e0a..5bc068b9acdd 100644 --- a/arch/x86/realmode/rm/reboot.S +++ b/arch/x86/realmode/rm/reboot.S @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include +#include #include #include #include @@ -153,5 +154,5 @@ SYM_DATA_START(machine_real_restart_gdt) * base value 0x100; since this is consistent with real mode * semantics we don't have to reload the segments once CR0.PE = 0. */ - .quad GDT_ENTRY(0x0093, 0x100, 0xffff) + .quad GDT_ENTRY(DESC_DATA16 | _DESC_ACCESSED, 0x100, 0xffff) SYM_DATA_END(machine_real_restart_gdt) diff --git a/drivers/firmware/efi/libstub/x86-5lvl.c b/drivers/firmware/efi/libstub/x86-5lvl.c index 479dd445acdc..005dd9b14f95 100644 --- a/drivers/firmware/efi/libstub/x86-5lvl.c +++ b/drivers/firmware/efi/libstub/x86-5lvl.c @@ -13,8 +13,8 @@ bool efi_no5lvl; static void (*la57_toggle)(void *cr3); static const struct desc_struct gdt[] = { - [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff), - [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff), + [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(DESC_CODE32 | _DESC_ACCESSED, 0, 0xfffff), + [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(DESC_CODE64 | _DESC_ACCESSED, 0, 0xfffff), }; /* diff --git a/drivers/pnp/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c index ddc6f2163c8e..1f31dce5835a 100644 --- a/drivers/pnp/pnpbios/bioscalls.c +++ b/drivers/pnp/pnpbios/bioscalls.c @@ -60,7 +60,7 @@ do { \ set_desc_limit(&gdt[(selname) >> 3], (size) - 1); \ } while(0) -static struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(0x4092, +static struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(DESC_DATA32_BIOS, (unsigned long)__va(0x400UL), PAGE_SIZE - 0x400 - 1); /* diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 0b872c0a42d2..509310cec626 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -14,7 +14,7 @@ TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap check_initial_reg_state sigreturn iopl ioperm \ test_vsyscall mov_ss_trap \ syscall_arg_fault fsgsbase_restore sigaltstack -TARGETS_C_BOTHBITS += nx_stack +TARGETS_C_BOTHBITS += nx_stack desc TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ vdso_restorer diff --git a/tools/testing/selftests/x86/desc.c b/tools/testing/selftests/x86/desc.c new file mode 100644 index 000000000000..7f30a482ad5d --- /dev/null +++ b/tools/testing/selftests/x86/desc.c @@ -0,0 +1,134 @@ +#include +#include + +#define u16 uint16_t + +#include "../../../../arch/x86/include/asm/desc_defs.h" + +struct testcase { + const char *name; + uint16_t flags; + uint16_t flags2; +}; + +#define TESTCASE(flags_, name_, flags2_) \ + { \ + .name = name_, \ + .flags = flags_, \ + .flags2 = flags2_, \ + } + +/* + * Find all of the existing uses of raw descriptor values with: + * + * git grep -P 'GDT_ENTRY(_INIT)?\(' v6.7-rc5 + * + */ +struct testcase testcases[] = { + // arch/x86/boot/pm.c + TESTCASE(0xc09b, "boot_cs", DESC_CODE32 | _DESC_ACCESSED), + TESTCASE(0xc093, "boot_ds", DESC_DATA32 | _DESC_ACCESSED), + TESTCASE(0x0089, "boot_tss", DESC_TSS32), + + // arch/x86/kernel/apm_32.c + TESTCASE(0x4092, "apmbios_32", DESC_DATA32_BIOS), + + // arch/x86/kernel/cpu/common.c + TESTCASE(0xc09b, "kernel32_cs", DESC_CODE32 | _DESC_ACCESSED), + TESTCASE(0xa09b, "kernel64_cs", DESC_CODE64 | _DESC_ACCESSED), + TESTCASE(0xc093, "kernel64_ds", DESC_DATA64 | _DESC_ACCESSED), + TESTCASE(0xc0fb, "default_user32_cs", DESC_CODE32 | DESC_USER | _DESC_ACCESSED), + TESTCASE(0xc0f3, "default_user64_ds", DESC_DATA64 | DESC_USER | _DESC_ACCESSED), + TESTCASE(0xa0fb, "default_user64_cs", DESC_CODE64 | DESC_USER | _DESC_ACCESSED), + + TESTCASE(0xc09a, "kernel32_cs", DESC_CODE32), + TESTCASE(0xc092, "kernel32_ds", DESC_DATA32), + TESTCASE(0xc0fa, "default_user_cs", DESC_CODE32 | DESC_USER), + TESTCASE(0xc0f2, "default_user_ds", DESC_DATA32 | DESC_USER), + + TESTCASE(0x409a, "pnpbios_cs32", DESC_CODE32_BIOS), + TESTCASE(0x009a, "pbpbios_cs16", DESC_CODE16), + TESTCASE(0x0092, "pnpbios_ds", DESC_DATA16), + TESTCASE(0x0092, "pnpbios_ts1", DESC_DATA16), + TESTCASE(0x0092, "pnpbios_ts2", DESC_DATA16), + + TESTCASE(0x409a, "apmbios_cs32", DESC_CODE32_BIOS), + TESTCASE(0x009a, "apmbios_cs16", DESC_CODE16), + TESTCASE(0x4092, "apmbios_ds", DESC_DATA32_BIOS), + TESTCASE(0xc092, "espfix_ss", DESC_DATA32), + TESTCASE(0xc092, "percpu", DESC_DATA32), + + // arch/x86/kernel/head64.c + TESTCASE(0xc09b, "kernel32_cs", DESC_CODE32 | _DESC_ACCESSED), + TESTCASE(0xa09b, "kernel64_cs", DESC_CODE64 | _DESC_ACCESSED), + TESTCASE(0xc093, "kernel64_ds", DESC_DATA64 | _DESC_ACCESSED), + + // arch/x86/kernel/setup_percpu.c + TESTCASE(0x8092, "percpu_32", DESC_DATA32 & ~_DESC_DB), + + // arch/x86/platform/pvh/head.S + TESTCASE(0xa09a, "pvh_cs64", DESC_CODE64), + TESTCASE(0xc09a, "pvh_cs32", DESC_CODE32), + TESTCASE(0xc092, "pvh_ds", DESC_DATA32), + + // arch/x86/realmode/rm/reboot.S + TESTCASE(0x0093, "data16", DESC_DATA16 | _DESC_ACCESSED), + + // drivers/firmware/efi/libstub/x86-5lvl.c + TESTCASE(0xc09b, "kernel32_cs", DESC_CODE32 | _DESC_ACCESSED), + TESTCASE(0xa09b, "kernel64_cs", DESC_CODE64 | _DESC_ACCESSED), + + // drivers/pnp/pnpbios/bioscalls.c + TESTCASE(0x4092, "pnpbios_bad", DESC_DATA32_BIOS), +}; + +// decode flags based on struct desc_struct + GDT_ENTRY() +// for debugging only + +struct desc_flags { + u16 base1: 8, a:1, rw:1, dc:1, e: 1, s: 1, dpl: 2, p: 1; + u16 limit1: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8; +} __attribute__((packed)); + +#define DESC_FLAGS(flags) \ + { \ + .a = (flags >> 0) & 0x01, \ + .rw = (flags >> 1) & 0x01, \ + .dc = (flags >> 2) & 0x01, \ + .e = (flags >> 3) & 0x01, \ + .s = (flags >> 4) & 0x01, \ + .dpl = (flags >> 5) & 0x03, \ + .p = (flags >> 7) & 0x01, \ + .avl = (flags >> 12) & 0x01, \ + .l = (flags >> 13) & 0x01, \ + .d = (flags >> 14) & 0x01, \ + .g = (flags >> 15) & 0x01, \ + } + +int main(int argc, char *argv[]) +{ + for (int i = 0; i < sizeof(testcases) / sizeof(*testcases); ++i) { + struct testcase t = testcases[i]; + struct desc_flags desc = DESC_FLAGS(t.flags); + int ok = (t.flags == t.flags2); + + printf("%s %04x %04x a=%x rw=%x dc=%x e=%x s=%x dpl=%x p=%x avl=%x l=%x db=%x g=%x %s\n", + ok ? "ok!" : "err", + t.flags, + t.flags2, + desc.a, + desc.rw, + desc.dc, + desc.e, + desc.s, + desc.dpl, + desc.p, + desc.avl, + desc.l, + desc.d, + desc.g, + t.name); + } + + return 0; +} -- 2.34.1