| #!/usr/bin/env python3 |
| # |
| # Copyright (c) 2017 Intel Corporation |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import argparse |
| import sys |
| import struct |
| import os |
| import elftools |
| from distutils.version import LooseVersion |
| from elftools.elf.elffile import ELFFile |
| from elftools.elf.sections import SymbolTableSection |
| |
| if LooseVersion(elftools.__version__) < LooseVersion('0.24'): |
| sys.stderr.write("pyelftools is out of date, need version 0.24 or later\n") |
| sys.exit(1) |
| |
| |
| def debug(text): |
| if not args.verbose: |
| return |
| sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
| |
| |
| def error(text): |
| sys.stderr.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
| sys.exit(1) |
| |
| |
| 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): |
| debug("create pseudo decriptor: %x %x" % (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): |
| debug("create code or data entry: %x %x %x %x %x" % |
| (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) | (dpl << 5) | (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 create_tss_entry(base, limit, dpl): |
| debug("create TSS entry: %x %x %x" % (base, limit, dpl)) |
| present = 1 |
| |
| base_lo, base_mid, base_hi, limit_lo, limit_hi, = chop_base_limit(base, |
| limit) |
| |
| type_code = 0x9 # non-busy 32-bit TSS descriptor |
| gran = 0 |
| |
| flags = (gran << 7) | limit_hi |
| type_byte = ((present << 7) | (dpl << 5) | type_code) |
| |
| return struct.pack(gdt_ent_fmt, limit_lo, base_lo, base_mid, |
| type_byte, 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() |
| if "VERBOSE" in os.environ: |
| args.verbose = 1 |
| |
| |
| def main(): |
| parse_args() |
| |
| with open(args.kernel, "rb") as fp: |
| kernel = ELFFile(fp) |
| syms = get_symbols(kernel) |
| |
| # NOTE: use-cases are extremely limited; we always have a basic flat |
| # code/data segments. If we are doing stack protection, we are going to |
| # have two TSS to manage the main task and the special task for double |
| # fault exception handling |
| if "CONFIG_USERSPACE" in syms: |
| num_entries = 7 |
| elif "CONFIG_HW_STACK_PROTECTION" in syms: |
| num_entries = 5 |
| else: |
| 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 num_entries >= 5: |
| main_tss = syms["_main_tss"] |
| df_tss = syms["_df_tss"] |
| |
| # Selector 0x18: main TSS |
| fp.write(create_tss_entry(main_tss, 0x67, 0)) |
| |
| # Selector 0x20: double-fault TSS |
| fp.write(create_tss_entry(df_tss, 0x67, 0)) |
| |
| if num_entries == 7: |
| # Selector 0x28: code descriptor, dpl = 3 |
| fp.write(create_code_data_entry(0, 0xFFFFF, 3, |
| FLAGS_GRAN, ACCESS_EX | ACCESS_RW)) |
| |
| # Selector 0x30: data descriptor, dpl = 3 |
| fp.write(create_code_data_entry(0, 0xFFFFF, 3, |
| FLAGS_GRAN, ACCESS_RW)) |
| |
| |
| if __name__ == "__main__": |
| main() |