From 6825645a21a3221629e7d5aa9d5cfbf6e3bc4188 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 15 Apr 2019 18:08:44 +0200 Subject: [PATCH] zipl: Move stage 3 loader to external file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first component that gets control after IPL is the zIPL-provided stage 3 boot loader. This boot loader is based on a binary image linked into the zIPL executable. When preparing a disk for IPL, zIPL updates runtime parameters in the boot loader image and writes the resulting data to disk. To support the Secure Boot firmware feature, the stage 3 code must be signed, and can therefore no longer contain variable parameter portions. Also the boot loader image should be accessible as a separate file to enable the use of external programs for creating the signature. This patch moves the stage 3 boot loader code to an external file location and splits out the parameter portion into a separate, unsigned on-disk component. The new memory layout of the stage 3 loader during its execution looks as follows: * 0x0000-0x1fff Lowcore * 0x2000-0x5fff Memory allocation (heap) * 0x7000-0x8fff free * 0x9000-0x9fff Stage3 parameter * 0xa000-0xcfff Stage3 code * 0xd000-0xefff Section: bss, rodata, data * 0xf000-0xffff Stack Signed-off-by: Stefan Haberland Reviewed-by: Peter Oberparleiter Signed-off-by: Jan Höppner --- zipl/boot/Makefile | 10 ++++----- zipl/boot/stage2.lds | 5 +++-- zipl/boot/stage3.c | 35 ----------------------------- zipl/boot/stage3.lds | 39 +++++++++++++++++++-------------- zipl/include/boot.h | 9 ++++---- zipl/include/zipl.h | 4 +++- zipl/src/Makefile | 7 +++++- zipl/src/boot.c | 32 +++++++++++++-------------- zipl/src/bootmap.c | 52 +++++++++++++++++++++++++++++--------------- 9 files changed, 93 insertions(+), 100 deletions(-) diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile index 7a3571e9..8d8cef80 100644 --- a/zipl/boot/Makefile +++ b/zipl/boot/Makefile @@ -13,10 +13,9 @@ FILES = fba0.bin fba1b.bin fba2.bin \ eckd0_ldl.bin eckd0_cdl.bin \ eckd1.bin eckd1b.bin eckd2.bin \ tape0.bin \ - eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin \ - stage3.bin + eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin -all: data.o data.h tape0.bin +all: data.o data.h tape0.bin stage3.bin # Prevent make from using some default rules... %: %.S @@ -44,7 +43,7 @@ eckd2.exec: head.o stage2.o cio.o eckd2.o libc.o menu.o sclp.o \ fba2.exec: head.o stage2.o cio.o fba2.o libc.o menu.o sclp.o \ kdump2.o kdump.o entry.o stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \ - kdump.o entry.o + kdump.o entry.o stage3.lds %.exec: %.o @STAGE=$$( \ @@ -87,6 +86,7 @@ data.h: data.o echo "extern char $$SYMBOL;" >>data.h; done clean: - rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy + rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy \ + stage3.bin .PHONY: all clean diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds index befe8e2d..41efa79d 100644 --- a/zipl/boot/stage2.lds +++ b/zipl/boot/stage2.lds @@ -9,7 +9,8 @@ * 0x2000-0x4fff Sections (load): head, text, data, rodata, rodata.str * 0x5000-0x51ff eckd2dump_mv parameter block (426 bytes) * 0x5200-0x5fff Sections: bss - * 0x6000-0x9fff Memory allocation (heap) + * 0x6000-0x8fff Memory allocation (heap) + * 0x9000-0x9fff Memory to load stage3 parameter to * 0xa000-0xdfff Memory to load stage3 to * 0xe000-0xffff Stack * @@ -52,7 +53,7 @@ SECTIONS . = 0x6000; __heap_start = .; - . = 0xa000; + . = 0x9000; __heap_stop = .; . = 0xf000; diff --git a/zipl/boot/stage3.c b/zipl/boot/stage3.c index b803b0b9..dda17338 100644 --- a/zipl/boot/stage3.c +++ b/zipl/boot/stage3.c @@ -13,41 +13,6 @@ #include "s390.h" #include "stage3.h" -/* - * 64 Byte dummy space for external symbols - * _parm_addr; address of parmline - * _initrd_addr; address of initrd - * _initrd_len; length of initrd - * _load_psw; load psw of kernel - * _extra_parm; use extra parm line mechanism? - * stage3_flags; flags (e.g. STAGE3_FLAG_KDUMP) - * _image_len; length of kernel - * _image_addr; target address of kernel - * - * needed to blow up the binary and leave room - */ -__attribute__ ((section(".text.dummy"))) void _dummy(void) -{ - asm volatile( - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ".long 0x00000000\n" - ); -} - static unsigned char ebc_037[256] = { /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, diff --git a/zipl/boot/stage3.lds b/zipl/boot/stage3.lds index 7beafb03..c67bc320 100644 --- a/zipl/boot/stage3.lds +++ b/zipl/boot/stage3.lds @@ -1,38 +1,47 @@ /* * Memory layout for stage 3 + * ========================= + * + * General memory layout + * --------------------- + * + * 0x0000-0x1fff Lowcore + * 0x2000-0x5fff Memory allocation (heap) + * 0x6000-0x8fff free + * 0x9000-0x9fff Stage3 parameter + * 0xa000-0xdfff Stage3 code + data + * 0xe000-0xffff Stack */ SECTIONS { . = 0x0; - . = 0x7000; + . = 0x2000; __heap_start = .; - . = 0xa000; + . = 0x6000; __heap_stop = .; - . = 0xa000; + + /* stage 3 parameter */ + . = 0x9000; _parm_addr = .; - . = 0xa008; + . = 0x9008; _initrd_addr = .; - . = 0xa010; + . = 0x9010; _initrd_len = .; - . = 0xa018; + . = 0x9018; _load_psw = .; - . = 0xa020; + . = 0x9020; _extra_parm = .; - . = 0xa028; + . = 0x9028; stage3_flags =.; - . = 0xa030; + . = 0x9030; _image_len = .; - . = 0xa038; + . = 0x9038; _image_addr = .; - . = 0xa000; - .text.dummy : { *(.text.dummy) } - - . = 0xa050; .text.start : { *(.text.start) } .text : { *(.text) } __ex_table_start = .; @@ -40,11 +49,9 @@ SECTIONS __ex_table_stop = .; .eh_frame : { *(.eh_frame) } - . = 0xc000; __bss_start = .; .bss : { *(.bss) } __bss_stop = .; .rodata : {*(.rodata) } .data : { *(.data) } - } diff --git a/zipl/include/boot.h b/zipl/include/boot.h index 8412a65b..6bd2a9bf 100644 --- a/zipl/include/boot.h +++ b/zipl/include/boot.h @@ -308,11 +308,10 @@ int boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b, disk_blockptr_t *stage2_list, blocknum_t stage2_count); int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job); -size_t get_stage3_size(); -int boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, - address_t initrd_addr, size_t initrd_len, - address_t image_addr, int extra_parm, uint16_t flags, - size_t image_len); +int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr, + address_t initrd_addr, size_t initrd_len, + address_t load_addr, int extra_parm, uint16_t flags, + size_t image_len); int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr, address_t initrd_addr, address_t image_addr); int boot_get_tape_dump(void** data, size_t* size, uint64_t mem); diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h index 5cd7cb4e..9b2dadb6 100644 --- a/zipl/include/zipl.h +++ b/zipl/include/zipl.h @@ -20,10 +20,11 @@ #define DISK_LAYOUT_ID 0x00000001 #define ZIPL_STAGE2_LOAD_ADDRESS 0x2000 -#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa050LL +#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa000LL #define DEFAULT_IMAGE_ADDRESS 0x10000LL #define KDUMP_IMAGE_ADDRESS 0x10010LL #define DEFAULT_STAGE3_ADDRESS 0xa000LL +#define DEFAULT_STAGE3_PARAMS_ADDRESS 0x9000LL #define MINIMUM_ADDRESS 0x10000LL #define ADDRESS_LIMIT 0x80000000LL #define ADDRESS_LIMIT_KDUMP 0x2000000UL /* HSA size: 32 MiB */ @@ -43,6 +44,7 @@ #define ZIPL_CONF_VAR "ZIPLCONF" #define ZIPL_DEFAULT_CONF "/etc/zipl.conf" #define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries" +#define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin" #define MENU_DEFAULT_PROMPT 0 #define MENU_DEFAULT_TIMEOUT 0 diff --git a/zipl/src/Makefile b/zipl/src/Makefile index 1f39d069..eae081bc 100644 --- a/zipl/src/Makefile +++ b/zipl/src/Makefile @@ -15,8 +15,9 @@ objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \ zipl_helpers = $(basename $(wildcard zipl_helper.*.c)) chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers)) +zipl_stage3 = ../boot/stage3.bin -all: zipl $(chreipl_helpers) +all: zipl $(chreipl_helpers) $(zipl_stage3) zipl: $(objects) $(libs) @@ -33,6 +34,7 @@ install: all $(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \ $(DESTDIR)$(TOOLS_LIBDIR) $(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR) + $(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR) clean: rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl @@ -48,3 +50,6 @@ clean: ../boot/data.o: make -C ../boot data.o + +../boot/stage3.bin: + make -C ../boot stage3.bin diff --git a/zipl/src/boot.c b/zipl/src/boot.c index f4290fe2..663d1d31 100644 --- a/zipl/src/boot.c +++ b/zipl/src/boot.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "../boot/data.h" #include "boot.h" @@ -68,20 +70,17 @@ boot_check_data(void) return 0; } -/* Export stage 3 size for partition dump with dump kernel */ -size_t -get_stage3_size() -{ - return DATA_SIZE(stage3); -} - -/* Create a stage 3 loader in memory. +/* + * Create a stage 3 parameter block in memory. * Upon success, return 0 and set BUFFER to point to the data buffer and set - * BYTECOUNT to contain the loader size in bytes. Return non-zero otherwise. */ + * BYTECOUNT to contain the parameter block size in bytes. + * Return non-zero otherwise. + */ int -boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, - address_t initrd_addr, size_t initrd_len, address_t image_addr, - int extra_parm, uint16_t flags, size_t image_len) +boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr, + address_t initrd_addr, size_t initrd_len, + address_t image_addr, int extra_parm, uint16_t flags, + size_t image_len) { struct boot_stage3_params params; void* data; @@ -92,10 +91,10 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, return -1; } /* Get memory */ - data = misc_malloc(DATA_SIZE(stage3)); + data = misc_malloc(sizeof(params)); if (data == NULL) return -1; - memset(data, 0, DATA_SIZE(stage3)); + memset(data, 0, sizeof(params)); /* Prepare params section */ params.parm_addr = (uint64_t) parm_addr; params.initrd_addr = (uint64_t) initrd_addr; @@ -106,10 +105,9 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, params.image_len = (uint64_t) image_len; params.image_addr = (uint64_t) image_addr; /* Initialize buffer */ - memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3)); - memcpy(data, ¶ms, sizeof(struct boot_stage3_params)); + memcpy(data, ¶ms, sizeof(params)); *buffer = data; - *bytecount = DATA_SIZE(stage3); + *bytecount = sizeof(params); return 0; } diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c index 1243b0e0..f7c70e3f 100644 --- a/zipl/src/bootmap.c +++ b/zipl/src/bootmap.c @@ -416,11 +416,12 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, { struct stat stats; void* table; - void* stage3; - size_t stage3_size; - const char *comp_name[4] = {"kernel image", "parmline", - "initial ramdisk", "internal loader"}; - struct component_loc comp_loc[4]; + void *stage3_params; + size_t stage3_params_size; + const char *comp_name[5] = {"kernel image", "parmline", + "initial ramdisk", "internal loader", + "parameters"}; + struct component_loc comp_loc[5]; int rc; int offset, flags = 0; size_t ramdisk_size, image_size; @@ -473,22 +474,35 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, image_size = stats.st_size; /* Add stage 3 loader to bootmap */ - rc = boot_get_stage3(&stage3, &stage3_size, ipl->parm_addr, - ipl->ramdisk_addr, ramdisk_size, - ipl->is_kdump ? ipl->image_addr + 0x10 : - ipl->image_addr, - (info->type == disk_type_scsi) ? 0 : 1, - flags, image_size); + rc = add_component_file(fd, ZIPL_STAGE3_PATH, DEFAULT_STAGE3_ADDRESS, 0, + VOID_ADD(table, offset), 1, info, target, + &comp_loc[3]); + if (rc) { + error_text("Could not add internal loader file '%s'", + ZIPL_STAGE3_PATH); + free(table); + return rc; + } + offset += sizeof(struct component_entry); + + /* Add stage 3 parameter to bootmap */ + rc = boot_get_stage3_parms(&stage3_params, &stage3_params_size, + ipl->parm_addr, ipl->ramdisk_addr, + ramdisk_size, + ipl->is_kdump ? ipl->image_addr + 0x10 : + ipl->image_addr, + (info->type == disk_type_scsi) ? 0 : 1, + flags, image_size); if (rc) { free(table); return rc; } - rc = add_component_buffer(fd, stage3, stage3_size, - DEFAULT_STAGE3_ADDRESS, - VOID_ADD(table, offset), info, &comp_loc[3]); - free(stage3); + rc = add_component_buffer(fd, stage3_params, stage3_params_size, + DEFAULT_STAGE3_PARAMS_ADDRESS, + VOID_ADD(table, offset), info, &comp_loc[4]); + free(stage3_params); if (rc) { - error_text("Could not add stage 3 boot loader"); + error_text("Could not add parameters"); free(table); return -1; } @@ -540,7 +554,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, offset += sizeof(struct component_entry); } if (verbose) - print_components(comp_name, comp_loc, 4); + print_components(comp_name, comp_loc, ARRAY_SIZE(comp_name)); /* Terminate component table */ create_component_entry(VOID_ADD(table, offset), NULL, component_execute, @@ -924,7 +938,9 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table, ulong size; ulong unused_size; - size = DIV_ROUND_UP(get_stage3_size(), info->phy_block_size); + /* Use approximated stage 3 size as starting point */ + size = MINIMUM_ADDRESS; + /* Ramdisk */ if (job->data.dump.ramdisk != NULL) { if (stat(job->data.dump.ramdisk, &st))