mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
zipl: Do not strip kernel image IPL header
The binary Linux kernel image is built to be loaded to memory address 0x0 but the first 64 kbyte contain an IPL header that is not used for disk IPL. zIPL strips away this IPL header when writing IPL records to disk, loads the remaining data to memory address 0x10000 and uses the memory area below that for its own boot loader code. The Secure Boot firmware feature checks the integrity of an installed image during IPL using a checksum that was generated for the full image. Since the checksum becomes invalid if the IPL header is removed, zIPL must be changed to write the full image to disk. This patch modifies the zIPL logic to no longer strip away the IPL header. Instead the full image is loaded to a higher memory address and relocated by the stage 3 boot loader code to its final location. Signed-off-by: Stefan Haberland <sth@linux.ibm.com> Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
63089835b0
commit
e764f460c4
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -23,6 +23,11 @@ SECTIONS
|
||||
_extra_parm = .;
|
||||
. = 0xa028;
|
||||
stage3_flags =.;
|
||||
. = 0xa030;
|
||||
_image_len = .;
|
||||
. = 0xa038;
|
||||
_image_addr = .;
|
||||
|
||||
|
||||
. = 0xa000;
|
||||
.text.dummy : { *(.text.dummy) }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user