blob: 5794a965a028687bb138138731aa15b2e47ed84a [file] [log] [blame]
Andrew Boie08c29132017-07-14 15:29:17 -07001#!/usr/bin/env python3
2#
3# Copyright (c) 2017 Intel Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6
Andrew Boieb8cbf212019-02-24 11:36:31 -08007"""Generate a Global Descriptor Table (GDT) for x86 CPUs.
8
9For additional detail on GDT and x86 memory management, please
10consult the IA Architecture SW Developer Manual, vol. 3.
11
12This script accepts as input the zephyr_prebuilt.elf binary,
13which is a link of the Zephyr kernel without various build-time
14generated data structures (such as the GDT) inserted into it.
15This kernel image has been properly padded such that inserting
16these data structures will not disturb the memory addresses of
17other symbols.
18
Andrew Boie98bcc512020-07-17 19:02:30 -070019The input kernel ELF binary is used to obtain the following
20information:
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 Boieb8cbf212019-02-24 11:36:31 -080028The output is a GDT whose contents depend on the kernel
29configuration. With no memory protection features enabled,
30we generate flat 32-bit code and data segments. If hardware-
31based stack overflow protection or userspace is enabled,
32we additionally create descriptors for the main and double-
33fault IA tasks, needed for userspace privilege elevation and
34double-fault handling. If userspace is enabled, we also create
35flat code/data segments for ring 3 execution.
36"""
37
Andrew Boie08c29132017-07-14 15:29:17 -070038import argparse
39import sys
40import struct
41import os
Daniel Leung90722ad2021-03-03 14:37:57 -080042
Julien Massot36f116b2021-11-19 16:30:43 +010043from packaging import version
Daniel Leung90722ad2021-03-03 14:37:57 -080044
45import elftools
Andrew Boie08c29132017-07-14 15:29:17 -070046from elftools.elf.elffile import ELFFile
47from elftools.elf.sections import SymbolTableSection
48
Daniel Leung90722ad2021-03-03 14:37:57 -080049
Julien Massot36f116b2021-11-19 16:30:43 +010050if version.parse(elftools.__version__) < version.parse('0.24'):
Ulf Magnusson50b9b122019-09-07 14:41:01 +020051 sys.exit("pyelftools is out of date, need version 0.24 or later")
Andrew Boie3aecba12017-07-25 09:44:30 -070052
Anas Nashif72565532017-12-12 08:19:25 -050053
Andrew Boiebc666ae2017-07-14 16:35:17 -070054def debug(text):
Daniel Leung90722ad2021-03-03 14:37:57 -080055 """Display debug message if --verbose"""
56 if args.verbose:
57 sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
Andrew Boiebc666ae2017-07-14 16:35:17 -070058
Anas Nashif72565532017-12-12 08:19:25 -050059
Andrew Boiebc666ae2017-07-14 16:35:17 -070060def error(text):
Daniel Leung90722ad2021-03-03 14:37:57 -080061 """Exit program with an error message"""
Ulf Magnusson50b9b122019-09-07 14:41:01 +020062 sys.exit(os.path.basename(sys.argv[0]) + ": " + text)
Andrew Boiebc666ae2017-07-14 16:35:17 -070063
64
Daniel Leung90722ad2021-03-03 14:37:57 -080065GDT_PD_FMT = "<HIH"
Andrew Boie08c29132017-07-14 15:29:17 -070066
Anas Nashif72565532017-12-12 08:19:25 -050067FLAGS_GRAN = 1 << 7 # page granularity
Andrew Boie08c29132017-07-14 15:29:17 -070068ACCESS_EX = 1 << 3 # executable
69ACCESS_DC = 1 << 2 # direction/conforming
70ACCESS_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 Nashif72565532017-12-12 08:19:25 -050074
75
Andrew Boie08c29132017-07-14 15:29:17 -070076def create_gdt_pseudo_desc(addr, size):
Daniel Leung90722ad2021-03-03 14:37:57 -080077 """Create pseudo GDT descriptor"""
Nazar Kazakov9713f0d2022-02-24 12:00:55 +000078 debug("create pseudo descriptor: %x %x" % (addr, size))
Andrew Boie08c29132017-07-14 15:29:17 -070079 # ...and take back one byte for the Intel god whose Ark this is...
80 size = size - 1
Daniel Leung90722ad2021-03-03 14:37:57 -080081 return struct.pack(GDT_PD_FMT, size, addr, 0)
Andrew Boie08c29132017-07-14 15:29:17 -070082
83
Andrew Boie08c29132017-07-14 15:29:17 -070084def chop_base_limit(base, limit):
Daniel Leung90722ad2021-03-03 14:37:57 -080085 """Limit argument always in bytes"""
Andrew Boie08c29132017-07-14 15:29:17 -070086 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 Leung90722ad2021-03-03 14:37:57 -080096GDT_ENT_FMT = "<HHBBBB"
Andrew Boie08c29132017-07-14 15:29:17 -070097
Anas Nashif72565532017-12-12 08:19:25 -050098
Andrew Boie08c29132017-07-14 15:29:17 -070099def create_code_data_entry(base, limit, dpl, flags, access):
Daniel Leung90722ad2021-03-03 14:37:57 -0800100 """Create GDT entry for code or data"""
Andrew Boiea705eae2017-11-03 10:39:08 -0700101 debug("create code or data entry: %x %x %x %x %x" %
Anas Nashif72565532017-12-12 08:19:25 -0500102 (base, limit, dpl, flags, access))
Andrew Boiea705eae2017-11-03 10:39:08 -0700103
Andrew Boie08c29132017-07-14 15:29:17 -0700104 base_lo, base_mid, base_hi, limit_lo, limit_hi = chop_base_limit(base,
Anas Nashif72565532017-12-12 08:19:25 -0500105 limit)
Andrew Boie08c29132017-07-14 15:29:17 -0700106
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 Boiea0da6322017-08-01 09:30:09 -0700121 access = access | (present << 7) | (dpl << 5) | (desc_type << 4) | accessed
Andrew Boie08c29132017-07-14 15:29:17 -0700122 flags = flags | (size << 6) | limit_hi
123
Daniel Leung90722ad2021-03-03 14:37:57 -0800124 return struct.pack(GDT_ENT_FMT, limit_lo, base_lo, base_mid,
Andrew Boie08c29132017-07-14 15:29:17 -0700125 access, flags, base_hi)
126
127
Andrew Boiebc666ae2017-07-14 16:35:17 -0700128def create_tss_entry(base, limit, dpl):
Daniel Leung90722ad2021-03-03 14:37:57 -0800129 """Create GDT TSS entry"""
Anas Nashif72565532017-12-12 08:19:25 -0500130 debug("create TSS entry: %x %x %x" % (base, limit, dpl))
Andrew Boiebc666ae2017-07-14 16:35:17 -0700131 present = 1
132
133 base_lo, base_mid, base_hi, limit_lo, limit_hi, = chop_base_limit(base,
Anas Nashif72565532017-12-12 08:19:25 -0500134 limit)
Andrew Boiebc666ae2017-07-14 16:35:17 -0700135
Anas Nashif72565532017-12-12 08:19:25 -0500136 type_code = 0x9 # non-busy 32-bit TSS descriptor
Andrew Boiebc666ae2017-07-14 16:35:17 -0700137 gran = 0
138
139 flags = (gran << 7) | limit_hi
Daniel Leungd8614af2024-03-15 11:35:03 -0700140 type_byte = (present << 7) | (dpl << 5) | type_code
Andrew Boiebc666ae2017-07-14 16:35:17 -0700141
Daniel Leung90722ad2021-03-03 14:37:57 -0800142 return struct.pack(GDT_ENT_FMT, limit_lo, base_lo, base_mid,
Andrew Boiebc666ae2017-07-14 16:35:17 -0700143 type_byte, flags, base_hi)
144
145
Andrew Boie08c29132017-07-14 15:29:17 -0700146def get_symbols(obj):
Daniel Leung90722ad2021-03-03 14:37:57 -0800147 """Extract all symbols from ELF file object"""
Andrew Boie08c29132017-07-14 15:29:17 -0700148 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
156def parse_args():
Daniel Leung90722ad2021-03-03 14:37:57 -0800157 """Parse command line arguments"""
Andrew Boie08c29132017-07-14 15:29:17 -0700158 global args
Anas Nashif72565532017-12-12 08:19:25 -0500159 parser = argparse.ArgumentParser(
160 description=__doc__,
Jamie McCraeec704442023-01-04 16:08:36 +0000161 formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
Andrew Boie08c29132017-07-14 15:29:17 -0700162
163 parser.add_argument("-k", "--kernel", required=True,
Anas Nashif72565532017-12-12 08:19:25 -0500164 help="Zephyr kernel image")
Andrew Boie08c29132017-07-14 15:29:17 -0700165 parser.add_argument("-v", "--verbose", action="store_true",
Anas Nashif72565532017-12-12 08:19:25 -0500166 help="Print extra debugging information")
Andrew Boie08c29132017-07-14 15:29:17 -0700167 parser.add_argument("-o", "--output-gdt", required=True,
Anas Nashif72565532017-12-12 08:19:25 -0500168 help="output GDT binary")
Andrew Boie08c29132017-07-14 15:29:17 -0700169 args = parser.parse_args()
Sebastian Bøe4971d2a2017-12-28 17:34:50 +0100170 if "VERBOSE" in os.environ:
171 args.verbose = 1
Andrew Boie08c29132017-07-14 15:29:17 -0700172
173
174def main():
Daniel Leung90722ad2021-03-03 14:37:57 -0800175 """Main Program"""
Andrew Boie08c29132017-07-14 15:29:17 -0700176 parse_args()
177
Daniel Leung90722ad2021-03-03 14:37:57 -0800178 with open(args.kernel, "rb") as elf_fp:
179 kernel = ELFFile(elf_fp)
Andrew Boie08c29132017-07-14 15:29:17 -0700180 syms = get_symbols(kernel)
181
Andrew Boiebc666ae2017-07-14 16:35:17 -0700182 # 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 Boiea705eae2017-11-03 10:39:08 -0700186 if "CONFIG_USERSPACE" in syms:
187 num_entries = 7
Daniel Leungac583552024-03-07 16:15:18 -0800188 elif "CONFIG_X86_STACK_PROTECTION" in syms:
Andrew Boiea705eae2017-11-03 10:39:08 -0700189 num_entries = 5
Andrew Boiebc666ae2017-07-14 16:35:17 -0700190 else:
Andrew Boiebc666ae2017-07-14 16:35:17 -0700191 num_entries = 3
Andrew Boie08c29132017-07-14 15:29:17 -0700192
Daniel Leung4b383922020-09-29 15:32:35 -0700193 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 Leung9109fbb2021-03-07 21:48:48 -0800200 gdt_base = syms["_gdt"]
Andrew Boie08c29132017-07-14 15:29:17 -0700201
Daniel Leung90722ad2021-03-03 14:37:57 -0800202 with open(args.output_gdt, "wb") as output_fp:
Andrew Boie08c29132017-07-14 15:29:17 -0700203 # The pseudo descriptor is stuffed into the NULL descriptor
204 # since the CPU never looks at it
Daniel Leung90722ad2021-03-03 14:37:57 -0800205 output_fp.write(create_gdt_pseudo_desc(gdt_base, num_entries * 8))
Andrew Boie08c29132017-07-14 15:29:17 -0700206
207 # Selector 0x08: code descriptor
Daniel Leung90722ad2021-03-03 14:37:57 -0800208 output_fp.write(create_code_data_entry(0, 0xFFFFF, 0,
Anas Nashif72565532017-12-12 08:19:25 -0500209 FLAGS_GRAN, ACCESS_EX | ACCESS_RW))
Andrew Boie08c29132017-07-14 15:29:17 -0700210
211 # Selector 0x10: data descriptor
Daniel Leung90722ad2021-03-03 14:37:57 -0800212 output_fp.write(create_code_data_entry(0, 0xFFFFF, 0,
Anas Nashif72565532017-12-12 08:19:25 -0500213 FLAGS_GRAN, ACCESS_RW))
Andrew Boie08c29132017-07-14 15:29:17 -0700214
Andrew Boiea705eae2017-11-03 10:39:08 -0700215 if num_entries >= 5:
Daniel Leung9109fbb2021-03-07 21:48:48 -0800216 main_tss = syms["_main_tss"]
217 df_tss = syms["_df_tss"]
Andrew Boiebc666ae2017-07-14 16:35:17 -0700218
219 # Selector 0x18: main TSS
Daniel Leung90722ad2021-03-03 14:37:57 -0800220 output_fp.write(create_tss_entry(main_tss, 0x67, 0))
Andrew Boiebc666ae2017-07-14 16:35:17 -0700221
222 # Selector 0x20: double-fault TSS
Daniel Leung90722ad2021-03-03 14:37:57 -0800223 output_fp.write(create_tss_entry(df_tss, 0x67, 0))
Andrew Boiebc666ae2017-07-14 16:35:17 -0700224
Daniel Leung4b383922020-09-29 15:32:35 -0700225 if num_entries >= 7:
Andrew Boiea705eae2017-11-03 10:39:08 -0700226 # Selector 0x28: code descriptor, dpl = 3
Daniel Leung90722ad2021-03-03 14:37:57 -0800227 output_fp.write(create_code_data_entry(0, 0xFFFFF, 3,
Anas Nashif72565532017-12-12 08:19:25 -0500228 FLAGS_GRAN, ACCESS_EX | ACCESS_RW))
Andrew Boie424e9932017-08-30 14:06:30 -0700229
Andrew Boiea705eae2017-11-03 10:39:08 -0700230 # Selector 0x30: data descriptor, dpl = 3
Daniel Leung90722ad2021-03-03 14:37:57 -0800231 output_fp.write(create_code_data_entry(0, 0xFFFFF, 3,
Anas Nashif72565532017-12-12 08:19:25 -0500232 FLAGS_GRAN, ACCESS_RW))
Andrew Boie424e9932017-08-30 14:06:30 -0700233
Daniel Leung4b383922020-09-29 15:32:35 -0700234 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 Leung90722ad2021-03-03 14:37:57 -0800240 output_fp.write(create_code_data_entry(0, 0xFFFFF, 3,
Daniel Leung4b383922020-09-29 15:32:35 -0700241 FLAGS_GRAN, ACCESS_RW))
242
Andrew Boiebc666ae2017-07-14 16:35:17 -0700243
Andrew Boie08c29132017-07-14 15:29:17 -0700244if __name__ == "__main__":
245 main()