Complete and checksum EFI_COMPATIBILITY16_TABLE at build time

GCC, for reasons unknown, will refuse to compile code such as
   extern int bar;
   uint16_t foo = &bar;

The assembler would happily emit a R_386_16 relocation for this if asked
nicely, and all would be well. But instead, GCC complains about the
initialiser not being constant.

So we tend to fill in 16-bit offsets at run-time, which is only
moderately inefficient. But for the CSM table used by EFI, it doesn't
work. We need the offset to be present in the *image*, before a line of
our own code has been run. Likewise the checksum.

This special-cases the table and entry point in rather than
attempting to do something generic. I did have a functional generic
implementation which could be invoked from csm.c along the lines of
 CHECKSUM(csm_compat_table, TableChecksum)
... and which would emit the required offsetof(typeof(\1), \2) into
a special data section which was elided from the final build but parsed
by 'objdump -s' and the location of the table to be checksummed was
inferred from the name of the *variable* that got put into that special
section... seriously, it's better just to special-case it.

It was baroque enough just for the checksums, and filling in the entry
point which required access to *two* symbols was probably going to involve
emitting a *string* into that special build-data section. It had to die.

So yes, we have hard-coded symbol names, and even magic numbers in the
python script for table offsets etc., but that's because this is an ABI.
It doesn't change, and neither do any of the other tables that we might
now consider filling at build time just to avoid having to spend time
on doing so at runtime.

Signed-off-by: David Woodhouse <>
diff --git a/tools/ b/tools/
index 69d65e8..f141f8b 100755
--- a/tools/
+++ b/tools/
@@ -8,6 +8,17 @@
 import sys
 import layoutrom
+def subst(data, offset, new):
+    return data[:offset] + new + data[offset + len(new):]
+def checksum(data, start, size, csum):
+    sumbyte = 0
+    while size:
+        sumbyte = sumbyte + ord(data[start + size - 1])
+        size = size - 1
+    sumbyte = (0x100 - sumbyte) & 0xff
+    return subst(data, start+csum, chr(sumbyte))
 def main():
     # Get args
     objinfo, rawfile, outfile = sys.argv[1:]
@@ -45,6 +56,22 @@
             datasize, expdatasize)
+    # Fix up CSM Compatibility16 table
+    if 'csm_compat_table' in symbols and 'entry_csm' in symbols:
+        # Field offsets within EFI_COMPATIBILITY16_TABLE
+        ENTRY_FIELD_OFS = 14 # Compatibility16CallOffset (UINT16)
+        SIZE_FIELD_OFS = 5   # TableLength (UINT8)
+        CSUM_FIELD_OFS = 4   # TableChecksum (UINT8)
+        tableofs = symbols['csm_compat_table'].offset - symbols['code32flat_start'].offset
+        entry_addr = symbols['entry_csm'].offset - layoutrom.BUILD_BIOS_ADDR
+        byte1 = chr(entry_addr & 0xff)
+        byte2 = chr(entry_addr >> 8)
+        rawdata = subst(rawdata, tableofs+ENTRY_FIELD_OFS, byte1+byte2)
+        tablesize = ord(rawdata[tableofs+SIZE_FIELD_OFS])
+        rawdata = checksum(rawdata, tableofs, tablesize, CSUM_FIELD_OFS)
     # Print statistics
     runtimesize = datasize
     if '_reloc_abs_start' in symbols: