Boot menu patches by Collin L. Walling
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJak+BVAAoJEC7Z13T+cC21br8P+gKcQLzJGpwXCTVScSwa/ZhZ i41v47C8V+yROzFzASB9pXaO2RZcZSnAK9yOh+g9xEq4PCwUpWQjU8+HaFztzrhF pdfAILKycREA8ODi6jD9jzhBFQMHHGWG8pGETQX6f+BI+/9SDb1UqW9RQfk9kALu iQpumzfZTC/wLZwXTFZCpVV4/itRKWk4vycH/7Lm3VXvO7D1S1yD3jYQWEy5Y3Hw WvVtL6NvVtPnVb98lEF6jQcSvw2esT+X+nN+RZVsVsO94UlqAoS8p+hHEziO7Spi L165QmrH3tDVdB9T63EuSRRhG2t6C3bgrWVk+6rQpbsxx50acGvzenh++OQYkoYW 2UJJJj0NfjLqmE+3/z8TJ4bBwZwvi3YfvEHd305xt1ri5SBFwV4XOrGmBhSsJkii z16RUdy9r7YewckJ8lcqJX8I57w21z2CbikJJXl6fMlZrObfjJb2ghTZ/tmjWjTb birI77jvog7SLkysz+UdPhMypE7PyI2gGdK6bsSQphEckiAVNKdWRrnYcMi4iSpk jqD1SR5KWyr08n3buCylSGuceyUI8zkjJiPVjt8MAHB4mD7xFFj+fOtbdu/vWNtQ 0d45FUc3UH85e8zcyBqO1oqO2Sq+cBeULUpVD9AsbAMNHpJLaWwFxFZ4q9Z5FxmB on4XOBYHYmh70J9ltX9V =ERxC -----END PGP SIGNATURE----- Merge tag 'tags/s390-ccw-bios-2018-02-26' into s390-next Boot menu patches by Collin L. Walling # gpg: Signature made Mon 26 Feb 2018 11:24:21 AM CET # gpg: using RSA key 2ED9D774FE702DB5 # gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full] # gpg: aka "Thomas Huth <thuth@redhat.com>" [undefined] # gpg: aka "Thomas Huth <huth@tuxfamily.org>" [undefined] # gpg: aka "Thomas Huth <th.huth@posteo.de>" [unknown] * tag 'tags/s390-ccw-bios-2018-02-26': pc-bios/s390: Rebuild the s390x firmware images with the boot menu changes s390-ccw: interactive boot menu for scsi s390-ccw: use zipl values when no boot menu options are present s390-ccw: set cp_receive mask only when needed and consume pending service irqs s390-ccw: read user input for boot index via the SCLP console s390-ccw: print zipl boot menu s390-ccw: read stage2 boot loader data to find menu s390-ccw: set up interactive boot menu parameters s390-ccw: parse and set boot menu options s390-ccw: move auxiliary IPL data to separate location s390-ccw: update libc s390-ccw: refactor IPL structs s390-ccw: refactor eckd_block_num to use CHS s390-ccw: refactor boot map table code
This commit is contained in:
commit
eae9f29130
@ -23,6 +23,9 @@
|
||||
#include "hw/s390x/ebcdic.h"
|
||||
#include "ipl.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/option.h"
|
||||
|
||||
#define KERN_IMAGE_START 0x010000UL
|
||||
#define KERN_PARM_AREA 0x010480UL
|
||||
@ -219,6 +222,61 @@ static Property s390_ipl_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void s390_ipl_set_boot_menu(S390IPLState *ipl)
|
||||
{
|
||||
QemuOptsList *plist = qemu_find_opts("boot-opts");
|
||||
QemuOpts *opts = QTAILQ_FIRST(&plist->head);
|
||||
uint8_t *flags = &ipl->qipl.qipl_flags;
|
||||
uint32_t *timeout = &ipl->qipl.boot_menu_timeout;
|
||||
const char *tmp;
|
||||
unsigned long splash_time = 0;
|
||||
|
||||
if (!get_boot_device(0)) {
|
||||
if (boot_menu) {
|
||||
error_report("boot menu requires a bootindex to be specified for "
|
||||
"the IPL device.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ipl->iplb.pbt) {
|
||||
case S390_IPL_TYPE_CCW:
|
||||
/* In the absence of -boot menu, use zipl parameters */
|
||||
if (!qemu_opt_get(opts, "menu")) {
|
||||
*flags |= QIPL_FLAG_BM_OPTS_ZIPL;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
break;
|
||||
default:
|
||||
error_report("boot menu is not supported for this device type.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!boot_menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
*flags |= QIPL_FLAG_BM_OPTS_CMD;
|
||||
|
||||
tmp = qemu_opt_get(opts, "splash-time");
|
||||
|
||||
if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) {
|
||||
error_report("splash-time is invalid, forcing it to 0.");
|
||||
*timeout = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (splash_time > 0xffffffff) {
|
||||
error_report("splash-time is too large, forcing it to max value.");
|
||||
*timeout = 0xffffffff;
|
||||
return;
|
||||
}
|
||||
|
||||
*timeout = cpu_to_be32(splash_time);
|
||||
}
|
||||
|
||||
static bool s390_gen_initial_iplb(S390IPLState *ipl)
|
||||
{
|
||||
DeviceState *dev_st;
|
||||
@ -399,6 +457,21 @@ void s390_reipl_request(void)
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
}
|
||||
|
||||
static void s390_ipl_prepare_qipl(S390CPU *cpu)
|
||||
{
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
uint8_t *addr;
|
||||
uint64_t len = 4096;
|
||||
|
||||
addr = cpu_physical_memory_map(cpu->env.psa, &len, 1);
|
||||
if (!addr || len < QIPL_ADDRESS + sizeof(QemuIplParameters)) {
|
||||
error_report("Cannot set QEMU IPL parameters");
|
||||
return;
|
||||
}
|
||||
memcpy(addr + QIPL_ADDRESS, &ipl->qipl, sizeof(QemuIplParameters));
|
||||
cpu_physical_memory_unmap(addr, len, 1, len);
|
||||
}
|
||||
|
||||
void s390_ipl_prepare_cpu(S390CPU *cpu)
|
||||
{
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
@ -418,8 +491,10 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
|
||||
error_report_err(err);
|
||||
vm_stop(RUN_STATE_INTERNAL_ERROR);
|
||||
}
|
||||
ipl->iplb.ccw.netboot_start_addr = cpu_to_be64(ipl->start_addr);
|
||||
ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
|
||||
}
|
||||
s390_ipl_set_boot_menu(ipl);
|
||||
s390_ipl_prepare_qipl(cpu);
|
||||
}
|
||||
|
||||
static void s390_ipl_reset(DeviceState *dev)
|
||||
|
@ -16,8 +16,7 @@
|
||||
#include "cpu.h"
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint64_t netboot_start_addr;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
@ -90,6 +89,33 @@ void s390_ipl_prepare_cpu(S390CPU *cpu);
|
||||
IplParameterBlock *s390_ipl_get_iplb(void);
|
||||
void s390_reipl_request(void);
|
||||
|
||||
#define QIPL_ADDRESS 0xcc
|
||||
|
||||
/* Boot Menu flags */
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
/*
|
||||
* The QEMU IPL Parameters will be stored at absolute address
|
||||
* 204 (0xcc) which means it is 32-bit word aligned but not
|
||||
* double-word aligned.
|
||||
* Placement of data fields in this area must account for
|
||||
* their alignment needs. E.g., netboot_start_address must
|
||||
* have an offset of 4 + n * 8 bytes within the struct in order
|
||||
* to keep it double-word aligned.
|
||||
* The total size of the struct must never exceed 28 bytes.
|
||||
* This definition must be kept in sync with the defininition
|
||||
* in pc-bios/s390-ccw/iplb.h.
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t reserved1[3];
|
||||
uint64_t netboot_start_addr;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved2[12];
|
||||
} QEMU_PACKED;
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
|
||||
#define TYPE_S390_IPL "s390-ipl"
|
||||
#define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
|
||||
|
||||
@ -105,6 +131,7 @@ struct S390IPLState {
|
||||
bool iplb_valid;
|
||||
bool reipl_requested;
|
||||
bool netboot;
|
||||
QemuIplParameters qipl;
|
||||
|
||||
/*< public >*/
|
||||
char *kernel;
|
||||
|
Binary file not shown.
@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
|
||||
|
||||
.PHONY : all clean build-all
|
||||
|
||||
OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o
|
||||
OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o menu.o
|
||||
QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
|
||||
QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
|
||||
QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
|
||||
|
@ -83,6 +83,10 @@ static void jump_to_IPL_code(uint64_t address)
|
||||
|
||||
static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */
|
||||
static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr);
|
||||
static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
static void *s2_prev_blk = _s2;
|
||||
static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
|
||||
static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
|
||||
|
||||
static inline void verify_boot_info(BootInfo *bip)
|
||||
{
|
||||
@ -95,32 +99,32 @@ static inline void verify_boot_info(BootInfo *bip)
|
||||
"Bad block size in zIPL section of the 1st record.");
|
||||
}
|
||||
|
||||
static block_number_t eckd_block_num(BootMapPointer *p)
|
||||
static block_number_t eckd_block_num(EckdCHS *chs)
|
||||
{
|
||||
const uint64_t sectors = virtio_get_sectors();
|
||||
const uint64_t heads = virtio_get_heads();
|
||||
const uint64_t cylinder = p->eckd.cylinder
|
||||
+ ((p->eckd.head & 0xfff0) << 12);
|
||||
const uint64_t head = p->eckd.head & 0x000f;
|
||||
const uint64_t cylinder = chs->cylinder
|
||||
+ ((chs->head & 0xfff0) << 12);
|
||||
const uint64_t head = chs->head & 0x000f;
|
||||
const block_number_t block = sectors * heads * cylinder
|
||||
+ sectors * head
|
||||
+ p->eckd.sector
|
||||
+ chs->sector
|
||||
- 1; /* block nr starts with zero */
|
||||
return block;
|
||||
}
|
||||
|
||||
static bool eckd_valid_address(BootMapPointer *p)
|
||||
{
|
||||
const uint64_t head = p->eckd.head & 0x000f;
|
||||
const uint64_t head = p->eckd.chs.head & 0x000f;
|
||||
|
||||
if (head >= virtio_get_heads()
|
||||
|| p->eckd.sector > virtio_get_sectors()
|
||||
|| p->eckd.sector <= 0) {
|
||||
|| p->eckd.chs.sector > virtio_get_sectors()
|
||||
|| p->eckd.chs.sector <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!virtio_guessed_disk_nature() &&
|
||||
eckd_block_num(p) >= virtio_get_blocks()) {
|
||||
eckd_block_num(&p->eckd.chs) >= virtio_get_blocks()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -140,7 +144,7 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
|
||||
do {
|
||||
more_data = false;
|
||||
for (j = 0;; j++) {
|
||||
block_nr = eckd_block_num((void *)&(bprs[j].xeckd));
|
||||
block_nr = eckd_block_num(&bprs[j].xeckd.bptr.chs);
|
||||
if (is_null_block_number(block_nr)) { /* end of chunk */
|
||||
break;
|
||||
}
|
||||
@ -182,31 +186,105 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
|
||||
return block_nr;
|
||||
}
|
||||
|
||||
static void run_eckd_boot_script(block_number_t mbr_block_nr)
|
||||
static bool find_zipl_boot_menu_banner(int *offset)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Menu banner starts with "zIPL" */
|
||||
for (i = 0; i < virtio_get_block_size() - 4; i++) {
|
||||
if (magic_match(s2_cur_blk + i, ZIPL_MAGIC_EBCDIC)) {
|
||||
*offset = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
|
||||
{
|
||||
block_number_t cur_block_nr;
|
||||
block_number_t prev_block_nr = 0;
|
||||
block_number_t next_block_nr = 0;
|
||||
EckdStage1b *s1b = (void *)sec;
|
||||
int banner_offset;
|
||||
int i;
|
||||
|
||||
/* Get Stage1b data */
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader");
|
||||
|
||||
memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
|
||||
|
||||
/* Get Stage2 data */
|
||||
for (i = 0; i < STAGE2_BLK_CNT_MAX; i++) {
|
||||
cur_block_nr = eckd_block_num(&s1b->seek[i].chs);
|
||||
|
||||
if (!cur_block_nr) {
|
||||
break;
|
||||
}
|
||||
|
||||
read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader");
|
||||
|
||||
if (find_zipl_boot_menu_banner(&banner_offset)) {
|
||||
/*
|
||||
* Load the adjacent blocks to account for the
|
||||
* possibility of menu data spanning multiple blocks.
|
||||
*/
|
||||
if (prev_block_nr) {
|
||||
read_block(prev_block_nr, s2_prev_blk,
|
||||
"Cannot read stage2 boot loader");
|
||||
}
|
||||
|
||||
if (i + 1 < STAGE2_BLK_CNT_MAX) {
|
||||
next_block_nr = eckd_block_num(&s1b->seek[i + 1].chs);
|
||||
}
|
||||
|
||||
if (next_block_nr) {
|
||||
read_block(next_block_nr, s2_next_blk,
|
||||
"Cannot read stage2 boot loader");
|
||||
}
|
||||
|
||||
return menu_get_zipl_boot_index(s2_cur_blk + banner_offset);
|
||||
}
|
||||
|
||||
prev_block_nr = cur_block_nr;
|
||||
}
|
||||
|
||||
sclp_print("No zipl boot menu data found. Booting default entry.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_eckd_boot_script(block_number_t bmt_block_nr,
|
||||
block_number_t s1b_block_nr)
|
||||
{
|
||||
int i;
|
||||
unsigned int loadparm = get_loadparm_index();
|
||||
block_number_t block_nr;
|
||||
uint64_t address;
|
||||
ScsiMbr *bte = (void *)sec; /* Eckd bootmap table entry */
|
||||
BootMapTable *bmt = (void *)sec;
|
||||
BootMapScript *bms = (void *)sec;
|
||||
|
||||
if (menu_is_enabled_zipl()) {
|
||||
loadparm = eckd_get_boot_menu_index(s1b_block_nr);
|
||||
}
|
||||
|
||||
debug_print_int("loadparm", loadparm);
|
||||
IPL_assert(loadparm < 31, "loadparm value greater than"
|
||||
IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than"
|
||||
" maximum number of boot entries allowed");
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(mbr_block_nr, sec, "Cannot read MBR");
|
||||
read_block(bmt_block_nr, sec, "Cannot read Boot Map Table");
|
||||
|
||||
block_nr = eckd_block_num((void *)&(bte->blockptr[loadparm]));
|
||||
IPL_assert(block_nr != -1, "No Boot Map");
|
||||
block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs);
|
||||
IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry");
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(block_nr, sec, "Cannot read Boot Map Script");
|
||||
|
||||
for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD; i++) {
|
||||
address = bms->entry[i].address.load_address;
|
||||
block_nr = eckd_block_num(&(bms->entry[i].blkptr));
|
||||
block_nr = eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs);
|
||||
|
||||
do {
|
||||
block_nr = load_eckd_segments(block_nr, &address);
|
||||
@ -221,9 +299,9 @@ static void run_eckd_boot_script(block_number_t mbr_block_nr)
|
||||
static void ipl_eckd_cdl(void)
|
||||
{
|
||||
XEckdMbr *mbr;
|
||||
Ipl2 *ipl2 = (void *)sec;
|
||||
EckdCdlIpl2 *ipl2 = (void *)sec;
|
||||
IplVolumeLabel *vlbl = (void *)sec;
|
||||
block_number_t block_nr;
|
||||
block_number_t bmt_block_nr, s1b_block_nr;
|
||||
|
||||
/* we have just read the block #0 and recognized it as "IPL1" */
|
||||
sclp_print("CDL\n");
|
||||
@ -231,15 +309,18 @@ static void ipl_eckd_cdl(void)
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(1, ipl2, "Cannot read IPL2 record at block 1");
|
||||
|
||||
mbr = &ipl2->u.x.mbr;
|
||||
mbr = &ipl2->mbr;
|
||||
IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record.");
|
||||
IPL_assert(block_size_ok(mbr->blockptr.xeckd.bptr.size),
|
||||
"Bad block size in zIPL section of IPL2 record.");
|
||||
IPL_assert(mbr->dev_type == DEV_TYPE_ECKD,
|
||||
"Non-ECKD device type in zIPL section of IPL2 record.");
|
||||
|
||||
/* save pointer to Boot Script */
|
||||
block_nr = eckd_block_num((void *)&(mbr->blockptr));
|
||||
/* save pointer to Boot Map Table */
|
||||
bmt_block_nr = eckd_block_num(&mbr->blockptr.xeckd.bptr.chs);
|
||||
|
||||
/* save pointer to Stage1b Data */
|
||||
s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs);
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(2, vlbl, "Cannot read Volume Label at block 2");
|
||||
@ -249,7 +330,7 @@ static void ipl_eckd_cdl(void)
|
||||
"Invalid magic of volser block");
|
||||
print_volser(vlbl->f.volser);
|
||||
|
||||
run_eckd_boot_script(block_nr);
|
||||
run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||
/* no return */
|
||||
}
|
||||
|
||||
@ -280,8 +361,8 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode)
|
||||
|
||||
static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||
{
|
||||
block_number_t block_nr;
|
||||
BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */
|
||||
block_number_t bmt_block_nr, s1b_block_nr;
|
||||
EckdLdlIpl1 *ipl1 = (void *)sec;
|
||||
|
||||
if (mode != ECKD_LDL_UNLABELED) {
|
||||
print_eckd_ldl_msg(mode);
|
||||
@ -292,15 +373,20 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(0, sec, "Cannot read block 0 to grab boot info.");
|
||||
if (mode == ECKD_LDL_UNLABELED) {
|
||||
if (!magic_match(bip->magic, ZIPL_MAGIC)) {
|
||||
if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) {
|
||||
return; /* not applicable layout */
|
||||
}
|
||||
sclp_print("unlabeled LDL.\n");
|
||||
}
|
||||
verify_boot_info(bip);
|
||||
verify_boot_info(&ipl1->bip);
|
||||
|
||||
block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr));
|
||||
run_eckd_boot_script(block_nr);
|
||||
/* save pointer to Boot Map Table */
|
||||
bmt_block_nr = eckd_block_num(&ipl1->bip.bp.ipl.bm_ptr.eckd.bptr.chs);
|
||||
|
||||
/* save pointer to Stage1b Data */
|
||||
s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs);
|
||||
|
||||
run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||
/* no return */
|
||||
}
|
||||
|
||||
@ -325,7 +411,7 @@ static void print_eckd_msg(void)
|
||||
|
||||
static void ipl_eckd(void)
|
||||
{
|
||||
ScsiMbr *mbr = (void *)sec;
|
||||
XEckdMbr *mbr = (void *)sec;
|
||||
LDL_VTOC *vlbl = (void *)sec;
|
||||
|
||||
print_eckd_msg();
|
||||
@ -449,10 +535,8 @@ static void zipl_run(ScsiBlockPtr *pte)
|
||||
static void ipl_scsi(void)
|
||||
{
|
||||
ScsiMbr *mbr = (void *)sec;
|
||||
uint8_t *ns, *ns_end;
|
||||
int program_table_entries = 0;
|
||||
const int pte_len = sizeof(ScsiBlockPtr);
|
||||
ScsiBlockPtr *prog_table_entry = NULL;
|
||||
BootMapTable *prog_table = (void *)sec;
|
||||
unsigned int loadparm = get_loadparm_index();
|
||||
|
||||
/* Grab the MBR */
|
||||
@ -467,34 +551,32 @@ static void ipl_scsi(void)
|
||||
debug_print_int("MBR Version", mbr->version_id);
|
||||
IPL_check(mbr->version_id == 1,
|
||||
"Unknown MBR layout version, assuming version 1");
|
||||
debug_print_int("program table", mbr->blockptr[0].blockno);
|
||||
IPL_assert(mbr->blockptr[0].blockno, "No Program Table");
|
||||
debug_print_int("program table", mbr->pt.blockno);
|
||||
IPL_assert(mbr->pt.blockno, "No Program Table");
|
||||
|
||||
/* Parse the program table */
|
||||
read_block(mbr->blockptr[0].blockno, sec,
|
||||
"Error reading Program Table");
|
||||
|
||||
read_block(mbr->pt.blockno, sec, "Error reading Program Table");
|
||||
IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
|
||||
|
||||
debug_print_int("loadparm index", loadparm);
|
||||
ns_end = sec + virtio_get_block_size();
|
||||
for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) {
|
||||
prog_table_entry = (ScsiBlockPtr *)ns;
|
||||
if (!prog_table_entry->blockno) {
|
||||
while (program_table_entries <= MAX_TABLE_ENTRIES) {
|
||||
if (!prog_table->entry[program_table_entries].scsi.blockno) {
|
||||
break;
|
||||
}
|
||||
|
||||
program_table_entries++;
|
||||
if (program_table_entries == loadparm + 1) {
|
||||
break; /* selected entry found */
|
||||
}
|
||||
}
|
||||
|
||||
debug_print_int("program table entries", program_table_entries);
|
||||
|
||||
IPL_assert(program_table_entries != 0, "Empty Program Table");
|
||||
|
||||
zipl_run(prog_table_entry); /* no return */
|
||||
if (menu_is_enabled_enum()) {
|
||||
loadparm = menu_get_enum_boot_index(program_table_entries);
|
||||
}
|
||||
|
||||
debug_print_int("loadparm", loadparm);
|
||||
IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than"
|
||||
" maximum number of boot entries allowed");
|
||||
|
||||
zipl_run(&prog_table->entry[loadparm].scsi); /* no return */
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
@ -512,7 +594,7 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s)
|
||||
"Failed to read image sector 0");
|
||||
|
||||
/* Checking bytes 8 - 32 for S390 Linux magic */
|
||||
return !_memcmp(magic_sec + 8, linux_s390_magic, 24);
|
||||
return !memcmp(magic_sec + 8, linux_s390_magic, 24);
|
||||
}
|
||||
|
||||
/* Location of the current sector of the directory */
|
||||
@ -641,7 +723,7 @@ static uint32_t find_iso_bc(void)
|
||||
if (vd->type == VOL_DESC_TYPE_BOOT) {
|
||||
IsoVdElTorito *et = &vd->vd.boot;
|
||||
|
||||
if (!_memcmp(&et->el_torito[0], el_torito_magic, 32)) {
|
||||
if (!memcmp(&et->el_torito[0], el_torito_magic, 32)) {
|
||||
return bswap32(et->bc_offset);
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,14 @@ typedef struct FbaBlockPtr {
|
||||
uint16_t blockct;
|
||||
} __attribute__ ((packed)) FbaBlockPtr;
|
||||
|
||||
typedef struct EckdBlockPtr {
|
||||
uint16_t cylinder; /* cylinder/head/sector is an address of the block */
|
||||
typedef struct EckdCHS {
|
||||
uint16_t cylinder;
|
||||
uint16_t head;
|
||||
uint8_t sector;
|
||||
} __attribute__ ((packed)) EckdCHS;
|
||||
|
||||
typedef struct EckdBlockPtr {
|
||||
EckdCHS chs; /* cylinder/head/sector is an address of the block */
|
||||
uint16_t size;
|
||||
uint8_t count; /* (size_in_blocks-1);
|
||||
* it's 0 for TablePtr, ScriptPtr, and SectionPtr */
|
||||
@ -53,6 +57,15 @@ typedef union BootMapPointer {
|
||||
ExtEckdBlockPtr xeckd;
|
||||
} __attribute__ ((packed)) BootMapPointer;
|
||||
|
||||
#define MAX_TABLE_ENTRIES 30
|
||||
|
||||
/* aka Program Table */
|
||||
typedef struct BootMapTable {
|
||||
uint8_t magic[4];
|
||||
uint8_t reserved[12];
|
||||
BootMapPointer entry[];
|
||||
} __attribute__ ((packed)) BootMapTable;
|
||||
|
||||
typedef struct ComponentEntry {
|
||||
ScsiBlockPtr data;
|
||||
uint8_t pad[7];
|
||||
@ -70,10 +83,11 @@ typedef struct ScsiMbr {
|
||||
uint8_t magic[4];
|
||||
uint32_t version_id;
|
||||
uint8_t reserved[8];
|
||||
ScsiBlockPtr blockptr[];
|
||||
ScsiBlockPtr pt; /* block pointer to program table */
|
||||
} __attribute__ ((packed)) ScsiMbr;
|
||||
|
||||
#define ZIPL_MAGIC "zIPL"
|
||||
#define ZIPL_MAGIC_EBCDIC "\xa9\xc9\xd7\xd3"
|
||||
#define IPL1_MAGIC "\xc9\xd7\xd3\xf1" /* == "IPL1" in EBCDIC */
|
||||
#define IPL2_MAGIC "\xc9\xd7\xd3\xf2" /* == "IPL2" in EBCDIC */
|
||||
#define VOL1_MAGIC "\xe5\xd6\xd3\xf1" /* == "VOL1" in EBCDIC */
|
||||
@ -226,22 +240,45 @@ typedef struct BootInfo { /* @ 0x70, record #0 */
|
||||
} bp;
|
||||
} __attribute__ ((packed)) BootInfo; /* see also XEckdMbr */
|
||||
|
||||
typedef struct Ipl1 {
|
||||
unsigned char key[4]; /* == "IPL1" */
|
||||
unsigned char data[24];
|
||||
} __attribute__((packed)) Ipl1;
|
||||
/*
|
||||
* Structs for IPL
|
||||
*/
|
||||
#define STAGE2_BLK_CNT_MAX 24 /* Stage 1b can load up to 24 blocks */
|
||||
|
||||
typedef struct Ipl2 {
|
||||
unsigned char key[4]; /* == "IPL2" */
|
||||
union {
|
||||
unsigned char data[144];
|
||||
struct {
|
||||
unsigned char reserved1[92-4];
|
||||
XEckdMbr mbr;
|
||||
unsigned char reserved2[144-(92-4)-sizeof(XEckdMbr)];
|
||||
} x;
|
||||
} u;
|
||||
} __attribute__((packed)) Ipl2;
|
||||
typedef struct EckdCdlIpl1 {
|
||||
uint8_t key[4]; /* == "IPL1" */
|
||||
uint8_t data[24];
|
||||
} __attribute__((packed)) EckdCdlIpl1;
|
||||
|
||||
typedef struct EckdSeekArg {
|
||||
uint16_t pad;
|
||||
EckdCHS chs;
|
||||
uint8_t pad2;
|
||||
} __attribute__ ((packed)) EckdSeekArg;
|
||||
|
||||
typedef struct EckdStage1b {
|
||||
uint8_t reserved[32 * STAGE2_BLK_CNT_MAX];
|
||||
struct EckdSeekArg seek[STAGE2_BLK_CNT_MAX];
|
||||
uint8_t unused[64];
|
||||
} __attribute__ ((packed)) EckdStage1b;
|
||||
|
||||
typedef struct EckdStage1 {
|
||||
uint8_t reserved[72];
|
||||
struct EckdSeekArg seek[2];
|
||||
} __attribute__ ((packed)) EckdStage1;
|
||||
|
||||
typedef struct EckdCdlIpl2 {
|
||||
uint8_t key[4]; /* == "IPL2" */
|
||||
struct EckdStage1 stage1;
|
||||
XEckdMbr mbr;
|
||||
uint8_t reserved[24];
|
||||
} __attribute__((packed)) EckdCdlIpl2;
|
||||
|
||||
typedef struct EckdLdlIpl1 {
|
||||
uint8_t reserved[24];
|
||||
struct EckdStage1 stage1;
|
||||
BootInfo bip; /* BootInfo is MBR for LDL */
|
||||
} __attribute__((packed)) EckdLdlIpl1;
|
||||
|
||||
typedef struct IplVolumeLabel {
|
||||
unsigned char key[4]; /* == "VOL1" */
|
||||
@ -310,20 +347,6 @@ static inline bool magic_match(const void *data, const void *magic)
|
||||
return *((uint32_t *)data) == *((uint32_t *)magic);
|
||||
}
|
||||
|
||||
static inline int _memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
int i;
|
||||
const uint8_t *p1 = s1, *p2 = s2;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (p1[i] != p2[i]) {
|
||||
return p1[i] > p2[i] ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t iso_733_to_u32(uint64_t x)
|
||||
{
|
||||
return (uint32_t)x;
|
||||
@ -416,7 +439,7 @@ const uint8_t vol_desc_magic[] = "CD001";
|
||||
|
||||
static inline bool is_iso_vd_valid(IsoVolDesc *vd)
|
||||
{
|
||||
return !_memcmp(&vd->ident[0], vol_desc_magic, 5) &&
|
||||
return !memcmp(&vd->ident[0], vol_desc_magic, 5) &&
|
||||
vd->version == 0x1 &&
|
||||
vd->type <= VOL_DESC_TYPE_PARTITION;
|
||||
}
|
||||
|
@ -13,8 +13,7 @@
|
||||
#define IPLB_H
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint64_t netboot_start_addr;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
@ -73,6 +72,27 @@ typedef struct IplParameterBlock IplParameterBlock;
|
||||
|
||||
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||
|
||||
#define QIPL_ADDRESS 0xcc
|
||||
|
||||
/* Boot Menu flags */
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
/*
|
||||
* This definition must be kept in sync with the defininition
|
||||
* in hw/s390x/ipl.h
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t reserved1[3];
|
||||
uint64_t netboot_start_addr;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved2[12];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
|
||||
extern QemuIplParameters qipl;
|
||||
|
||||
#define S390_IPL_TYPE_FCP 0x00
|
||||
#define S390_IPL_TYPE_CCW 0x02
|
||||
#define S390_IPL_TYPE_QEMU_SCSI 0xff
|
||||
|
88
pc-bios/s390-ccw/libc.c
Normal file
88
pc-bios/s390-ccw/libc.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* libc-style definitions and functions
|
||||
*
|
||||
* Copyright 2018 IBM Corp.
|
||||
* Author(s): Collin L. Walling <walling@linux.vnet.ibm.com>
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include "s390-ccw.h"
|
||||
|
||||
/**
|
||||
* atoui:
|
||||
* @str: the string to be converted.
|
||||
*
|
||||
* Given a string @str, convert it to an integer. Leading spaces are
|
||||
* ignored. Any other non-numerical value will terminate the conversion
|
||||
* and return 0. This function only handles numbers between 0 and
|
||||
* UINT64_MAX inclusive.
|
||||
*
|
||||
* Returns: an integer converted from the string @str, or the number 0
|
||||
* if an error occurred.
|
||||
*/
|
||||
uint64_t atoui(const char *str)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (!str || !str[0]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*str == ' ') {
|
||||
str++;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
if (!isdigit(*str)) {
|
||||
break;
|
||||
}
|
||||
val = val * 10 + *str - '0';
|
||||
str++;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* uitoa:
|
||||
* @num: an integer (base 10) to be converted.
|
||||
* @str: a pointer to a string to store the conversion.
|
||||
* @len: the length of the passed string.
|
||||
*
|
||||
* Given an integer @num, convert it to a string. The string @str must be
|
||||
* allocated beforehand. The resulting string will be null terminated and
|
||||
* returned. This function only handles numbers between 0 and UINT64_MAX
|
||||
* inclusive.
|
||||
*
|
||||
* Returns: the string @str of the converted integer @num
|
||||
*/
|
||||
char *uitoa(uint64_t num, char *str, size_t len)
|
||||
{
|
||||
size_t num_idx = 1; /* account for NUL */
|
||||
uint64_t tmp = num;
|
||||
|
||||
IPL_assert(str != NULL, "uitoa: no space allocated to store string");
|
||||
|
||||
/* Count indices of num */
|
||||
while ((tmp /= 10) != 0) {
|
||||
num_idx++;
|
||||
}
|
||||
|
||||
/* Check if we have enough space for num and NUL */
|
||||
IPL_assert(len > num_idx, "uitoa: array too small for conversion");
|
||||
|
||||
str[num_idx--] = '\0';
|
||||
|
||||
/* Convert int to string */
|
||||
while (num_idx >= 0) {
|
||||
str[num_idx--] = num % 10 + '0';
|
||||
num /= 10;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* libc-style definitions and functions
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
@ -19,7 +21,7 @@ typedef unsigned long long uint64_t;
|
||||
|
||||
static inline void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
unsigned char *p = s;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
@ -33,7 +35,7 @@ static inline void *memcpy(void *s1, const void *s2, size_t n)
|
||||
{
|
||||
uint8_t *dest = s1;
|
||||
const uint8_t *src = s2;
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
dest[i] = src[i];
|
||||
@ -42,4 +44,35 @@ static inline void *memcpy(void *s1, const void *s2, size_t n)
|
||||
return s1;
|
||||
}
|
||||
|
||||
static inline int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
const uint8_t *p1 = s1, *p2 = s2;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (p1[i] != p2[i]) {
|
||||
return p1[i] > p2[i] ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t strlen(const char *str)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; *str; i++) {
|
||||
str++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline int isdigit(int c)
|
||||
{
|
||||
return (c >= '0') && (c <= '9');
|
||||
}
|
||||
|
||||
uint64_t atoui(const char *str);
|
||||
char *uitoa(uint64_t num, char *str, size_t len);
|
||||
|
||||
#endif
|
||||
|
@ -16,6 +16,11 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
static SubChannelId blk_schid = { .one = 1 };
|
||||
IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||
static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
QemuIplParameters qipl;
|
||||
|
||||
#define LOADPARM_PROMPT "PROMPT "
|
||||
#define LOADPARM_EMPTY "........"
|
||||
#define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL)
|
||||
|
||||
/*
|
||||
* Priniciples of Operations (SA22-7832-09) chapter 17 requires that
|
||||
@ -40,22 +45,7 @@ void panic(const char *string)
|
||||
|
||||
unsigned int get_loadparm_index(void)
|
||||
{
|
||||
const char *lp = loadparm;
|
||||
int i;
|
||||
unsigned int idx = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
char c = lp[i];
|
||||
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
|
||||
idx *= 10;
|
||||
idx += c - '0';
|
||||
}
|
||||
|
||||
return idx;
|
||||
return atoui(loadparm);
|
||||
}
|
||||
|
||||
static bool find_dev(Schib *schib, int dev_no)
|
||||
@ -88,6 +78,27 @@ static bool find_dev(Schib *schib, int dev_no)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void menu_setup(void)
|
||||
{
|
||||
if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) {
|
||||
menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If loadparm was set to any other value, then do not enable menu */
|
||||
if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (iplb.pbt) {
|
||||
case S390_IPL_TYPE_CCW:
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK,
|
||||
qipl.boot_menu_timeout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_setup(void)
|
||||
{
|
||||
Schib schib;
|
||||
@ -96,6 +107,7 @@ static void virtio_setup(void)
|
||||
uint16_t dev_no;
|
||||
char ldp[] = "LOADPARM=[________]\n";
|
||||
VDev *vdev = virtio_get_device();
|
||||
QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
|
||||
|
||||
/*
|
||||
* We unconditionally enable mss support. In every sane configuration,
|
||||
@ -108,6 +120,8 @@ static void virtio_setup(void)
|
||||
memcpy(ldp + 10, loadparm, 8);
|
||||
sclp_print(ldp);
|
||||
|
||||
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
|
||||
|
||||
if (store_iplb(&iplb)) {
|
||||
switch (iplb.pbt) {
|
||||
case S390_IPL_TYPE_CCW:
|
||||
@ -128,6 +142,7 @@ static void virtio_setup(void)
|
||||
default:
|
||||
panic("List-directed IPL not supported yet!\n");
|
||||
}
|
||||
menu_setup();
|
||||
} else {
|
||||
for (ssid = 0; ssid < 0x3; ssid++) {
|
||||
blk_schid.ssid = ssid;
|
||||
@ -142,7 +157,7 @@ static void virtio_setup(void)
|
||||
|
||||
if (virtio_get_device_type() == VIRTIO_ID_NET) {
|
||||
sclp_print("Network boot device detected\n");
|
||||
vdev->netboot_start_addr = iplb.ccw.netboot_start_addr;
|
||||
vdev->netboot_start_addr = qipl.netboot_start_addr;
|
||||
} else {
|
||||
virtio_blk_setup_device(blk_schid);
|
||||
|
||||
|
249
pc-bios/s390-ccw/menu.c
Normal file
249
pc-bios/s390-ccw/menu.c
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* QEMU S390 Interactive Boot Menu
|
||||
*
|
||||
* Copyright 2018 IBM Corp.
|
||||
* Author: Collin L. Walling <walling@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||
* your option) any later version. See the COPYING file in the top-level
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include "s390-ccw.h"
|
||||
#include "sclp.h"
|
||||
|
||||
#define KEYCODE_NO_INP '\0'
|
||||
#define KEYCODE_ESCAPE '\033'
|
||||
#define KEYCODE_BACKSP '\177'
|
||||
#define KEYCODE_ENTER '\r'
|
||||
|
||||
/* Offsets from zipl fields to zipl banner start */
|
||||
#define ZIPL_TIMEOUT_OFFSET 138
|
||||
#define ZIPL_FLAG_OFFSET 140
|
||||
|
||||
#define TOD_CLOCK_MILLISECOND 0x3e8000
|
||||
|
||||
#define LOW_CORE_EXTERNAL_INT_ADDR 0x86
|
||||
#define CLOCK_COMPARATOR_INT 0X1004
|
||||
|
||||
static uint8_t flag;
|
||||
static uint64_t timeout;
|
||||
|
||||
static inline void enable_clock_int(void)
|
||||
{
|
||||
uint64_t tmp = 0;
|
||||
|
||||
asm volatile(
|
||||
"stctg 0,0,%0\n"
|
||||
"oi 6+%0, 0x8\n"
|
||||
"lctlg 0,0,%0"
|
||||
: : "Q" (tmp) : "memory"
|
||||
);
|
||||
}
|
||||
|
||||
static inline void disable_clock_int(void)
|
||||
{
|
||||
uint64_t tmp = 0;
|
||||
|
||||
asm volatile(
|
||||
"stctg 0,0,%0\n"
|
||||
"ni 6+%0, 0xf7\n"
|
||||
"lctlg 0,0,%0"
|
||||
: : "Q" (tmp) : "memory"
|
||||
);
|
||||
}
|
||||
|
||||
static inline void set_clock_comparator(uint64_t time)
|
||||
{
|
||||
asm volatile("sckc %0" : : "Q" (time));
|
||||
}
|
||||
|
||||
static inline bool check_clock_int(void)
|
||||
{
|
||||
uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR;
|
||||
|
||||
consume_sclp_int();
|
||||
|
||||
return *code == CLOCK_COMPARATOR_INT;
|
||||
}
|
||||
|
||||
static int read_prompt(char *buf, size_t len)
|
||||
{
|
||||
char inp[2] = {};
|
||||
uint8_t idx = 0;
|
||||
uint64_t time;
|
||||
|
||||
if (timeout) {
|
||||
time = get_clock() + timeout * TOD_CLOCK_MILLISECOND;
|
||||
set_clock_comparator(time);
|
||||
enable_clock_int();
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
while (!check_clock_int()) {
|
||||
|
||||
sclp_read(inp, 1); /* Process only one character at a time */
|
||||
|
||||
switch (inp[0]) {
|
||||
case KEYCODE_NO_INP:
|
||||
case KEYCODE_ESCAPE:
|
||||
continue;
|
||||
case KEYCODE_BACKSP:
|
||||
if (idx > 0) {
|
||||
buf[--idx] = 0;
|
||||
sclp_print("\b \b");
|
||||
}
|
||||
continue;
|
||||
case KEYCODE_ENTER:
|
||||
disable_clock_int();
|
||||
return idx;
|
||||
default:
|
||||
/* Echo input and add to buffer */
|
||||
if (idx < len) {
|
||||
buf[idx++] = inp[0];
|
||||
sclp_print(inp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disable_clock_int();
|
||||
*buf = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_index(void)
|
||||
{
|
||||
char buf[11];
|
||||
int len;
|
||||
int i;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII);
|
||||
|
||||
len = read_prompt(buf, sizeof(buf) - 1);
|
||||
|
||||
sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
|
||||
|
||||
/* If no input, boot default */
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for erroneous input */
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!isdigit(buf[i])) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return atoui(buf);
|
||||
}
|
||||
|
||||
static void boot_menu_prompt(bool retry)
|
||||
{
|
||||
char tmp[11];
|
||||
|
||||
if (retry) {
|
||||
sclp_print("\nError: undefined configuration"
|
||||
"\nPlease choose:\n");
|
||||
} else if (timeout > 0) {
|
||||
sclp_print("Please choose (default will boot in ");
|
||||
sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp)));
|
||||
sclp_print(" seconds):\n");
|
||||
} else {
|
||||
sclp_print("Please choose:\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int get_boot_index(int entries)
|
||||
{
|
||||
int boot_index;
|
||||
bool retry = false;
|
||||
char tmp[5];
|
||||
|
||||
do {
|
||||
boot_menu_prompt(retry);
|
||||
boot_index = get_index();
|
||||
retry = true;
|
||||
} while (boot_index < 0 || boot_index >= entries);
|
||||
|
||||
sclp_print("\nBooting entry #");
|
||||
sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
|
||||
|
||||
return boot_index;
|
||||
}
|
||||
|
||||
static void zipl_println(const char *data, size_t len)
|
||||
{
|
||||
char buf[len + 2];
|
||||
|
||||
ebcdic_to_ascii(data, buf, len);
|
||||
buf[len] = '\n';
|
||||
buf[len + 1] = '\0';
|
||||
|
||||
sclp_print(buf);
|
||||
}
|
||||
|
||||
int menu_get_zipl_boot_index(const char *menu_data)
|
||||
{
|
||||
size_t len;
|
||||
int entries;
|
||||
uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
|
||||
uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
|
||||
|
||||
if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
|
||||
if (!zipl_flag) {
|
||||
return 0; /* Boot default */
|
||||
}
|
||||
/* zipl stores timeout as seconds */
|
||||
timeout = zipl_timeout * 1000;
|
||||
}
|
||||
|
||||
/* Print and count all menu items, including the banner */
|
||||
for (entries = 0; *menu_data; entries++) {
|
||||
len = strlen(menu_data);
|
||||
zipl_println(menu_data, len);
|
||||
menu_data += len + 1;
|
||||
|
||||
if (entries < 2) {
|
||||
sclp_print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sclp_print("\n");
|
||||
return get_boot_index(entries - 1); /* subtract 1 to exclude banner */
|
||||
}
|
||||
|
||||
|
||||
int menu_get_enum_boot_index(int entries)
|
||||
{
|
||||
char tmp[4];
|
||||
|
||||
sclp_print("s390x Enumerated Boot Menu.\n\n");
|
||||
|
||||
sclp_print(uitoa(entries, tmp, sizeof(tmp)));
|
||||
sclp_print(" entries detected. Select from boot index 0 to ");
|
||||
sclp_print(uitoa(entries - 1, tmp, sizeof(tmp)));
|
||||
sclp_print(".\n\n");
|
||||
|
||||
return get_boot_index(entries);
|
||||
}
|
||||
|
||||
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
|
||||
{
|
||||
flag = boot_menu_flag;
|
||||
timeout = boot_menu_timeout;
|
||||
}
|
||||
|
||||
bool menu_is_enabled_zipl(void)
|
||||
{
|
||||
return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
|
||||
}
|
||||
|
||||
bool menu_is_enabled_enum(void)
|
||||
{
|
||||
return flag & QIPL_FLAG_BM_OPTS_CMD;
|
||||
}
|
@ -69,8 +69,10 @@ unsigned int get_loadparm_index(void);
|
||||
|
||||
/* sclp.c */
|
||||
void sclp_print(const char *string);
|
||||
void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
|
||||
void sclp_setup(void);
|
||||
void sclp_get_loadparm_ascii(char *loadparm);
|
||||
int sclp_read(char *str, size_t count);
|
||||
|
||||
/* virtio.c */
|
||||
unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
|
||||
@ -79,11 +81,19 @@ bool virtio_is_supported(SubChannelId schid);
|
||||
void virtio_blk_setup_device(SubChannelId schid);
|
||||
int virtio_read(ulong sector, void *load_addr);
|
||||
int enable_mss_facility(void);
|
||||
u64 get_clock(void);
|
||||
ulong get_second(void);
|
||||
|
||||
/* bootmap.c */
|
||||
void zipl_load(void);
|
||||
|
||||
/* menu.c */
|
||||
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout);
|
||||
int menu_get_zipl_boot_index(const char *menu_data);
|
||||
bool menu_is_enabled_zipl(void);
|
||||
int menu_get_enum_boot_index(int entries);
|
||||
bool menu_is_enabled_enum(void);
|
||||
|
||||
static inline void fill_hex(char *out, unsigned char val)
|
||||
{
|
||||
const char hex[] = "0123456789abcdef";
|
||||
|
@ -46,31 +46,21 @@ static int sclp_service_call(unsigned int command, void *sccb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sclp_set_write_mask(void)
|
||||
void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask)
|
||||
{
|
||||
WriteEventMask *sccb = (void *)_sccb;
|
||||
|
||||
sccb->h.length = sizeof(WriteEventMask);
|
||||
sccb->mask_length = sizeof(unsigned int);
|
||||
sccb->receive_mask = SCLP_EVENT_MASK_MSG_ASCII;
|
||||
sccb->cp_receive_mask = SCLP_EVENT_MASK_MSG_ASCII;
|
||||
sccb->send_mask = SCLP_EVENT_MASK_MSG_ASCII;
|
||||
sccb->cp_send_mask = SCLP_EVENT_MASK_MSG_ASCII;
|
||||
sccb->cp_receive_mask = receive_mask;
|
||||
sccb->cp_send_mask = send_mask;
|
||||
|
||||
sclp_service_call(SCLP_CMD_WRITE_EVENT_MASK, sccb);
|
||||
}
|
||||
|
||||
void sclp_setup(void)
|
||||
{
|
||||
sclp_set_write_mask();
|
||||
}
|
||||
|
||||
static int _strlen(const char *str)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; *str; i++)
|
||||
str++;
|
||||
return i;
|
||||
sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
|
||||
}
|
||||
|
||||
long write(int fd, const void *str, size_t len)
|
||||
@ -113,7 +103,7 @@ long write(int fd, const void *str, size_t len)
|
||||
|
||||
void sclp_print(const char *str)
|
||||
{
|
||||
write(1, str, _strlen(str));
|
||||
write(1, str, strlen(str));
|
||||
}
|
||||
|
||||
void sclp_get_loadparm_ascii(char *loadparm)
|
||||
@ -127,3 +117,22 @@ void sclp_get_loadparm_ascii(char *loadparm)
|
||||
ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8);
|
||||
}
|
||||
}
|
||||
|
||||
int sclp_read(char *str, size_t count)
|
||||
{
|
||||
ReadEventData *sccb = (void *)_sccb;
|
||||
char *buf = (char *)(&sccb->ebh) + 7;
|
||||
|
||||
/* If count exceeds max buffer size, then restrict it to the max size */
|
||||
if (count > SCCB_SIZE - 8) {
|
||||
count = SCCB_SIZE - 8;
|
||||
}
|
||||
|
||||
sccb->h.length = SCCB_SIZE;
|
||||
sccb->h.function_code = SCLP_UNCONDITIONAL_READ;
|
||||
|
||||
sclp_service_call(SCLP_CMD_READ_EVENT_DATA, sccb);
|
||||
memcpy(str, buf, count);
|
||||
|
||||
return sccb->ebh.length - 7;
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
static u64 get_clock(void)
|
||||
u64 get_clock(void)
|
||||
{
|
||||
u64 r;
|
||||
|
||||
|
BIN
pc-bios/s390-netboot.img
Executable file → Normal file
BIN
pc-bios/s390-netboot.img
Executable file → Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user