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:
Stefan Haberland
2019-04-15 18:08:43 +02:00
committed by Jan Höppner
parent 63089835b0
commit e764f460c4
10 changed files with 70 additions and 9 deletions

View File

@@ -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
*/

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 */

View File

@@ -23,6 +23,11 @@ SECTIONS
_extra_parm = .;
. = 0xa028;
stage3_flags =.;
. = 0xa030;
_image_len = .;
. = 0xa038;
_image_addr = .;
. = 0xa000;
.text.dummy : { *(.text.dummy) }

View File

@@ -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);

View File

@@ -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"

View File

@@ -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, &params, sizeof(struct boot_stage3_params));

View File

@@ -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);

View File

@@ -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;