x86: generate RAM-based GDT dynamically

We will need this for stack memory protection scenarios
where a writable GDT with Task State Segment descriptors
will be used. The addresses of the TSS segments cannot be
put in the GDT via preprocessor magic due to architecture
requirments that the address be split up into different
fields in the segment descriptor.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2017-07-14 15:29:17 -07:00 committed by Anas Nashif
commit 08c291306e
7 changed files with 160 additions and 42 deletions

View file

@ -898,6 +898,9 @@ include $(srctree)/arch/x86/Makefile.idt
ifeq ($(CONFIG_X86_MMU),y)
include $(srctree)/arch/x86/Makefile.mmu
endif
ifeq ($(CONFIG_GDT_DYNAMIC),y)
include $(srctree)/arch/x86/Makefile.gdt
endif
endif
ifeq ($(CONFIG_GEN_ISR_TABLES),y)

26
arch/x86/Makefile.gdt Normal file
View file

@ -0,0 +1,26 @@
ifeq ($(KBUILD_VERBOSE),1)
GENGDT_EXTRA_ARGS := --verbose
else
GENGDT_EXTRA_ARGS :=
endif
GENGDT := $(srctree)/scripts/gen_gdt.py
OUTPUT_FORMAT ?= elf32-i386
OUTPUT_ARCH ?= i386
quiet_cmd_gen_gdt = GDT $@
cmd_gen_gdt = \
( \
$(GENGDT) --kernel $(PREBUILT_KERNEL) \
--output-gdt gdt.bin \
$(GENGDT_EXTRA_ARGS) && \
$(OBJCOPY) -I binary -B $(OUTPUT_ARCH) -O $(OUTPUT_FORMAT) \
--rename-section .data=gdt_ram_data gdt.bin $@ \
)
gdt.o: $(PREBUILT_KERNEL) $(GENGDT)
$(call cmd,gen_gdt)
GENERATED_KERNEL_OBJECT_FILES += gdt.o

View file

@ -19,7 +19,6 @@ obj-y += cpuhalt.o \
obj-$(CONFIG_IRQ_OFFLOAD) += irq_offload.o
obj-$(CONFIG_FP_SHARING) += float.o
obj-$(CONFIG_GDT_DYNAMIC) += gdt.o
obj-$(CONFIG_REBOOT_RST_CNT) += reboot_rst_cnt.o
obj-$(CONFIG_X86_MMU) += x86_mmu.o

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2011-2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Global Descriptor Table support
*
* This module contains routines for updating the global descriptor table (GDT)
* for the IA-32 architecture.
*/
#include <linker/linker-defs.h>
#include <toolchain.h>
#include <linker/sections.h>
#include <kernel_structs.h>
#include <arch/cpu.h>
#include <arch/x86/segmentation.h>
/*
* The RAM based global descriptor table. It is aligned on an 8 byte boundary
* as the Intel manuals recommend this for best performance, see
* Section 3.5.1 of IA architecture SW developer manual, Vol 3.
*
* TODO: CPU never looks at the 8-byte zero entry at all. Save a few bytes by
* stuffing the 6-byte pseudo descriptor there.
*/
static struct segment_descriptor _gdt_entries[] __aligned(8) = {
DT_ZERO_ENTRY,
DT_CODE_SEG_ENTRY(0, 0xFFFFF, DT_GRAN_PAGE, 0, DT_READABLE,
DT_NONCONFORM),
DT_DATA_SEG_ENTRY(0, 0xFFFFF, DT_GRAN_PAGE, 0, DT_WRITABLE,
DT_EXPAND_UP)
};
struct pseudo_descriptor _gdt = DT_INIT(_gdt_entries);

View file

@ -151,6 +151,19 @@ SECTIONS
#include <custom-rwdata.ld>
#endif
#ifdef CONFIG_GDT_DYNAMIC
. = ALIGN(8);
_gdt = .;
#ifdef LINKER_PASS2
KEEP(*(gdt_ram_data))
#else /* LINKER_PASS2 */
#define GDT_NUM_ENTRIES 3
. += GDT_NUM_ENTRIES * 8;
#endif /* LINKER_PASS2 */
#endif /* CONFIG_GDT_DYNAMIC */
#ifdef CONFIG_X86_MMU
/* Page Tables are located here if MMU is enabled.*/
MMU_PAGE_ALIGN

View file

@ -383,7 +383,7 @@ struct __packed far_ptr {
#define DT_INIT(entries) { sizeof(entries) - 1, &entries[0] }
#ifdef CONFIG_SET_GDT
/* This is either the ROM-based GDT in crt0.S or RAM-based in gdt.c,
/* This is either the ROM-based GDT in crt0.S or generated by gen_gdt.py,
* depending on CONFIG_GDT_DYNAMIC
*/
extern struct pseudo_descriptor _gdt;

117
scripts/gen_gdt.py Executable file
View file

@ -0,0 +1,117 @@
#!/usr/bin/env python3
#
# Copyright (c) 2017 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
import argparse
import sys
import struct
import os
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
gdt_pd_fmt = "<HIH"
FLAGS_GRAN = 1 << 7 # page granularity
ACCESS_EX = 1 << 3 # executable
ACCESS_DC = 1 << 2 # direction/conforming
ACCESS_RW = 1 << 1 # read or write permission
# 6 byte pseudo descriptor, but we're going to actually use this as the
# zero descriptor and return 8 bytes
def create_gdt_pseudo_desc(addr, size):
# ...and take back one byte for the Intel god whose Ark this is...
size = size - 1
return struct.pack(gdt_pd_fmt, size, addr, 0)
# Limit argument always in bytes
def chop_base_limit(base, limit):
base_lo = base & 0xFFFF
base_mid = (base >> 16) & 0xFF
base_hi = (base >> 24) & 0xFF
limit_lo = limit & 0xFFFF
limit_hi = (limit >> 16) & 0xF
return (base_lo, base_mid, base_hi, limit_lo, limit_hi)
gdt_ent_fmt = "<HHBBBB"
def create_code_data_entry(base, limit, dpl, flags, access):
base_lo, base_mid, base_hi, limit_lo, limit_hi = chop_base_limit(base,
limit)
# This is a valid descriptor
present = 1
# 32-bit protected mode
size = 1
# 1 = code or data, 0 = system type
desc_type = 1
# Just set accessed to 1 already so the CPU doesn't need it update it,
# prevents freakouts if the GDT is in ROM, we don't care about this
# bit in the OS
accessed = 1
access = access | (present << 7) | (desc_type << 4) | accessed
flags = flags | (size << 6) | limit_hi
return struct.pack(gdt_ent_fmt, limit_lo, base_lo, base_mid,
access, flags, base_hi)
def get_symbols(obj):
for section in obj.iter_sections():
if isinstance(section, SymbolTableSection):
return {sym.name: sym.entry.st_value
for sym in section.iter_symbols()}
raise LookupError("Could not find symbol table")
def parse_args():
global args
parser = argparse.ArgumentParser(description = __doc__,
formatter_class = argparse.RawDescriptionHelpFormatter)
parser.add_argument("-k", "--kernel", required=True,
help="Zephyr kernel image")
parser.add_argument("-v", "--verbose", action="store_true",
help="Print extra debugging information")
parser.add_argument("-o", "--output-gdt", required=True,
help="output GDT binary")
args = parser.parse_args()
def main():
parse_args()
with open(args.kernel, "rb") as fp:
kernel = ELFFile(fp)
syms = get_symbols(kernel)
num_entries = 3
gdt_base = syms["_gdt"]
with open(args.output_gdt, "wb") as fp:
# The pseudo descriptor is stuffed into the NULL descriptor
# since the CPU never looks at it
fp.write(create_gdt_pseudo_desc(gdt_base, num_entries * 8))
# Selector 0x08: code descriptor
fp.write(create_code_data_entry(0, 0xFFFFF, 0,
FLAGS_GRAN, ACCESS_EX | ACCESS_RW))
# Selector 0x10: data descriptor
fp.write(create_code_data_entry(0, 0xFFFFF, 0,
FLAGS_GRAN, ACCESS_RW))
if __name__ == "__main__":
main()