Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (c) 2017 Intel Corporation |
| 4 | # |
| 5 | # SPDX-License-Identifier: Apache-2.0 |
| 6 | |
Andrew Boie | b8cbf21 | 2019-02-24 11:36:31 -0800 | [diff] [blame] | 7 | """Generate a Global Descriptor Table (GDT) for x86 CPUs. |
| 8 | |
| 9 | For additional detail on GDT and x86 memory management, please |
| 10 | consult the IA Architecture SW Developer Manual, vol. 3. |
| 11 | |
| 12 | This script accepts as input the zephyr_prebuilt.elf binary, |
| 13 | which is a link of the Zephyr kernel without various build-time |
| 14 | generated data structures (such as the GDT) inserted into it. |
| 15 | This kernel image has been properly padded such that inserting |
| 16 | these data structures will not disturb the memory addresses of |
| 17 | other symbols. |
| 18 | |
Andrew Boie | 98bcc51 | 2020-07-17 19:02:30 -0700 | [diff] [blame] | 19 | The input kernel ELF binary is used to obtain the following |
| 20 | information: |
| 21 | |
| 22 | - Memory addresses of the Main and Double Fault TSS structures |
| 23 | so GDT descriptors can be created for them |
| 24 | - Memory addresses of where the GDT lives in memory, so that this |
| 25 | address can be populated in the GDT pseudo descriptor |
| 26 | - whether userspace or HW stack protection are enabled in Kconfig |
| 27 | |
Andrew Boie | b8cbf21 | 2019-02-24 11:36:31 -0800 | [diff] [blame] | 28 | The output is a GDT whose contents depend on the kernel |
| 29 | configuration. With no memory protection features enabled, |
| 30 | we generate flat 32-bit code and data segments. If hardware- |
| 31 | based stack overflow protection or userspace is enabled, |
| 32 | we additionally create descriptors for the main and double- |
| 33 | fault IA tasks, needed for userspace privilege elevation and |
| 34 | double-fault handling. If userspace is enabled, we also create |
| 35 | flat code/data segments for ring 3 execution. |
| 36 | """ |
| 37 | |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 38 | import argparse |
| 39 | import sys |
| 40 | import struct |
| 41 | import os |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 42 | |
Julien Massot | 36f116b | 2021-11-19 16:30:43 +0100 | [diff] [blame] | 43 | from packaging import version |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 44 | |
| 45 | import elftools |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 46 | from elftools.elf.elffile import ELFFile |
| 47 | from elftools.elf.sections import SymbolTableSection |
| 48 | |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 49 | |
Julien Massot | 36f116b | 2021-11-19 16:30:43 +0100 | [diff] [blame] | 50 | if version.parse(elftools.__version__) < version.parse('0.24'): |
Ulf Magnusson | 50b9b12 | 2019-09-07 14:41:01 +0200 | [diff] [blame] | 51 | sys.exit("pyelftools is out of date, need version 0.24 or later") |
Andrew Boie | 3aecba1 | 2017-07-25 09:44:30 -0700 | [diff] [blame] | 52 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 53 | |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 54 | def debug(text): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 55 | """Display debug message if --verbose""" |
| 56 | if args.verbose: |
| 57 | sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 58 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 59 | |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 60 | def error(text): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 61 | """Exit program with an error message""" |
Ulf Magnusson | 50b9b12 | 2019-09-07 14:41:01 +0200 | [diff] [blame] | 62 | sys.exit(os.path.basename(sys.argv[0]) + ": " + text) |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 63 | |
| 64 | |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 65 | GDT_PD_FMT = "<HIH" |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 66 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 67 | FLAGS_GRAN = 1 << 7 # page granularity |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 68 | ACCESS_EX = 1 << 3 # executable |
| 69 | ACCESS_DC = 1 << 2 # direction/conforming |
| 70 | ACCESS_RW = 1 << 1 # read or write permission |
| 71 | |
| 72 | # 6 byte pseudo descriptor, but we're going to actually use this as the |
| 73 | # zero descriptor and return 8 bytes |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 74 | |
| 75 | |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 76 | def create_gdt_pseudo_desc(addr, size): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 77 | """Create pseudo GDT descriptor""" |
Nazar Kazakov | 9713f0d | 2022-02-24 12:00:55 +0000 | [diff] [blame] | 78 | debug("create pseudo descriptor: %x %x" % (addr, size)) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 79 | # ...and take back one byte for the Intel god whose Ark this is... |
| 80 | size = size - 1 |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 81 | return struct.pack(GDT_PD_FMT, size, addr, 0) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 82 | |
| 83 | |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 84 | def chop_base_limit(base, limit): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 85 | """Limit argument always in bytes""" |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 86 | base_lo = base & 0xFFFF |
| 87 | base_mid = (base >> 16) & 0xFF |
| 88 | base_hi = (base >> 24) & 0xFF |
| 89 | |
| 90 | limit_lo = limit & 0xFFFF |
| 91 | limit_hi = (limit >> 16) & 0xF |
| 92 | |
| 93 | return (base_lo, base_mid, base_hi, limit_lo, limit_hi) |
| 94 | |
| 95 | |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 96 | GDT_ENT_FMT = "<HHBBBB" |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 97 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 98 | |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 99 | def create_code_data_entry(base, limit, dpl, flags, access): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 100 | """Create GDT entry for code or data""" |
Andrew Boie | a705eae | 2017-11-03 10:39:08 -0700 | [diff] [blame] | 101 | debug("create code or data entry: %x %x %x %x %x" % |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 102 | (base, limit, dpl, flags, access)) |
Andrew Boie | a705eae | 2017-11-03 10:39:08 -0700 | [diff] [blame] | 103 | |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 104 | base_lo, base_mid, base_hi, limit_lo, limit_hi = chop_base_limit(base, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 105 | limit) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 106 | |
| 107 | # This is a valid descriptor |
| 108 | present = 1 |
| 109 | |
| 110 | # 32-bit protected mode |
| 111 | size = 1 |
| 112 | |
| 113 | # 1 = code or data, 0 = system type |
| 114 | desc_type = 1 |
| 115 | |
| 116 | # Just set accessed to 1 already so the CPU doesn't need it update it, |
| 117 | # prevents freakouts if the GDT is in ROM, we don't care about this |
| 118 | # bit in the OS |
| 119 | accessed = 1 |
| 120 | |
Andrew Boie | a0da632 | 2017-08-01 09:30:09 -0700 | [diff] [blame] | 121 | access = access | (present << 7) | (dpl << 5) | (desc_type << 4) | accessed |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 122 | flags = flags | (size << 6) | limit_hi |
| 123 | |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 124 | return struct.pack(GDT_ENT_FMT, limit_lo, base_lo, base_mid, |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 125 | access, flags, base_hi) |
| 126 | |
| 127 | |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 128 | def create_tss_entry(base, limit, dpl): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 129 | """Create GDT TSS entry""" |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 130 | debug("create TSS entry: %x %x %x" % (base, limit, dpl)) |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 131 | present = 1 |
| 132 | |
| 133 | base_lo, base_mid, base_hi, limit_lo, limit_hi, = chop_base_limit(base, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 134 | limit) |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 135 | |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 136 | type_code = 0x9 # non-busy 32-bit TSS descriptor |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 137 | gran = 0 |
| 138 | |
| 139 | flags = (gran << 7) | limit_hi |
Daniel Leung | d8614af | 2024-03-15 11:35:03 -0700 | [diff] [blame] | 140 | type_byte = (present << 7) | (dpl << 5) | type_code |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 141 | |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 142 | return struct.pack(GDT_ENT_FMT, limit_lo, base_lo, base_mid, |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 143 | type_byte, flags, base_hi) |
| 144 | |
| 145 | |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 146 | def get_symbols(obj): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 147 | """Extract all symbols from ELF file object""" |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 148 | for section in obj.iter_sections(): |
| 149 | if isinstance(section, SymbolTableSection): |
| 150 | return {sym.name: sym.entry.st_value |
| 151 | for sym in section.iter_symbols()} |
| 152 | |
| 153 | raise LookupError("Could not find symbol table") |
| 154 | |
| 155 | |
| 156 | def parse_args(): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 157 | """Parse command line arguments""" |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 158 | global args |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 159 | parser = argparse.ArgumentParser( |
| 160 | description=__doc__, |
Jamie McCrae | ec70444 | 2023-01-04 16:08:36 +0000 | [diff] [blame] | 161 | formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 162 | |
| 163 | parser.add_argument("-k", "--kernel", required=True, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 164 | help="Zephyr kernel image") |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 165 | parser.add_argument("-v", "--verbose", action="store_true", |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 166 | help="Print extra debugging information") |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 167 | parser.add_argument("-o", "--output-gdt", required=True, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 168 | help="output GDT binary") |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 169 | args = parser.parse_args() |
Sebastian Bøe | 4971d2a | 2017-12-28 17:34:50 +0100 | [diff] [blame] | 170 | if "VERBOSE" in os.environ: |
| 171 | args.verbose = 1 |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 172 | |
| 173 | |
| 174 | def main(): |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 175 | """Main Program""" |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 176 | parse_args() |
| 177 | |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 178 | with open(args.kernel, "rb") as elf_fp: |
| 179 | kernel = ELFFile(elf_fp) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 180 | syms = get_symbols(kernel) |
| 181 | |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 182 | # NOTE: use-cases are extremely limited; we always have a basic flat |
| 183 | # code/data segments. If we are doing stack protection, we are going to |
| 184 | # have two TSS to manage the main task and the special task for double |
| 185 | # fault exception handling |
Andrew Boie | a705eae | 2017-11-03 10:39:08 -0700 | [diff] [blame] | 186 | if "CONFIG_USERSPACE" in syms: |
| 187 | num_entries = 7 |
Daniel Leung | ac58355 | 2024-03-07 16:15:18 -0800 | [diff] [blame] | 188 | elif "CONFIG_X86_STACK_PROTECTION" in syms: |
Andrew Boie | a705eae | 2017-11-03 10:39:08 -0700 | [diff] [blame] | 189 | num_entries = 5 |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 190 | else: |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 191 | num_entries = 3 |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 192 | |
Daniel Leung | 4b38392 | 2020-09-29 15:32:35 -0700 | [diff] [blame] | 193 | use_tls = False |
| 194 | if ("CONFIG_THREAD_LOCAL_STORAGE" in syms) and ("CONFIG_X86_64" not in syms): |
| 195 | use_tls = True |
| 196 | |
| 197 | # x86_64 does not use descriptor for thread local storage |
| 198 | num_entries += 1 |
| 199 | |
Daniel Leung | 9109fbb | 2021-03-07 21:48:48 -0800 | [diff] [blame] | 200 | gdt_base = syms["_gdt"] |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 201 | |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 202 | with open(args.output_gdt, "wb") as output_fp: |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 203 | # The pseudo descriptor is stuffed into the NULL descriptor |
| 204 | # since the CPU never looks at it |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 205 | output_fp.write(create_gdt_pseudo_desc(gdt_base, num_entries * 8)) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 206 | |
| 207 | # Selector 0x08: code descriptor |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 208 | output_fp.write(create_code_data_entry(0, 0xFFFFF, 0, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 209 | FLAGS_GRAN, ACCESS_EX | ACCESS_RW)) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 210 | |
| 211 | # Selector 0x10: data descriptor |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 212 | output_fp.write(create_code_data_entry(0, 0xFFFFF, 0, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 213 | FLAGS_GRAN, ACCESS_RW)) |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 214 | |
Andrew Boie | a705eae | 2017-11-03 10:39:08 -0700 | [diff] [blame] | 215 | if num_entries >= 5: |
Daniel Leung | 9109fbb | 2021-03-07 21:48:48 -0800 | [diff] [blame] | 216 | main_tss = syms["_main_tss"] |
| 217 | df_tss = syms["_df_tss"] |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 218 | |
| 219 | # Selector 0x18: main TSS |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 220 | output_fp.write(create_tss_entry(main_tss, 0x67, 0)) |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 221 | |
| 222 | # Selector 0x20: double-fault TSS |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 223 | output_fp.write(create_tss_entry(df_tss, 0x67, 0)) |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 224 | |
Daniel Leung | 4b38392 | 2020-09-29 15:32:35 -0700 | [diff] [blame] | 225 | if num_entries >= 7: |
Andrew Boie | a705eae | 2017-11-03 10:39:08 -0700 | [diff] [blame] | 226 | # Selector 0x28: code descriptor, dpl = 3 |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 227 | output_fp.write(create_code_data_entry(0, 0xFFFFF, 3, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 228 | FLAGS_GRAN, ACCESS_EX | ACCESS_RW)) |
Andrew Boie | 424e993 | 2017-08-30 14:06:30 -0700 | [diff] [blame] | 229 | |
Andrew Boie | a705eae | 2017-11-03 10:39:08 -0700 | [diff] [blame] | 230 | # Selector 0x30: data descriptor, dpl = 3 |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 231 | output_fp.write(create_code_data_entry(0, 0xFFFFF, 3, |
Anas Nashif | 7256553 | 2017-12-12 08:19:25 -0500 | [diff] [blame] | 232 | FLAGS_GRAN, ACCESS_RW)) |
Andrew Boie | 424e993 | 2017-08-30 14:06:30 -0700 | [diff] [blame] | 233 | |
Daniel Leung | 4b38392 | 2020-09-29 15:32:35 -0700 | [diff] [blame] | 234 | if use_tls: |
| 235 | # Selector 0x18, 0x28 or 0x38 (depending on entries above): |
| 236 | # data descriptor, dpl = 3 |
| 237 | # |
| 238 | # for use with thread local storage while this will be |
| 239 | # modified at runtime. |
Daniel Leung | 90722ad | 2021-03-03 14:37:57 -0800 | [diff] [blame] | 240 | output_fp.write(create_code_data_entry(0, 0xFFFFF, 3, |
Daniel Leung | 4b38392 | 2020-09-29 15:32:35 -0700 | [diff] [blame] | 241 | FLAGS_GRAN, ACCESS_RW)) |
| 242 | |
Andrew Boie | bc666ae | 2017-07-14 16:35:17 -0700 | [diff] [blame] | 243 | |
Andrew Boie | 08c2913 | 2017-07-14 15:29:17 -0700 | [diff] [blame] | 244 | if __name__ == "__main__": |
| 245 | main() |