[PATCH 11/17] x86/boot: Use fixed size of 16k for setup block

From: Ard Biesheuvel
Date: Fri Aug 18 2023 - 09:46:25 EST


The setup block contains the real mode startup code that is used when
booting from a legacy BIOS, along with the boot_params/setup_data that
is used by legacy x86 bootloaders to pass the command line and initial
ramdisk parameters, among other things.

The setup block also contains the PE/COFF header of the entire combined
image, which includes the compressed kernel image, the decompressor and
the EFI stub.

This PE header describes the layout of the executable image in memory,
and currently, the fact that the setup block precedes it makes it rather
fiddly to get the right values into the right place in the final image.

One complicating factor here is the variable setup block size, and given
that we will need to round up the setup block size to page size anyway
in a subsequent patch (in order to be able to use different permissions
for .text and .data), let's round it up to a fixed size of 16 KiB and be
done with it.

Note that Clang does not optimize for size as aggressively as GCC when
using the -Os option, but it supports -Oz for this purpose, so pass that
if the compiler supports it.

Signed-off-by: Ard Biesheuvel <ardb@xxxxxxxxxx>
---
arch/x86/boot/Makefile | 1 +
arch/x86/boot/header.S | 6 +++++-
arch/x86/boot/setup.ld | 1 +
arch/x86/boot/tools/build.c | 12 +++++-------
4 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 0e98bc5036994715..be1e8b94c93afa4a 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -69,6 +69,7 @@ KBUILD_CFLAGS := $(REALMODE_CFLAGS) -D_SETUP
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
+KBUILD_CFLAGS += $(call cc-option,-Oz)
GCOV_PROFILE := n
UBSAN_SANITIZE := n

diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index 72744ba440f6ea09..bef9265173757a5a 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -36,6 +36,10 @@ SYSSEG = 0x1000 /* historical load address >> 4 */
#define ROOT_RDONLY 1
#endif

+ /* The setup block has a fixed size: 32 * 512 == 16k */
+ .globl setup_size
+ .set setup_size, 0x4000
+
.code16
.section ".bstext", "ax"
#ifdef CONFIG_EFI_STUB
@@ -231,7 +235,7 @@ sentinel: .byte 0xff, 0xff /* Used to detect broken loaders */

.globl hdr
hdr:
-setup_sects: .byte 0 /* Filled in by build.c */
+setup_sects: .byte (setup_size / 512) - 1
root_flags: .word ROOT_RDONLY
syssize: .long 0 /* Filled in by build.c */
ram_size: .word 0 /* Obsolete */
diff --git a/arch/x86/boot/setup.ld b/arch/x86/boot/setup.ld
index a05dcaa4b74cd9f8..f1c14616cd80390d 100644
--- a/arch/x86/boot/setup.ld
+++ b/arch/x86/boot/setup.ld
@@ -57,6 +57,7 @@ SECTIONS
}

ASSERT(_end <= 0x8000, "Setup too big!")
+ ASSERT(__bss_start <= setup_size, "Setup image size too big!")
ASSERT(hdr == 0x1f1, "The setup header has the wrong offset!")
/* Necessary for the very-old-loader check to work... */
ASSERT(__end_init <= 5*512, "init sections too big!")
diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c
index 06949754316458ce..665ce7241542e475 100644
--- a/arch/x86/boot/tools/build.c
+++ b/arch/x86/boot/tools/build.c
@@ -40,12 +40,10 @@ typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;

-/* Minimal number of setup sectors */
-#define SETUP_SECT_MIN 5
-#define SETUP_SECT_MAX 64
+#define SETUP_SECT_NUM 32

/* This must be large enough to hold the entire setup */
-u8 buf[SETUP_SECT_MAX*512];
+u8 buf[(SETUP_SECT_NUM+1)*512];

#define PECOFF_RELOC_RESERVE 0x20

@@ -360,8 +358,9 @@ int main(int argc, char ** argv)

/* Pad unused space with zeros */
setup_sectors = (c + 511) / 512;
- if (setup_sectors < SETUP_SECT_MIN)
- setup_sectors = SETUP_SECT_MIN;
+ if (setup_sectors > SETUP_SECT_NUM)
+ die("setup size exceeds maximum");
+ setup_sectors = SETUP_SECT_NUM;
i = setup_sectors*512;
memset(buf+c, 0, i-c);

@@ -388,7 +387,6 @@ int main(int argc, char ** argv)
#endif

/* Patch the setup code with the appropriate size parameters */
- buf[0x1f1] = setup_sectors-1;
put_unaligned_le32(sys_size, &buf[0x1f4]);

update_pecoff_text(setup_sectors * 512, i + (sys_size * 16));
--
2.39.2