Skip to content

Commit

Permalink
zipl: Do not strip kernel image IPL header
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Stefan Haberland authored and hoeppnerj committed Apr 29, 2019
1 parent 6308983 commit e764f46
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 9 deletions.
20 changes: 20 additions & 0 deletions zipl/boot/libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
1 change: 1 addition & 0 deletions zipl/boot/libc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 15 additions & 1 deletion zipl/boot/stage3.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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"
);
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions zipl/boot/stage3.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 */
5 changes: 5 additions & 0 deletions zipl/boot/stage3.lds
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ SECTIONS
_extra_parm = .;
. = 0xa028;
stage3_flags =.;
. = 0xa030;
_image_len = .;
. = 0xa038;
_image_addr = .;


. = 0xa000;
.text.dummy : { *(.text.dummy) }
Expand Down
6 changes: 5 additions & 1 deletion zipl/include/boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
1 change: 0 additions & 1 deletion zipl/include/zipl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
5 changes: 4 additions & 1 deletion zipl/src/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -95,13 +95,16 @@ 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;
params.initrd_len = (uint64_t) initrd_len;
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));
Expand Down
19 changes: 15 additions & 4 deletions zipl/src/bootmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion zipl/src/job.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit e764f46

Please sign in to comment.