mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Currently, each generated loader binary is linked into one data.o object file which is then linked against boot.o. The data.o file is also used to generate the data.h header file using nm, which will contain all symbols required by zipl. The linking process is somewhat depended on the directory, as the symbol names are derived from the file names of the input files, which is the default behavior of the linker. The file name in this case being the entire path. Examples for the current situation (starting from root dir in each case): $ cd zipl/boot && make eckd0_cdl.bin $ cc -no-pie -static -nostdlib -Wl,--relocatable -Wl,--format,binary -o data.o eckd0_cdl.bin $ nm data.o 0000000000000018 D _binary_eckd0_cdl_bin_end 0000000000000018 A _binary_eckd0_cdl_bin_size 0000000000000000 D _binary_eckd0_cdl_bin_start $ make -C zipl/boot eckd0_cdl.bin $ cc -no-pie -static -nostdlib -Wl,--relocatable -Wl,--format,binary -o data.o zipl/boot/eckd0_cdl.bin $ nm data.o 000000000000018 D _binary_zipl_boot_eckd0_cdl_bin_end 000000000000018 A _binary_zipl_boot_eckd0_cdl_bin_size 000000000000000 D _binary_zipl_boot_eckd0_cdl_bin_start The example above shows that the entire path would end up in the symbol name if specified that way. To make this more robust, future proof, and get more control of the resulting symbol names, use '.incbin' and some macros for embedding the loader binary files. This also reduces the linker step and the generation of the data.h header file. Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Reviewed-by: Stefan Haberland <sth@linux.ibm.com> Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com> Co-developed-by: Jan Höppner <hoeppner@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
637 lines
17 KiB
C
637 lines
17 KiB
C
/*
|
|
* zipl - zSeries Initial Program Loader tool
|
|
*
|
|
* Functions to handle the boot loader data
|
|
*
|
|
* Copyright IBM Corp. 2001, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include "lib/util_libc.h"
|
|
#include "boot/loaders_layout.h"
|
|
|
|
#include "stage3.h"
|
|
|
|
#include "boot.h"
|
|
#include "bootmap.h"
|
|
#include "error.h"
|
|
#include "misc.h"
|
|
|
|
/* Import a binary file */
|
|
/* clang-format off */
|
|
#define DATA_NAME(SYM, SUFFIX) _binary_##SYM##_bin##SUFFIX
|
|
#define DATA_SIZE(SYM) ((size_t)(&DATA_NAME(SYM, _end) - &DATA_NAME(SYM, _start)))
|
|
#define DATA_ADDR(SYM) (&DATA_NAME(SYM, _start))
|
|
#define BIN_FILE_PATH(FILE_NAME) STRINGIFY(BUILD_PATH) "/" STRINGIFY(FILE_NAME) ".bin"
|
|
#define IMPORT_DATA(SYM) \
|
|
extern const uint8_t DATA_NAME(SYM, _start); \
|
|
extern const uint8_t DATA_NAME(SYM, _end); \
|
|
asm(".section \".rodata\", \"a\", @progbits\n" \
|
|
".balign 4\n" \
|
|
".global " STRINGIFY(DATA_NAME(SYM, _start)) "\n" \
|
|
STRINGIFY(DATA_NAME(SYM, _start)) ":\n" \
|
|
".incbin \"" BIN_FILE_PATH(SYM) "\"\n" \
|
|
".global " STRINGIFY(DATA_NAME(SYM, _end)) "\n" \
|
|
STRINGIFY(DATA_NAME(SYM, _end)) ":\n" \
|
|
".balign 4\n" \
|
|
".previous\n")
|
|
/* clang-format on */
|
|
|
|
/* Stage 0 Loader */
|
|
IMPORT_DATA(eckd0_cdl);
|
|
IMPORT_DATA(eckd0_ldl);
|
|
IMPORT_DATA(fba0);
|
|
IMPORT_DATA(tape0);
|
|
/* Stage 1 Loader */
|
|
IMPORT_DATA(eckd1);
|
|
/* Stage 1b Loader */
|
|
IMPORT_DATA(eckd1b);
|
|
IMPORT_DATA(fba1b);
|
|
/* Stage 2 Loader */
|
|
IMPORT_DATA(eckd2);
|
|
IMPORT_DATA(fba2);
|
|
/* Stage 2 Dump Loader */
|
|
IMPORT_DATA(eckd2dump_mv);
|
|
IMPORT_DATA(eckd2dump_sv);
|
|
IMPORT_DATA(fba2dump);
|
|
IMPORT_DATA(tape2dump);
|
|
|
|
#define CCW_FLAG_CC 0x40
|
|
#define CCW_FLAG_SLI 0x20
|
|
#define FBA_BLK_SIZE 512
|
|
|
|
static struct boot_ccw0 tic_to_stage1b = {
|
|
.cmd = 0x08, /* tic */
|
|
.address_lo = STAGE1B_LOAD_ADDRESS,
|
|
};
|
|
|
|
/* Check sizes of internal objects. Return 0 if everything is correct,
|
|
* non-zero otherwise. */
|
|
int
|
|
boot_check_data(void)
|
|
{
|
|
if (DATA_SIZE(fba0) != sizeof(struct boot_fba_stage0)) {
|
|
error_reason("Size mismatch of FBA stage 0 loader");
|
|
return -1;
|
|
}
|
|
if (DATA_SIZE(fba1b) != sizeof(struct boot_fba_stage1b)) {
|
|
error_reason("Size mismatch of FBA stage 1b loader");
|
|
return -1;
|
|
}
|
|
if (DATA_SIZE(eckd0_ldl) !=
|
|
sizeof(struct boot_eckd_ldl_stage0)) {
|
|
error_reason("Size mismatch of ECKD LDL stage 0 loader");
|
|
return -1;
|
|
}
|
|
if (DATA_SIZE(eckd0_cdl) != sizeof(struct boot_eckd_cdl_stage0)) {
|
|
error_reason("Size mismatch of ECKD CDL stage 0 loader");
|
|
return -1;
|
|
}
|
|
if (DATA_SIZE(eckd1) != sizeof(struct boot_eckd_stage1)) {
|
|
error_reason("Size mismatch of ECKD stage 1 loader");
|
|
return -1;
|
|
}
|
|
if (DATA_SIZE(eckd1b) != sizeof(struct boot_eckd_stage1b)) {
|
|
error_reason("Size mismatch of ECKD stage 1b loader");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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 parameter block size in bytes.
|
|
* Return non-zero otherwise.
|
|
*/
|
|
int
|
|
boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
|
|
address_t initrd_addr, size_t initrd_len,
|
|
address_t entry, int extra_parm, uint64_t flags,
|
|
address_t image_addr, size_t image_len,
|
|
address_t envblk_addr, size_t envblk_len)
|
|
{
|
|
struct stage3_parms params;
|
|
void* data;
|
|
|
|
if (entry != (entry & PSW32_ADDR_MASK)) {
|
|
error_reason("Kernel image entry point to high (31 bit "
|
|
"addressing mode)");
|
|
return -1;
|
|
}
|
|
/* Get memory */
|
|
data = misc_malloc(sizeof(params));
|
|
if (data == NULL)
|
|
return -1;
|
|
memset(data, 0, sizeof(params));
|
|
/* 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)(entry | 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;
|
|
params.envblk_addr = (uint64_t) envblk_addr;
|
|
params.envblk_len = (uint64_t) envblk_len;
|
|
/* Initialize buffer */
|
|
memcpy(data, ¶ms, sizeof(params));
|
|
*buffer = data;
|
|
*bytecount = sizeof(params);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
boot_init_fba_stage0(struct boot_fba_stage0 *stage0,
|
|
disk_blockptr_t *stage1b_list, blocknum_t stage1b_count)
|
|
{
|
|
blocknum_t i;
|
|
|
|
/* Initialize stage 0 data */
|
|
memcpy(stage0, DATA_ADDR(fba0), DATA_SIZE(fba0));
|
|
/* Fill in blocklist for stage 2 loader */
|
|
if (stage1b_count > STAGE1B_BLK_CNT_MAX) {
|
|
error_reason("Not enough room for FBA stage 1b loader");
|
|
return -1;
|
|
}
|
|
for (i = 0; i < stage1b_count; i++) {
|
|
stage0->locdata[i].blocknr =
|
|
(uint32_t) stage1b_list[i].linear.block;
|
|
stage0->locread[i].read.address_lo = STAGE1B_LOAD_ADDRESS + i * FBA_BLK_SIZE;
|
|
}
|
|
/* Terminate CCW chain: Tic to stage 1b */
|
|
memcpy(&stage0->locread[i], &tic_to_stage1b, sizeof(tic_to_stage1b));
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
boot_init_eckd_ldl_stage0(struct boot_eckd_ldl_stage0 *stage0)
|
|
{
|
|
memcpy(stage0, DATA_ADDR(eckd0_ldl), DATA_SIZE(eckd0_ldl));
|
|
/* Fill in size of stage 1 plus stage 0 loader */
|
|
stage0->read_r1.count = sizeof(struct boot_eckd_stage1) +
|
|
sizeof(struct boot_eckd_ldl_stage0);
|
|
}
|
|
|
|
void
|
|
boot_init_eckd_cdl_stage0(struct boot_eckd_cdl_stage0 *stage0)
|
|
{
|
|
memcpy(stage0, DATA_ADDR(eckd0_cdl), DATA_SIZE(eckd0_cdl));
|
|
/* Fill in size of stage 1 loader */
|
|
stage0->read.count = sizeof(struct boot_eckd_stage1);
|
|
}
|
|
|
|
int
|
|
boot_init_eckd_stage1(struct boot_eckd_stage1 *stage1,
|
|
disk_blockptr_t *stage1b_list, blocknum_t stage1b_count)
|
|
{
|
|
blocknum_t i;
|
|
|
|
memcpy(stage1, DATA_ADDR(eckd1), DATA_SIZE(eckd1));
|
|
/* Fill in blocklist for stage 1b loader */
|
|
if (stage1b_count > STAGE1B_BLK_CNT_MAX) {
|
|
error_reason("Not enough room for ECKD stage 1b loader "
|
|
"(try larger block size)");
|
|
return -1;
|
|
}
|
|
for (i = 0; i < stage1b_count; i++) {
|
|
stage1->ssrt[i].read.count = stage1b_list[i].chs.size;
|
|
stage1->seek[i].cyl = stage1b_list[i].chs.cyl;
|
|
stage1->seek[i].head = stage1b_list[i].chs.head |
|
|
((stage1b_list[i].chs.cyl >> 12) & 0xfff0);
|
|
stage1->seek[i].sec = stage1b_list[i].chs.sec;
|
|
stage1->ssrt[i].read.address_lo =
|
|
STAGE1B_LOAD_ADDRESS + i * stage1b_list[i].chs.size;
|
|
stage1->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI;
|
|
}
|
|
/* Terminate CCW chain: Tic to stage 1b */
|
|
memcpy(&stage1->ssrt[i], &tic_to_stage1b, sizeof(tic_to_stage1b));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b,
|
|
disk_blockptr_t *stage2_list, blocknum_t stage2_count)
|
|
{
|
|
blocknum_t i;
|
|
|
|
memcpy(stage1b, DATA_ADDR(fba1b), DATA_SIZE(fba1b));
|
|
if (stage2_count > STAGE2_BLK_CNT_MAX) {
|
|
error_reason("Not enough room for FBA stage 2 loader");
|
|
return -1;
|
|
}
|
|
for (i = 0; i < stage2_count; i++) {
|
|
stage1b->locdata[i].blocknr =
|
|
(uint32_t) stage2_list[i].linear.block;
|
|
stage1b->locread[i].read.address_lo =
|
|
STAGE2_LOAD_ADDRESS + i * FBA_BLK_SIZE;
|
|
}
|
|
/* Terminate CCW chain */
|
|
stage1b->locread[i - 1].read.flags &= ~CCW_FLAG_CC;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
boot_init_eckd_stage1b(struct boot_eckd_stage1b *stage1b,
|
|
disk_blockptr_t *stage2_list, blocknum_t stage2_count)
|
|
{
|
|
blocknum_t i;
|
|
|
|
memcpy(stage1b, DATA_ADDR(eckd1b), DATA_SIZE(eckd1b));
|
|
if (stage2_count > STAGE2_BLK_CNT_MAX) {
|
|
error_reason("Not enough room for ECKD stage 2 loader "
|
|
"(try larger block size)");
|
|
return -1;
|
|
}
|
|
for (i = 0; i < stage2_count; i++) {
|
|
stage1b->ssrt[i].read.count = stage2_list[i].chs.size;
|
|
stage1b->seek[i].cyl = stage2_list[i].chs.cyl;
|
|
stage1b->seek[i].head = stage2_list[i].chs.head |
|
|
((stage2_list[i].chs.cyl >> 12) & 0xfff0);
|
|
stage1b->seek[i].sec = stage2_list[i].chs.sec;
|
|
stage1b->ssrt[i].read.address_lo = STAGE2_LOAD_ADDRESS +
|
|
i * stage2_list[i].chs.size;
|
|
stage1b->ssrt[i].read.flags = CCW_FLAG_CC | CCW_FLAG_SLI;
|
|
}
|
|
/* Terminate CCW chain */
|
|
stage1b->ssrt[i - 1].read.flags &= ~CCW_FLAG_CC;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
|
|
address_t initrd_addr, address_t image_addr)
|
|
{
|
|
struct boot_tape_ipl_params params;
|
|
void* buffer;
|
|
|
|
if (image_addr != (image_addr & PSW32_ADDR_MASK)) {
|
|
error_reason("Kernel image load address to high (31 bit "
|
|
"addressing mode)");
|
|
return -1;
|
|
}
|
|
buffer = misc_malloc(DATA_SIZE(tape0));
|
|
if (buffer == NULL)
|
|
return -1;
|
|
/* Prepare params section */
|
|
params.parm_addr = (uint64_t) parm_addr;
|
|
params.initrd_addr = (uint64_t) initrd_addr;
|
|
params.load_psw = (uint64_t) (image_addr | PSW_LOAD);
|
|
/* Initialize buffer */
|
|
memcpy(buffer, DATA_ADDR(tape0), DATA_SIZE(tape0));
|
|
memcpy(VOID_ADD(buffer, BOOT_TAPE_IPL_PARAMS_OFFSET), ¶ms,
|
|
sizeof(struct boot_tape_ipl_params));
|
|
*data = buffer;
|
|
*size = DATA_SIZE(tape0);
|
|
return 0;
|
|
}
|
|
|
|
struct menu_buffer {
|
|
void *start;
|
|
size_t size;
|
|
size_t off;
|
|
};
|
|
|
|
static void
|
|
mb_init(struct menu_buffer *buffer, void *data, size_t size)
|
|
{
|
|
buffer-> start = data;
|
|
buffer->size = size;
|
|
buffer->off = 0;
|
|
memset(data, 0, size);
|
|
}
|
|
|
|
|
|
static void *
|
|
mb_alloc(struct menu_buffer *buffer, size_t len)
|
|
{
|
|
void *result;
|
|
|
|
if (buffer->off + len > buffer->size)
|
|
return NULL;
|
|
result = VOID_ADD(buffer->start, buffer->off);
|
|
buffer->off += len;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static void*
|
|
mb_sprintf(struct menu_buffer *buffer, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int size;
|
|
int len;
|
|
char *str;
|
|
|
|
str = VOID_ADD(buffer->start, buffer->off);
|
|
size = buffer->size - buffer->off;
|
|
va_start(args, fmt);
|
|
len = vsnprintf(str, size, fmt, args);
|
|
va_end(args);
|
|
if (len < 0 || len >= size)
|
|
return NULL;
|
|
misc_ascii_to_ebcdic((unsigned char *) str,
|
|
(unsigned char *) str + len);
|
|
mb_alloc(buffer, len + 1);
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
static int
|
|
store_stage2_menu(void* data, size_t size, struct job_data* job)
|
|
{
|
|
struct boot_stage2_params* params;
|
|
char* name;
|
|
int flag;
|
|
int timeout;
|
|
int i;
|
|
struct menu_buffer mb;
|
|
void *str;
|
|
uint64_t config_kdump = 0;
|
|
|
|
mb_init(&mb, data, size);
|
|
if (job->id == job_menu) {
|
|
name = "-";
|
|
for (i = 0; i < job->data.menu.num; i++) {
|
|
if (job->data.menu.entry[i].id == job_ipl &&
|
|
job->data.menu.entry[i].data.ipl.is_kdump)
|
|
/* we start with 2nd bit */
|
|
config_kdump |= (0x1 << (i + 1));
|
|
if (job->data.menu.entry[i].pos ==
|
|
job->data.menu.default_pos) {
|
|
if (job->data.menu.entry[i].id == job_ipl &&
|
|
job->data.menu.entry[i].data.ipl.is_kdump)
|
|
/* default entry is first bit */
|
|
config_kdump |= 0x1;
|
|
name = job->data.menu.entry[i].name;
|
|
}
|
|
}
|
|
flag = (job->data.menu.prompt != 0);
|
|
timeout = job->data.menu.timeout;
|
|
/* Be verbose */
|
|
if (verbose) {
|
|
printf("Preparing boot menu\n");
|
|
printf(" Interactive prompt......: %s\n",
|
|
job->data.menu.prompt ? "enabled" : "disabled");
|
|
if (job->data.menu.timeout == 0)
|
|
printf(" Menu timeout............: "
|
|
"disabled\n");
|
|
else
|
|
printf(" Menu timeout............: %d "
|
|
"seconds\n", job->data.menu.timeout);
|
|
printf(" Default configuration...: '%s'\n", name);
|
|
}
|
|
} else {
|
|
if (job->id == job_ipl && job->data.ipl.is_kdump)
|
|
config_kdump |= 0x1;
|
|
name = job->name;
|
|
flag = 0;
|
|
timeout = 0;
|
|
}
|
|
/* Header */
|
|
params = mb_alloc(&mb, sizeof(struct boot_stage2_params));
|
|
if (!params)
|
|
goto err_nospace;
|
|
params->flag = flag;
|
|
params->config_kdump = config_kdump;
|
|
params->timeout = timeout;
|
|
/* Banner text */
|
|
str = mb_sprintf(&mb, "zIPL v%s interactive boot menu\n ",
|
|
RELEASE_STRING);
|
|
if (!str)
|
|
goto err_nospace;
|
|
params->banner = (uint16_t) ((unsigned long) str -
|
|
(unsigned long) data);
|
|
/* Default config text */
|
|
if (name != NULL)
|
|
str = mb_sprintf(&mb, " 0. default (%s)", name);
|
|
else
|
|
str = mb_sprintf(&mb, " 0. default");
|
|
if (!str)
|
|
goto err_nospace;
|
|
params->config[0] = (uint16_t) ((unsigned long) str -
|
|
(unsigned long) data);
|
|
/* Skip rest if job is not an actual menu */
|
|
if (job->id != job_menu)
|
|
return 0;
|
|
/* Config texts */
|
|
for (i = 0; i < job->data.menu.num; i++) {
|
|
if (job->data.menu.entry[i].data.ipl.common.ignore) {
|
|
params->config[job->data.menu.entry[i].pos] = 0;
|
|
continue;
|
|
}
|
|
const char *kdump_str = "";
|
|
if (job->data.menu.entry[i].data.ipl.is_kdump)
|
|
kdump_str = " (kdump)";
|
|
str = mb_sprintf(&mb, "%2d. %s%s",
|
|
job->data.menu.entry[i].pos,
|
|
job->data.menu.entry[i].name,
|
|
kdump_str);
|
|
if (!str)
|
|
goto err_nospace_user;
|
|
params->config[job->data.menu.entry[i].pos] = (uint16_t)
|
|
((unsigned long) str - (unsigned long) data);
|
|
}
|
|
return 0;
|
|
|
|
err_nospace:
|
|
error_reason("Not enough room for menu data");
|
|
return -1;
|
|
err_nospace_user:
|
|
error_reason("Not enough room for menu data (try fewer sections or "
|
|
"shorter names)");
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
boot_get_fba_stage2(void** data, size_t* size, struct job_data* job)
|
|
{
|
|
void* buffer;
|
|
int rc;
|
|
|
|
buffer = misc_malloc(STAGE2_MAX_SIZE);
|
|
if (buffer == NULL)
|
|
return -1;
|
|
memcpy(buffer, DATA_ADDR(fba2), DATA_SIZE(fba2));
|
|
rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(fba2)),
|
|
STAGE2_MAX_SIZE - DATA_SIZE(fba2),
|
|
job);
|
|
if (rc) {
|
|
free(buffer);
|
|
return rc;
|
|
}
|
|
*data = buffer;
|
|
*size = STAGE2_MAX_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job)
|
|
{
|
|
void* buffer;
|
|
int rc;
|
|
|
|
buffer = misc_malloc(STAGE2_MAX_SIZE);
|
|
if (buffer == NULL)
|
|
return -1;
|
|
memcpy(buffer, DATA_ADDR(eckd2), DATA_SIZE(eckd2));
|
|
rc = store_stage2_menu(VOID_ADD(buffer, DATA_SIZE(eckd2)),
|
|
STAGE2_MAX_SIZE - DATA_SIZE(eckd2),
|
|
job);
|
|
if (rc) {
|
|
free(buffer);
|
|
return rc;
|
|
}
|
|
*data = buffer;
|
|
*size = STAGE2_MAX_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
boot_get_tape_dump(void** data, size_t* size, uint64_t mem)
|
|
{
|
|
void* buffer;
|
|
|
|
buffer = misc_malloc(DATA_SIZE(tape2dump));
|
|
if (buffer == NULL)
|
|
return -1;
|
|
memcpy(buffer, DATA_ADDR(tape2dump), DATA_SIZE(tape2dump));
|
|
/* Write mem size to end of dump record */
|
|
memcpy(VOID_ADD(buffer, DATA_SIZE(tape2dump) - sizeof(mem)), &mem,
|
|
sizeof(mem));
|
|
*data = buffer;
|
|
*size = DATA_SIZE(tape2dump);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
boot_get_eckd_dump_stage2(void** data, size_t* size, uint64_t mem)
|
|
{
|
|
void* buffer;
|
|
|
|
buffer = misc_malloc(DATA_SIZE(eckd2dump_sv));
|
|
if (buffer == NULL)
|
|
return -1;
|
|
memcpy(buffer, DATA_ADDR(eckd2dump_sv), DATA_SIZE(eckd2dump_sv));
|
|
/* Write mem size to end of dump record */
|
|
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_sv) - sizeof(mem)),
|
|
&mem, sizeof(mem));
|
|
*data = buffer;
|
|
*size = DATA_SIZE(eckd2dump_sv);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
boot_get_eckd_mvdump_stage2(void** data, size_t* size, uint64_t mem,
|
|
uint8_t force, struct mvdump_parm_table parm)
|
|
{
|
|
void* buffer;
|
|
|
|
buffer = misc_malloc(DATA_SIZE(eckd2dump_mv));
|
|
if (buffer == NULL)
|
|
return -1;
|
|
memcpy(buffer, DATA_ADDR(eckd2dump_mv), DATA_SIZE(eckd2dump_mv));
|
|
/* Write mem size and force indicator (as specified by zipl -M)
|
|
* to end of dump record, right before 512-byte parameter table */
|
|
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) - sizeof(mem) -
|
|
sizeof(struct mvdump_parm_table)), &mem, sizeof(mem));
|
|
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) - sizeof(mem) -
|
|
sizeof(force) - sizeof(struct mvdump_parm_table)),
|
|
&force, sizeof(force));
|
|
memcpy(VOID_ADD(buffer, DATA_SIZE(eckd2dump_mv) -
|
|
sizeof(struct mvdump_parm_table)), &parm,
|
|
sizeof(struct mvdump_parm_table));
|
|
*data = buffer;
|
|
*size = DATA_SIZE(eckd2dump_mv);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
boot_get_fba_dump_stage2(void** data, size_t* size, uint64_t mem)
|
|
{
|
|
void* buffer;
|
|
|
|
buffer = misc_malloc(DATA_SIZE(fba2dump));
|
|
if (buffer == NULL)
|
|
return -1;
|
|
memcpy(buffer, DATA_ADDR(fba2dump), DATA_SIZE(fba2dump));
|
|
/* Write mem size to end of dump record */
|
|
memcpy(VOID_ADD(buffer, DATA_SIZE(fba2dump) - sizeof(mem)),
|
|
&mem, sizeof(mem));
|
|
*data = buffer;
|
|
*size = DATA_SIZE(fba2dump);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
boot_get_dump_info(struct boot_info *boot_info, uint8_t dev_type, void *param)
|
|
{
|
|
memset(boot_info, 0, sizeof(*boot_info));
|
|
memcpy(&boot_info->magic, BOOT_INFO_MAGIC, sizeof(boot_info->magic));
|
|
boot_info->flags |= BOOT_INFO_FLAGS_ARCH;
|
|
boot_info->dev_type = dev_type;
|
|
boot_info->bp_type = BOOT_INFO_BP_TYPE_DUMP;
|
|
boot_info->version = BOOT_INFO_VERSION;
|
|
memcpy(&boot_info->bp.dump.param, param,
|
|
sizeof(boot_info->bp.dump.param));
|
|
}
|
|
|
|
/*
|
|
* Helper function to install a boot record for CCW-type IPL from DASD
|
|
*/
|
|
void
|
|
boot_get_ipl_info_ccw(struct boot_info *boot_info, uint8_t dev_type,
|
|
disk_blockptr_t *bm_ptr, struct disk_info *info)
|
|
{
|
|
memset(boot_info, 0, sizeof(*boot_info));
|
|
memcpy(&boot_info->magic, BOOT_INFO_MAGIC, sizeof(boot_info->magic));
|
|
boot_info->flags |= BOOT_INFO_FLAGS_ARCH;
|
|
boot_info->dev_type = dev_type;
|
|
boot_info->bp_type = BOOT_INFO_BP_TYPE_IPL;
|
|
boot_info->version = BOOT_INFO_VERSION;
|
|
bootmap_store_blockptr(&boot_info->bp, bm_ptr, info,
|
|
LEGACY_BLKPTR_FORMAT_ID);
|
|
}
|
|
|
|
/**
|
|
* Allocate and initialize a boot record for List-Directed IPL from DASD
|
|
*
|
|
* TABLE: address of an actual program table
|
|
*/
|
|
int boot_get_eckd_ld_ipl_br(void **br_ptr, size_t *size_ptr,
|
|
disk_blockptr_t *table, struct disk_info *info)
|
|
{
|
|
struct eckd_boot_record *br;
|
|
|
|
br = util_zalloc(info->phy_block_size);
|
|
if (!br)
|
|
return -1;
|
|
|
|
memcpy(&br->magic, ZIPL_MAGIC, ZIPL_MAGIC_SIZE);
|
|
br->version_id = DISK_LAYOUT_ID;
|
|
bootmap_store_blockptr(&br->program_table_pointer, table, info,
|
|
BLKPTR_FORMAT_ID);
|
|
br->os_id = 0x55aa; /* LINUX */
|
|
*br_ptr = br;
|
|
*size_ptr = info->phy_block_size;
|
|
return 0;
|
|
}
|