diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c index f86f62ac..5944c4e2 100644 --- a/zipl/boot/libc.c +++ b/zipl/boot/libc.c @@ -58,6 +58,26 @@ void *memcpy(void *dest, const void *src, unsigned long n) return dest; } +/* + * Move @n bytes of memory from @src to @dest. The memory regions may overlap. + */ +void *memmove(void *dest, const void *src, unsigned long n) +{ + const char *s = src; + char *d = dest; + + if (s < d) { + d += n; + s += n; + while (n--) + *--d = *--s; + } else { + while (n--) + *d++ = *s++; + } + return dest; +} + /* * Copy string */ diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h index b05a1169..44097fa4 100644 --- a/zipl/boot/libc.h +++ b/zipl/boot/libc.h @@ -49,6 +49,7 @@ typedef unsigned char uint8_t; void printf(const char *, ...); void sprintf(char *, const char *, ...); void *memcpy(void *, const void *, unsigned long); +void *memmove(void *, const void *, unsigned long); void *memset(void *, int c, unsigned long); char *strcat(char *, const char *); int strncmp(const char *, const char *, unsigned long); diff --git a/zipl/boot/stage3.c b/zipl/boot/stage3.c index 109acd17..b803b0b9 100644 --- a/zipl/boot/stage3.c +++ b/zipl/boot/stage3.c @@ -14,13 +14,15 @@ #include "stage3.h" /* - * 48 Byte dummy space for external symbols + * 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 */ @@ -39,6 +41,10 @@ __attribute__ ((section(".text.dummy"))) void _dummy(void) ".long 0x00000000\n" ".long 0x00000000\n" ".long 0x00000000\n" + ".long 0x00000000\n" + ".long 0x00000000\n" + ".long 0x00000000\n" + ".long 0x00000000\n" ); } @@ -228,6 +234,14 @@ void start(void) unsigned char *command_line = (unsigned char *)COMMAND_LINE; unsigned int begin = 0, end = 0, length = 0; + /* + * Relocate the kernel image to its actual load address while stripping + * away the kernel IPL header to not overwrite the stage3 loader. + */ + memmove((void *)_image_addr, + (void *)_image_addr + KERNEL_HEADER_SIZE, + _image_len - KERNEL_HEADER_SIZE); + /* store subchannel ID into low core and into new kernel space */ subchannel_id = S390_lowcore.subchannel_id; *(unsigned int *)__LC_IPLDEV = subchannel_id; diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h index cdc0d778..9bc4f860 100644 --- a/zipl/boot/stage3.h +++ b/zipl/boot/stage3.h @@ -27,6 +27,8 @@ #define STAGE3_FLAG_SCSI 0x0001000000000000ULL #define STAGE3_FLAG_KDUMP 0x0002000000000000ULL +#define KERNEL_HEADER_SIZE 65536 + #define UNSPECIFIED_ADDRESS -1ULL extern unsigned long long _parm_addr; /* address of parmline */ @@ -35,6 +37,8 @@ extern unsigned long long _initrd_len; /* length of initrd */ extern unsigned long long _load_psw; /* load psw of kernel */ extern unsigned long long _extra_parm; /* use extra parm line mechanism? */ extern unsigned long long stage3_flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */ +extern unsigned long long _image_len; /* length of kernel */ +extern unsigned long long _image_addr; /* target address of kernel */ extern void kdump_stage3(); #endif /* STAGE3_H */ diff --git a/zipl/boot/stage3.lds b/zipl/boot/stage3.lds index 638fb25a..7beafb03 100644 --- a/zipl/boot/stage3.lds +++ b/zipl/boot/stage3.lds @@ -23,6 +23,11 @@ SECTIONS _extra_parm = .; . = 0xa028; stage3_flags =.; + . = 0xa030; + _image_len = .; + . = 0xa038; + _image_addr = .; + . = 0xa000; .text.dummy : { *(.text.dummy) } diff --git a/zipl/include/boot.h b/zipl/include/boot.h index f21ab598..8412a65b 100644 --- a/zipl/include/boot.h +++ b/zipl/include/boot.h @@ -248,6 +248,9 @@ struct boot_stage3_params { uint64_t load_psw; uint64_t extra_parm; uint16_t flags; + uint16_t reserved[3]; + uint64_t image_len; + uint64_t image_addr; } __attribute__ ((packed)); #define STAGE3_FLAG_SCSI 0x0001 @@ -308,7 +311,8 @@ 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); + address_t image_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 770801e5..5cd7cb4e 100644 --- a/zipl/include/zipl.h +++ b/zipl/include/zipl.h @@ -35,7 +35,6 @@ #define PSW_LOAD 0x0008000080000000LL #define PSW_DISABLED_WAIT 0x000a000000000000LL -#define KERNEL_HEADER_SIZE 65536 #define BOOTMAP_FILENAME "bootmap" #define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX" diff --git a/zipl/src/boot.c b/zipl/src/boot.c index f5dbf839..f4290fe2 100644 --- a/zipl/src/boot.c +++ b/zipl/src/boot.c @@ -81,7 +81,7 @@ 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) + int extra_parm, uint16_t flags, size_t image_len) { struct boot_stage3_params params; void* data; @@ -95,6 +95,7 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, data = misc_malloc(DATA_SIZE(stage3)); if (data == NULL) return -1; + memset(data, 0, DATA_SIZE(stage3)); /* Prepare params section */ params.parm_addr = (uint64_t) parm_addr; params.initrd_addr = (uint64_t) initrd_addr; @@ -102,6 +103,8 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr, params.load_psw = (uint64_t)(image_addr | PSW_LOAD); params.extra_parm = (uint64_t) extra_parm; params.flags = flags; + 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)); diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c index 1b71b037..1243b0e0 100644 --- a/zipl/src/bootmap.c +++ b/zipl/src/bootmap.c @@ -409,7 +409,6 @@ print_components(const char *name[], struct component_loc *loc, int num) } } - static int add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, int verbose, int add_files, component_header_type type, @@ -424,6 +423,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, struct component_loc comp_loc[4]; int rc; int offset, flags = 0; + size_t ramdisk_size, image_size; memset(comp_loc, 0, sizeof(comp_loc)); table = misc_malloc(info->phy_block_size); @@ -456,18 +456,29 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, return -1; } } + ramdisk_size = stats.st_size; if (info->type == disk_type_scsi) flags |= STAGE3_FLAG_SCSI; if (ipl->is_kdump) flags |= STAGE3_FLAG_KDUMP; + /* Get kernel file size */ + if (stat(ipl->image, &stats)) { + error_reason(strerror(errno)); + error_text("Could not get information for file '%s'", + ipl->image); + free(table); + return -1; + } + image_size = stats.st_size; + /* Add stage 3 loader to bootmap */ rc = boot_get_stage3(&stage3, &stage3_size, ipl->parm_addr, - ipl->ramdisk_addr, (size_t) stats.st_size, + ipl->ramdisk_addr, ramdisk_size, ipl->is_kdump ? ipl->image_addr + 0x10 : ipl->image_addr, (info->type == disk_type_scsi) ? 0 : 1, - flags); + flags, image_size); if (rc) { free(table); return rc; @@ -487,7 +498,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, printf(" kernel image......: %s\n", ipl->image); } rc = add_component_file(fd, ipl->image, ipl->image_addr, - KERNEL_HEADER_SIZE, VOID_ADD(table, offset), + 0, VOID_ADD(table, offset), add_files, info, target, &comp_loc[0]); if (rc) { error_text("Could not add image file '%s'", ipl->image); diff --git a/zipl/src/job.c b/zipl/src/job.c index 22d2549b..bdd1744c 100644 --- a/zipl/src/job.c +++ b/zipl/src/job.c @@ -511,7 +511,7 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp, /* Fill in component data */ num = 0; rc = set_cl_element(&cl[num++], "kernel image", ipl->image, - &ipl->image_addr, 0, 0x10000, + &ipl->image_addr, 0, 0, MAXIMUM_PHYSICAL_BLOCKSIZE); if (rc) goto error;