1#!/usr/bin/env python3 2# Copyright 2023 The ChromiumOS Authors 3# SPDX-License-Identifier: Apache-2.0 4import sys 5import re 6 7# Xtensa Vector Table linker generator 8# 9# Takes a pre-processed (gcc -dM) core-isa.h file as its first 10# argument, and emits a GNU linker section declartion which will 11# correctly load the exception vectors and literals as long as their 12# code is declared using standard conventions (see below). 13# 14# The section name will be ".z_xtensa_vectors", and a symbol 15# "z_xtensa_vecbase" is emitted containing a valid value for the 16# VECBASE SR at runtime. 17# 18# Obviously, this requires that XCHAL_HAVE_VECBASE=1. A similar trick 19# could be played to load vectors at fixed addresses on hardware that 20# lacks VECBASE, but the core-isa.h interface is inexplicably 21# different. 22# 23# Because the "standard conventions" (which descend from somewhere in 24# Cadence) are not documented anywhere and just end up cut and pasted 25# between devices, here's an attempt at a specification: 26# 27# + The six register window exception vectors are defined with offsets 28# internal to their assembly code. They are linked in a single 29# section named ".WindowVectors.text". 30# 31# + The "kernel", "user" and "double exception" vectors are emitted in 32# sections named ".KernelExceptionVector.text", 33# "UserExceptionVector.text" and "DoubleExceptionVector.text" 34# respectively. 35# 36# + XEA2 interrupt vectors are in sections named 37# ".Level<n>InterruptVector.text", except (!) for ones which are 38# given special names. The "debug" and "NMI" interrupts (if they 39# exist) are technically implemented as standard interrupt vectors 40# (of a platform-dependent level), but the code for them is emitted 41# in ".DebugExceptionVector.text" and ".NMIExceptionVector.text", 42# and not a section corresponding to their interrupt level. 43# 44# + Any unused bytes at the end of a vector are made available as 45# storage for immediate values used by the following vector (Xtensa 46# can only back-reference immediates for MOVI/L32R instructions) as 47# a "<name>Vector.literal" section. Note that there is no guarantee 48# of how much space is available, it depends on the previous 49# vector's code size. Zephyr code has historically not used this 50# space, as support in existing linker scripts is inconsistent. But 51# it's exposed here. 52 53coreisa = sys.argv[1] 54 55debug_level = 0 56 57# Translation for the core-isa.h vs. linker section naming conventions 58sect_names = { "DOUBLEEXC" : "DoubleException", 59 "KERNEL" : "KernelException", 60 "NMI" : "NMIException", 61 "USER" : "UserException" } 62 63offsets = {} 64 65with open(coreisa) as infile: 66 for line in infile.readlines(): 67 m = re.match(r"^#define\s+XCHAL_([^ ]+)_VECOFS\s*(.*)", line.rstrip()) 68 if m: 69 (sym, val) = (m.group(1), m.group(2)) 70 if sym == "WINDOW_OF4": 71 # This must be the start of the section 72 assert eval(val) == 0 73 elif sym.startswith("WINDOW"): 74 # Ignore the other window exceptions, they're internally sorted 75 pass 76 elif sym == "RESET": 77 # Ignore, not actually part of the vector table 78 pass 79 elif sym == "DEBUG": 80 # This one is a recursive macro that doesn't expand, 81 # so handle manually 82 m = re.match(r"XCHAL_INTLEVEL(\d+)_VECOFS", val) 83 if not m: 84 print(f"no intlevel match for debug val {val}") 85 assert m 86 debug_level = eval(m.group(1)) 87 else: 88 if val == "XCHAL_NMI_VECOFS": 89 # This gets recursively defined in the other 90 # direction, so ignore the INTLEVEL 91 pass 92 else: 93 addr = eval(val) 94 m = re.match(r"^INTLEVEL(\d+)", sym) 95 if m: 96 offsets[f"Level{m.group(1)}Interrupt"] = addr 97 else: 98 offsets[sect_names[sym]] = addr 99 100if debug_level > 0: 101 old = f"Level{debug_level}Interrupt" 102 offsets[f"DebugException"] = offsets[old] 103 del offsets[old] 104 105sects = list(offsets) 106sects.sort(key=lambda s: offsets[s]) 107 108print("/* Automatically Generated Code - Do Not Edit */") 109print("/* See arch/xtensa/core/gen_vector.py */") 110print("") 111 112# The 1k alignment is experimental, the docs on the Relocatable Vector 113# Option doesn't specify an alignment at all, but writes to the 114# bottom bits don't take... 115print( " .z_xtensa_vectors : ALIGN(1024) {") 116print( " z_xtensa_vecbase = .;") 117print(f" KEEP(*(.WindowVectors.text));") 118for s in sects: 119 print(f" KEEP(*(.{s}Vector.literal));") 120 print( " . = 0x%3.3x;" % (offsets[s])) 121 print(f" KEEP(*(.{s}Vector.text));") 122print(" }") 123