s390-ccw firmware updates:
- Improvements to the boot menu (can now handle non-sequential entries) - s390-netboot now resets the machine before jumping into the OS kernel - s390-netboot now supports indirect loading via .INS files - some other minor fixes and clean-ups -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJa6cf5AAoJEC7Z13T+cC211kEQAJhnZMIftoqi3z5lFFu5yWlj 6uTf1rE4yk/qzd5j2GsAEKfixwr4pNX7D6XjeZt0BYw5TKh2KUV8h6rx3Gf0nSr/ /EuXv5CM1Q/Q/YX8Ozz/OAOG9m4G+UXJ4UWJCIlQRoN1m3D5eh8lRYmT/aIBMHOZ iqiCGASCFvdrbsV1/aKxG5wj7Sl4XhFeMQVTKHSpQSOPN4dlHY9Z/9VoHeAKvC+N ia7kH791eKez4FBEkHWxu2iqHWhxKMxinvtg/OCDDjlBd0pJeYOh02wDfbbsNjYJ tE6yX2rUMM0sH8seEdYUgR8iGjS1OiFBocMlj2SUPCVg9ox6btbthMYyFXxiNUKJ plMKZCWtyLnGp5GSWhCulTDA6+7gxuf1yZOMksl0nnGQg/AR16vrVA0T4vKvzSVY gWgkoFWtPPA2e2ivd6w+PmQyNmH91skPH8/Dj8uw3zqRjcTVD97gNCpu2YVrcSOH ubgex8dRWSJEMd6EpnD/Zn24xel/tuPqtPRk9jW9aSUKpyTyOTaugdMu5B8Jp0fp pt+acvF29FLvZrXmoq7eYq2pzfYOMskfguMJ/2NxB80G9FUBN53cvx89mXOAZDtu BLMa5wRnI1RF6DI5o5u/gDC47vXRtaq+v3EPcu2PU5rd5NPeomvS6GHFB4zdOC/x qIb3PN/Mph9Qz2aGEId5 =HacQ -----END PGP SIGNATURE----- Merge tag 'tags/s390x-2018-05-02' into staging s390-ccw firmware updates: - Improvements to the boot menu (can now handle non-sequential entries) - s390-netboot now resets the machine before jumping into the OS kernel - s390-netboot now supports indirect loading via .INS files - some other minor fixes and clean-ups # gpg: Signature made Wed 02 May 2018 04:15:21 PM CEST # 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/s390x-2018-05-02': pc-bios/s390: Update firmware images s390-ccw: force diag 308 subcode to unsigned long pc-bios/s390-ccw/net: Add support for .INS config files pc-bios/s390-ccw/net: Use diag308 to reset machine before jumping to the OS pc-bios/s390-ccw/net: Split up net_load() into init, load and release parts pc-bios/s390-ccw: fix non-sequential boot entries (enum) pc-bios/s390-ccw: fix non-sequential boot entries (eckd) pc-bios/s390-ccw: fix loadparm initialization and int conversion pc-bios/s390-ccw: rename MAX_TABLE_ENTRIES to MAX_BOOT_ENTRIES pc-bios/s390-ccw: size_t should be unsigned Signed-off-by: Cornelia Huck <cohuck@redhat.com>
This commit is contained in:
commit
532cd4b067
@ -373,6 +373,10 @@ int s390_ipl_set_loadparm(uint8_t *loadparm)
|
|||||||
loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]];
|
loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i < 8) {
|
||||||
|
memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */
|
||||||
|
}
|
||||||
|
|
||||||
g_free(lp);
|
g_free(lp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -9,7 +9,9 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
|
|||||||
|
|
||||||
.PHONY : all clean build-all
|
.PHONY : all clean build-all
|
||||||
|
|
||||||
OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o menu.o
|
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \
|
||||||
|
virtio.o virtio-scsi.o virtio-blkdev.o libc.o
|
||||||
|
|
||||||
QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
|
QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
|
||||||
QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
|
QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
|
||||||
QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
|
QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
|
||||||
|
@ -29,14 +29,6 @@
|
|||||||
/* Scratch space */
|
/* Scratch space */
|
||||||
static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE)));
|
static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE)));
|
||||||
|
|
||||||
typedef struct ResetInfo {
|
|
||||||
uint32_t ipl_mask;
|
|
||||||
uint32_t ipl_addr;
|
|
||||||
uint32_t ipl_continue;
|
|
||||||
} ResetInfo;
|
|
||||||
|
|
||||||
static ResetInfo save;
|
|
||||||
|
|
||||||
const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
|
const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
|
||||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||||
|
|
||||||
@ -57,53 +49,6 @@ static inline bool is_iso_vd_valid(IsoVolDesc *vd)
|
|||||||
vd->type <= VOL_DESC_TYPE_PARTITION;
|
vd->type <= VOL_DESC_TYPE_PARTITION;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jump_to_IPL_2(void)
|
|
||||||
{
|
|
||||||
ResetInfo *current = 0;
|
|
||||||
|
|
||||||
void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue;
|
|
||||||
*current = save;
|
|
||||||
ipl(); /* should not return */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void jump_to_IPL_code(uint64_t address)
|
|
||||||
{
|
|
||||||
/* store the subsystem information _after_ the bootmap was loaded */
|
|
||||||
write_subsystem_identification();
|
|
||||||
|
|
||||||
/* prevent unknown IPL types in the guest */
|
|
||||||
if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
|
|
||||||
iplb.pbt = S390_IPL_TYPE_CCW;
|
|
||||||
set_iplb(&iplb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The IPL PSW is at address 0. We also must not overwrite the
|
|
||||||
* content of non-BIOS memory after we loaded the guest, so we
|
|
||||||
* save the original content and restore it in jump_to_IPL_2.
|
|
||||||
*/
|
|
||||||
ResetInfo *current = 0;
|
|
||||||
|
|
||||||
save = *current;
|
|
||||||
current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2;
|
|
||||||
current->ipl_continue = address & 0x7fffffff;
|
|
||||||
|
|
||||||
debug_print_int("set IPL addr to", current->ipl_continue);
|
|
||||||
|
|
||||||
/* Ensure the guest output starts fresh */
|
|
||||||
sclp_print("\n");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* HACK ALERT.
|
|
||||||
* We use the load normal reset to keep r15 unchanged. jump_to_IPL_2
|
|
||||||
* can then use r15 as its stack pointer.
|
|
||||||
*/
|
|
||||||
asm volatile("lghi 1,1\n\t"
|
|
||||||
"diag 1,1,0x308\n\t"
|
|
||||||
: : : "1", "memory");
|
|
||||||
panic("\n! IPL returns !\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* IPL an ECKD DASD (CDL or LDL/CMS format)
|
* IPL an ECKD DASD (CDL or LDL/CMS format)
|
||||||
*/
|
*/
|
||||||
@ -297,7 +242,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug_print_int("loadparm", loadparm);
|
debug_print_int("loadparm", loadparm);
|
||||||
IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than"
|
IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than"
|
||||||
" maximum number of boot entries allowed");
|
" maximum number of boot entries allowed");
|
||||||
|
|
||||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
@ -565,6 +510,8 @@ static void ipl_scsi(void)
|
|||||||
int program_table_entries = 0;
|
int program_table_entries = 0;
|
||||||
BootMapTable *prog_table = (void *)sec;
|
BootMapTable *prog_table = (void *)sec;
|
||||||
unsigned int loadparm = get_loadparm_index();
|
unsigned int loadparm = get_loadparm_index();
|
||||||
|
bool valid_entries[MAX_BOOT_ENTRIES] = {false};
|
||||||
|
size_t i;
|
||||||
|
|
||||||
/* Grab the MBR */
|
/* Grab the MBR */
|
||||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||||
@ -585,22 +532,22 @@ static void ipl_scsi(void)
|
|||||||
read_block(mbr->pt.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");
|
IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
|
||||||
|
|
||||||
while (program_table_entries <= MAX_TABLE_ENTRIES) {
|
for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
|
||||||
if (!prog_table->entry[program_table_entries].scsi.blockno) {
|
if (prog_table->entry[i].scsi.blockno) {
|
||||||
break;
|
valid_entries[i] = true;
|
||||||
|
program_table_entries++;
|
||||||
}
|
}
|
||||||
program_table_entries++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print_int("program table entries", program_table_entries);
|
debug_print_int("program table entries", program_table_entries);
|
||||||
IPL_assert(program_table_entries != 0, "Empty Program Table");
|
IPL_assert(program_table_entries != 0, "Empty Program Table");
|
||||||
|
|
||||||
if (menu_is_enabled_enum()) {
|
if (menu_is_enabled_enum()) {
|
||||||
loadparm = menu_get_enum_boot_index(program_table_entries);
|
loadparm = menu_get_enum_boot_index(valid_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print_int("loadparm", loadparm);
|
debug_print_int("loadparm", loadparm);
|
||||||
IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than"
|
IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than"
|
||||||
" maximum number of boot entries allowed");
|
" maximum number of boot entries allowed");
|
||||||
|
|
||||||
zipl_run(&prog_table->entry[loadparm].scsi); /* no return */
|
zipl_run(&prog_table->entry[loadparm].scsi); /* no return */
|
||||||
@ -727,13 +674,7 @@ static void load_iso_bc_entry(IsoBcSection *load)
|
|||||||
(void *)((uint64_t)bswap16(s.load_segment)),
|
(void *)((uint64_t)bswap16(s.load_segment)),
|
||||||
blks_to_load);
|
blks_to_load);
|
||||||
|
|
||||||
/* Trying to get PSW at zero address */
|
jump_to_low_kernel();
|
||||||
if (*((uint64_t *)0) & IPL_PSW_MASK) {
|
|
||||||
jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try default linux start address */
|
|
||||||
jump_to_IPL_code(KERN_IMAGE_START);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t find_iso_bc(void)
|
static uint32_t find_iso_bc(void)
|
||||||
|
@ -57,8 +57,6 @@ typedef union BootMapPointer {
|
|||||||
ExtEckdBlockPtr xeckd;
|
ExtEckdBlockPtr xeckd;
|
||||||
} __attribute__ ((packed)) BootMapPointer;
|
} __attribute__ ((packed)) BootMapPointer;
|
||||||
|
|
||||||
#define MAX_TABLE_ENTRIES 30
|
|
||||||
|
|
||||||
/* aka Program Table */
|
/* aka Program Table */
|
||||||
typedef struct BootMapTable {
|
typedef struct BootMapTable {
|
||||||
uint8_t magic[4];
|
uint8_t magic[4];
|
||||||
@ -355,10 +353,6 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
|
|||||||
#define ISO_SECTOR_SIZE 2048
|
#define ISO_SECTOR_SIZE 2048
|
||||||
/* El Torito specifies boot image size in 512 byte blocks */
|
/* El Torito specifies boot image size in 512 byte blocks */
|
||||||
#define ET_SECTOR_SHIFT 2
|
#define ET_SECTOR_SHIFT 2
|
||||||
#define KERN_IMAGE_START 0x010000UL
|
|
||||||
#define PSW_MASK_64 0x0000000100000000ULL
|
|
||||||
#define PSW_MASK_32 0x0000000080000000ULL
|
|
||||||
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
|
|
||||||
|
|
||||||
#define ISO_PRIMARY_VD_SECTOR 16
|
#define ISO_PRIMARY_VD_SECTOR 16
|
||||||
|
|
||||||
|
@ -101,10 +101,11 @@ static inline bool manage_iplb(IplParameterBlock *iplb, bool store)
|
|||||||
{
|
{
|
||||||
register unsigned long addr asm("0") = (unsigned long) iplb;
|
register unsigned long addr asm("0") = (unsigned long) iplb;
|
||||||
register unsigned long rc asm("1") = 0;
|
register unsigned long rc asm("1") = 0;
|
||||||
|
unsigned long subcode = store ? 6 : 5;
|
||||||
|
|
||||||
asm volatile ("diag %0,%2,0x308\n"
|
asm volatile ("diag %0,%2,0x308\n"
|
||||||
: "+d" (addr), "+d" (rc)
|
: "+d" (addr), "+d" (rc)
|
||||||
: "d" (store ? 6 : 5)
|
: "d" (subcode)
|
||||||
: "memory", "cc");
|
: "memory", "cc");
|
||||||
return rc == 0x01;
|
return rc == 0x01;
|
||||||
}
|
}
|
||||||
|
91
pc-bios/s390-ccw/jump2ipl.c
Normal file
91
pc-bios/s390-ccw/jump2ipl.c
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* QEMU s390-ccw firmware - jump to IPL code
|
||||||
|
*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
#define KERN_IMAGE_START 0x010000UL
|
||||||
|
#define PSW_MASK_64 0x0000000100000000ULL
|
||||||
|
#define PSW_MASK_32 0x0000000080000000ULL
|
||||||
|
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
|
||||||
|
|
||||||
|
typedef struct ResetInfo {
|
||||||
|
uint32_t ipl_mask;
|
||||||
|
uint32_t ipl_addr;
|
||||||
|
uint32_t ipl_continue;
|
||||||
|
} ResetInfo;
|
||||||
|
|
||||||
|
static ResetInfo save;
|
||||||
|
|
||||||
|
static void jump_to_IPL_2(void)
|
||||||
|
{
|
||||||
|
ResetInfo *current = 0;
|
||||||
|
|
||||||
|
void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue;
|
||||||
|
*current = save;
|
||||||
|
ipl(); /* should not return */
|
||||||
|
}
|
||||||
|
|
||||||
|
void jump_to_IPL_code(uint64_t address)
|
||||||
|
{
|
||||||
|
/* store the subsystem information _after_ the bootmap was loaded */
|
||||||
|
write_subsystem_identification();
|
||||||
|
|
||||||
|
/* prevent unknown IPL types in the guest */
|
||||||
|
if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
|
||||||
|
iplb.pbt = S390_IPL_TYPE_CCW;
|
||||||
|
set_iplb(&iplb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The IPL PSW is at address 0. We also must not overwrite the
|
||||||
|
* content of non-BIOS memory after we loaded the guest, so we
|
||||||
|
* save the original content and restore it in jump_to_IPL_2.
|
||||||
|
*/
|
||||||
|
ResetInfo *current = 0;
|
||||||
|
|
||||||
|
save = *current;
|
||||||
|
current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2;
|
||||||
|
current->ipl_continue = address & 0x7fffffff;
|
||||||
|
|
||||||
|
debug_print_int("set IPL addr to", current->ipl_continue);
|
||||||
|
|
||||||
|
/* Ensure the guest output starts fresh */
|
||||||
|
sclp_print("\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HACK ALERT.
|
||||||
|
* We use the load normal reset to keep r15 unchanged. jump_to_IPL_2
|
||||||
|
* can then use r15 as its stack pointer.
|
||||||
|
*/
|
||||||
|
asm volatile("lghi 1,1\n\t"
|
||||||
|
"diag 1,1,0x308\n\t"
|
||||||
|
: : : "1", "memory");
|
||||||
|
panic("\n! IPL returns !\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void jump_to_low_kernel(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If it looks like a Linux binary, i.e. there is the "S390EP" magic from
|
||||||
|
* arch/s390/kernel/head.S here, then let's jump to the well-known Linux
|
||||||
|
* kernel start address (when jumping to the PSW-at-zero address instead,
|
||||||
|
* the kernel startup code fails when we booted from a network device).
|
||||||
|
*/
|
||||||
|
if (!memcmp((char *)0x10008, "S390EP", 6)) {
|
||||||
|
jump_to_IPL_code(KERN_IMAGE_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trying to get PSW at zero address */
|
||||||
|
if (*((uint64_t *)0) & IPL_PSW_MASK) {
|
||||||
|
jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No other option left, so use the Linux kernel start address */
|
||||||
|
jump_to_IPL_code(KERN_IMAGE_START);
|
||||||
|
}
|
@ -63,7 +63,7 @@ uint64_t atoui(const char *str)
|
|||||||
*/
|
*/
|
||||||
char *uitoa(uint64_t num, char *str, size_t len)
|
char *uitoa(uint64_t num, char *str, size_t len)
|
||||||
{
|
{
|
||||||
size_t num_idx = 1; /* account for NUL */
|
long num_idx = 1; /* account for NUL */
|
||||||
uint64_t tmp = num;
|
uint64_t tmp = num;
|
||||||
|
|
||||||
IPL_assert(str != NULL, "uitoa: no space allocated to store string");
|
IPL_assert(str != NULL, "uitoa: no space allocated to store string");
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#ifndef S390_CCW_LIBC_H
|
#ifndef S390_CCW_LIBC_H
|
||||||
#define S390_CCW_LIBC_H
|
#define S390_CCW_LIBC_H
|
||||||
|
|
||||||
typedef long size_t;
|
typedef unsigned long size_t;
|
||||||
typedef int bool;
|
typedef int bool;
|
||||||
typedef unsigned char uint8_t;
|
typedef unsigned char uint8_t;
|
||||||
typedef unsigned short uint16_t;
|
typedef unsigned short uint16_t;
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
|
char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
|
||||||
static SubChannelId blk_schid = { .one = 1 };
|
static SubChannelId blk_schid = { .one = 1 };
|
||||||
IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||||
static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
static char loadparm_str[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
QemuIplParameters qipl;
|
QemuIplParameters qipl;
|
||||||
|
|
||||||
#define LOADPARM_PROMPT "PROMPT "
|
#define LOADPARM_PROMPT "PROMPT "
|
||||||
#define LOADPARM_EMPTY "........"
|
#define LOADPARM_EMPTY " "
|
||||||
#define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL)
|
#define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -45,7 +45,7 @@ void panic(const char *string)
|
|||||||
|
|
||||||
unsigned int get_loadparm_index(void)
|
unsigned int get_loadparm_index(void)
|
||||||
{
|
{
|
||||||
return atoui(loadparm);
|
return atoui(loadparm_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool find_dev(Schib *schib, int dev_no)
|
static bool find_dev(Schib *schib, int dev_no)
|
||||||
@ -80,13 +80,13 @@ static bool find_dev(Schib *schib, int dev_no)
|
|||||||
|
|
||||||
static void menu_setup(void)
|
static void menu_setup(void)
|
||||||
{
|
{
|
||||||
if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) {
|
if (memcmp(loadparm_str, LOADPARM_PROMPT, 8) == 0) {
|
||||||
menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0);
|
menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If loadparm was set to any other value, then do not enable menu */
|
/* If loadparm was set to any other value, then do not enable menu */
|
||||||
if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) {
|
if (memcmp(loadparm_str, LOADPARM_EMPTY, 8) != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,8 +116,8 @@ static void virtio_setup(void)
|
|||||||
*/
|
*/
|
||||||
enable_mss_facility();
|
enable_mss_facility();
|
||||||
|
|
||||||
sclp_get_loadparm_ascii(loadparm);
|
sclp_get_loadparm_ascii(loadparm_str);
|
||||||
memcpy(ldp + 10, loadparm, 8);
|
memcpy(ldp + 10, loadparm_str, 8);
|
||||||
sclp_print(ldp);
|
sclp_print(ldp);
|
||||||
|
|
||||||
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
|
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
|
||||||
|
@ -158,7 +158,7 @@ static void boot_menu_prompt(bool retry)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_boot_index(int entries)
|
static int get_boot_index(bool *valid_entries)
|
||||||
{
|
{
|
||||||
int boot_index;
|
int boot_index;
|
||||||
bool retry = false;
|
bool retry = false;
|
||||||
@ -168,7 +168,8 @@ static int get_boot_index(int entries)
|
|||||||
boot_menu_prompt(retry);
|
boot_menu_prompt(retry);
|
||||||
boot_index = get_index();
|
boot_index = get_index();
|
||||||
retry = true;
|
retry = true;
|
||||||
} while (boot_index < 0 || boot_index >= entries);
|
} while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES ||
|
||||||
|
!valid_entries[boot_index]);
|
||||||
|
|
||||||
sclp_print("\nBooting entry #");
|
sclp_print("\nBooting entry #");
|
||||||
sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
|
sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
|
||||||
@ -176,7 +177,8 @@ static int get_boot_index(int entries)
|
|||||||
return boot_index;
|
return boot_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zipl_println(const char *data, size_t len)
|
/* Returns the entry number that was printed */
|
||||||
|
static int zipl_print_entry(const char *data, size_t len)
|
||||||
{
|
{
|
||||||
char buf[len + 2];
|
char buf[len + 2];
|
||||||
|
|
||||||
@ -185,12 +187,15 @@ static void zipl_println(const char *data, size_t len)
|
|||||||
buf[len + 1] = '\0';
|
buf[len + 1] = '\0';
|
||||||
|
|
||||||
sclp_print(buf);
|
sclp_print(buf);
|
||||||
|
|
||||||
|
return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int menu_get_zipl_boot_index(const char *menu_data)
|
int menu_get_zipl_boot_index(const char *menu_data)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
int entries;
|
int entry;
|
||||||
|
bool valid_entries[MAX_BOOT_ENTRIES] = {false};
|
||||||
uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
|
uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
|
||||||
uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
|
uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
|
||||||
|
|
||||||
@ -202,34 +207,51 @@ int menu_get_zipl_boot_index(const char *menu_data)
|
|||||||
timeout = zipl_timeout * 1000;
|
timeout = zipl_timeout * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print and count all menu items, including the banner */
|
/* Print banner */
|
||||||
for (entries = 0; *menu_data; entries++) {
|
sclp_print("s390-ccw zIPL Boot Menu\n\n");
|
||||||
|
menu_data += strlen(menu_data) + 1;
|
||||||
|
|
||||||
|
/* Print entries */
|
||||||
|
while (*menu_data) {
|
||||||
len = strlen(menu_data);
|
len = strlen(menu_data);
|
||||||
zipl_println(menu_data, len);
|
entry = zipl_print_entry(menu_data, len);
|
||||||
menu_data += len + 1;
|
menu_data += len + 1;
|
||||||
|
|
||||||
if (entries < 2) {
|
valid_entries[entry] = true;
|
||||||
|
|
||||||
|
if (entry == 0) {
|
||||||
sclp_print("\n");
|
sclp_print("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sclp_print("\n");
|
sclp_print("\n");
|
||||||
return get_boot_index(entries - 1); /* subtract 1 to exclude banner */
|
return get_boot_index(valid_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int menu_get_enum_boot_index(bool *valid_entries)
|
||||||
int menu_get_enum_boot_index(int entries)
|
|
||||||
{
|
{
|
||||||
char tmp[4];
|
char tmp[3];
|
||||||
|
int i;
|
||||||
|
|
||||||
sclp_print("s390x Enumerated Boot Menu.\n\n");
|
sclp_print("s390-ccw Enumerated Boot Menu.\n\n");
|
||||||
|
|
||||||
sclp_print(uitoa(entries, tmp, sizeof(tmp)));
|
for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
|
||||||
sclp_print(" entries detected. Select from boot index 0 to ");
|
if (valid_entries[i]) {
|
||||||
sclp_print(uitoa(entries - 1, tmp, sizeof(tmp)));
|
if (i < 10) {
|
||||||
sclp_print(".\n\n");
|
sclp_print(" ");
|
||||||
|
}
|
||||||
|
sclp_print("[");
|
||||||
|
sclp_print(uitoa(i, tmp, sizeof(tmp)));
|
||||||
|
sclp_print("]");
|
||||||
|
if (i == 0) {
|
||||||
|
sclp_print(" default\n");
|
||||||
|
}
|
||||||
|
sclp_print("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return get_boot_index(entries);
|
sclp_print("\n");
|
||||||
|
return get_boot_index(valid_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
|
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
SLOF_DIR := $(SRC_PATH)/roms/SLOF
|
SLOF_DIR := $(SRC_PATH)/roms/SLOF
|
||||||
|
|
||||||
NETOBJS := start.o sclp.o virtio.o virtio-net.o netmain.o libnet.a libc.a
|
NETOBJS := start.o sclp.o virtio.o virtio-net.o jump2ipl.o netmain.o \
|
||||||
|
libnet.a libc.a
|
||||||
|
|
||||||
LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
|
LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
|
||||||
LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
|
LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
|
||||||
|
@ -39,8 +39,12 @@
|
|||||||
|
|
||||||
extern char _start[];
|
extern char _start[];
|
||||||
|
|
||||||
|
#define KERNEL_ADDR ((void *)0L)
|
||||||
|
#define KERNEL_MAX_SIZE ((long)_start)
|
||||||
|
|
||||||
char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE)));
|
char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE)));
|
||||||
IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE)));
|
IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE)));
|
||||||
|
static char cfgbuf[2048];
|
||||||
|
|
||||||
static SubChannelId net_schid = { .one = 1 };
|
static SubChannelId net_schid = { .one = 1 };
|
||||||
static int ip_version = 4;
|
static int ip_version = 4;
|
||||||
@ -128,17 +132,23 @@ static void seed_rng(uint8_t mac[])
|
|||||||
srand(seed);
|
srand(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tftp_load(filename_ip_t *fnip, void *buffer, int len,
|
static int tftp_load(filename_ip_t *fnip, void *buffer, int len)
|
||||||
unsigned int retries, int ip_vers)
|
|
||||||
{
|
{
|
||||||
tftp_err_t tftp_err;
|
tftp_err_t tftp_err;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = tftp(fnip, buffer, len, retries, &tftp_err, 1, 1428, ip_vers);
|
rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err, 1, 1428,
|
||||||
|
ip_version);
|
||||||
|
|
||||||
if (rc > 0) {
|
if (rc < 0) {
|
||||||
printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename,
|
/* Make sure that error messages are put into a new line */
|
||||||
rc / 1024);
|
printf("\n ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc > 1024) {
|
||||||
|
printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, rc / 1024);
|
||||||
|
} else if (rc > 0) {
|
||||||
|
printf(" TFTP: Received %s (%d Bytes)\n", fnip->filename, rc);
|
||||||
} else if (rc == -1) {
|
} else if (rc == -1) {
|
||||||
puts("unknown TFTP error");
|
puts("unknown TFTP error");
|
||||||
} else if (rc == -2) {
|
} else if (rc == -2) {
|
||||||
@ -199,20 +209,19 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int net_load(char *buffer, int len)
|
static int net_init(filename_ip_t *fn_ip)
|
||||||
{
|
{
|
||||||
filename_ip_t fn_ip;
|
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
memset(&fn_ip, 0, sizeof(filename_ip_t));
|
memset(fn_ip, 0, sizeof(filename_ip_t));
|
||||||
|
|
||||||
rc = virtio_net_init(mac);
|
rc = virtio_net_init(mac);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
puts("Could not initialize network device");
|
puts("Could not initialize network device");
|
||||||
return -101;
|
return -101;
|
||||||
}
|
}
|
||||||
fn_ip.fd = rc;
|
fn_ip->fd = rc;
|
||||||
|
|
||||||
printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
@ -220,10 +229,10 @@ static int net_load(char *buffer, int len)
|
|||||||
set_mac_address(mac); /* init ethernet layer */
|
set_mac_address(mac); /* init ethernet layer */
|
||||||
seed_rng(mac);
|
seed_rng(mac);
|
||||||
|
|
||||||
rc = dhcp(&fn_ip, DEFAULT_BOOT_RETRIES);
|
rc = dhcp(fn_ip, DEFAULT_BOOT_RETRIES);
|
||||||
if (rc >= 0) {
|
if (rc >= 0) {
|
||||||
if (ip_version == 4) {
|
if (ip_version == 4) {
|
||||||
set_ipv4_address(fn_ip.own_ip);
|
set_ipv4_address(fn_ip->own_ip);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
puts("Could not get IP address");
|
puts("Could not get IP address");
|
||||||
@ -232,18 +241,18 @@ static int net_load(char *buffer, int len)
|
|||||||
|
|
||||||
if (ip_version == 4) {
|
if (ip_version == 4) {
|
||||||
printf(" Using IPv4 address: %d.%d.%d.%d\n",
|
printf(" Using IPv4 address: %d.%d.%d.%d\n",
|
||||||
(fn_ip.own_ip >> 24) & 0xFF, (fn_ip.own_ip >> 16) & 0xFF,
|
(fn_ip->own_ip >> 24) & 0xFF, (fn_ip->own_ip >> 16) & 0xFF,
|
||||||
(fn_ip.own_ip >> 8) & 0xFF, fn_ip.own_ip & 0xFF);
|
(fn_ip->own_ip >> 8) & 0xFF, fn_ip->own_ip & 0xFF);
|
||||||
} else if (ip_version == 6) {
|
} else if (ip_version == 6) {
|
||||||
char ip6_str[40];
|
char ip6_str[40];
|
||||||
ipv6_to_str(fn_ip.own_ip6.addr, ip6_str);
|
ipv6_to_str(fn_ip->own_ip6.addr, ip6_str);
|
||||||
printf(" Using IPv6 address: %s\n", ip6_str);
|
printf(" Using IPv6 address: %s\n", ip6_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc == -2) {
|
if (rc == -2) {
|
||||||
printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n",
|
printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n",
|
||||||
(fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF,
|
(fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF,
|
||||||
(fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF);
|
(fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF);
|
||||||
return -102;
|
return -102;
|
||||||
}
|
}
|
||||||
if (rc == -4 || rc == -3) {
|
if (rc == -4 || rc == -3) {
|
||||||
@ -251,28 +260,108 @@ static int net_load(char *buffer, int len)
|
|||||||
return -107;
|
return -107;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf(" Using TFTP server: ");
|
||||||
if (ip_version == 4) {
|
if (ip_version == 4) {
|
||||||
printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n",
|
printf("%d.%d.%d.%d\n",
|
||||||
fn_ip.filename,
|
(fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF,
|
||||||
(fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF,
|
(fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF);
|
||||||
(fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF);
|
|
||||||
} else if (ip_version == 6) {
|
} else if (ip_version == 6) {
|
||||||
char ip6_str[40];
|
char ip6_str[40];
|
||||||
printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename);
|
ipv6_to_str(fn_ip->server_ip6.addr, ip6_str);
|
||||||
ipv6_to_str(fn_ip.server_ip6.addr, ip6_str);
|
|
||||||
printf("%s\n", ip6_str);
|
printf("%s\n", ip6_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do the TFTP load and print error message if necessary */
|
if (strlen((char *)fn_ip->filename) > 0) {
|
||||||
rc = tftp_load(&fn_ip, buffer, len, DEFAULT_TFTP_RETRIES, ip_version);
|
printf(" Bootfile name: '%s'\n", fn_ip->filename);
|
||||||
|
|
||||||
if (ip_version == 4) {
|
|
||||||
dhcp_send_release(fn_ip.fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void net_release(filename_ip_t *fn_ip)
|
||||||
|
{
|
||||||
|
if (ip_version == 4) {
|
||||||
|
dhcp_send_release(fn_ip->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load via information from a .INS file (which can be found on CD-ROMs
|
||||||
|
* for example)
|
||||||
|
*/
|
||||||
|
static int handle_ins_cfg(filename_ip_t *fn_ip, char *cfg, int cfgsize)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
int rc = -1, llen;
|
||||||
|
void *destaddr;
|
||||||
|
char *insbuf = cfg;
|
||||||
|
|
||||||
|
ptr = strchr(insbuf, '\n');
|
||||||
|
if (!ptr) {
|
||||||
|
puts("Does not seem to be a valid .INS file");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = 0;
|
||||||
|
printf("\nParsing .INS file:\n %s\n", &insbuf[2]);
|
||||||
|
|
||||||
|
insbuf = ptr + 1;
|
||||||
|
while (*insbuf && insbuf < cfg + cfgsize) {
|
||||||
|
ptr = strchr(insbuf, '\n');
|
||||||
|
if (ptr) {
|
||||||
|
*ptr = 0;
|
||||||
|
}
|
||||||
|
llen = strlen(insbuf);
|
||||||
|
if (!llen) {
|
||||||
|
insbuf = ptr + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ptr = strchr(insbuf, ' ');
|
||||||
|
if (!ptr) {
|
||||||
|
puts("Missing space separator in .INS file");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*ptr = 0;
|
||||||
|
strncpy((char *)fn_ip->filename, insbuf, sizeof(fn_ip->filename));
|
||||||
|
destaddr = (char *)atol(ptr + 1);
|
||||||
|
rc = tftp_load(fn_ip, destaddr, (long)_start - (long)destaddr);
|
||||||
|
if (rc <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
insbuf += llen + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int net_try_direct_tftp_load(filename_ip_t *fn_ip)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
void *loadaddr = (void *)0x2000; /* Load right after the low-core */
|
||||||
|
|
||||||
|
rc = tftp_load(fn_ip, loadaddr, KERNEL_MAX_SIZE - (long)loadaddr);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
} else if (rc < 8) {
|
||||||
|
printf("'%s' is too small (%i bytes only).\n", fn_ip->filename, rc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check whether it is a configuration file instead of a kernel */
|
||||||
|
if (rc < sizeof(cfgbuf) - 1) {
|
||||||
|
memcpy(cfgbuf, loadaddr, rc);
|
||||||
|
cfgbuf[rc] = 0; /* Make sure that it is NUL-terminated */
|
||||||
|
if (!strncmp("* ", cfgbuf, 2)) {
|
||||||
|
return handle_ins_cfg(fn_ip, cfgbuf, rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move kernel to right location */
|
||||||
|
memmove(KERNEL_ADDR, loadaddr, rc);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
void panic(const char *string)
|
void panic(const char *string)
|
||||||
{
|
{
|
||||||
sclp_print(string);
|
sclp_print(string);
|
||||||
@ -281,6 +370,15 @@ void panic(const char *string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write_subsystem_identification(void)
|
||||||
|
{
|
||||||
|
SubChannelId *schid = (SubChannelId *) 184;
|
||||||
|
uint32_t *zeroes = (uint32_t *) 188;
|
||||||
|
|
||||||
|
*schid = net_schid;
|
||||||
|
*zeroes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool find_net_dev(Schib *schib, int dev_no)
|
static bool find_net_dev(Schib *schib, int dev_no)
|
||||||
{
|
{
|
||||||
int i, r;
|
int i, r;
|
||||||
@ -344,17 +442,29 @@ static void virtio_setup(void)
|
|||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
int rc;
|
filename_ip_t fn_ip;
|
||||||
|
int rc, fnlen;
|
||||||
|
|
||||||
sclp_setup();
|
sclp_setup();
|
||||||
sclp_print("Network boot starting...\n");
|
sclp_print("Network boot starting...\n");
|
||||||
|
|
||||||
virtio_setup();
|
virtio_setup();
|
||||||
|
|
||||||
rc = net_load(NULL, (long)_start);
|
rc = net_init(&fn_ip);
|
||||||
|
if (rc) {
|
||||||
|
panic("Network initialization failed. Halting.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fnlen = strlen((char *)fn_ip.filename);
|
||||||
|
if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') {
|
||||||
|
rc = net_try_direct_tftp_load(&fn_ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
net_release(&fn_ip);
|
||||||
|
|
||||||
if (rc > 0) {
|
if (rc > 0) {
|
||||||
sclp_print("Network loading done, starting kernel...\n");
|
sclp_print("Network loading done, starting kernel...\n");
|
||||||
asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory");
|
jump_to_low_kernel();
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("Failed to load OS from network\n");
|
panic("Failed to load OS from network\n");
|
||||||
|
@ -87,13 +87,19 @@ ulong get_second(void);
|
|||||||
/* bootmap.c */
|
/* bootmap.c */
|
||||||
void zipl_load(void);
|
void zipl_load(void);
|
||||||
|
|
||||||
|
/* jump2ipl.c */
|
||||||
|
void jump_to_IPL_code(uint64_t address);
|
||||||
|
void jump_to_low_kernel(void);
|
||||||
|
|
||||||
/* menu.c */
|
/* menu.c */
|
||||||
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout);
|
void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout);
|
||||||
int menu_get_zipl_boot_index(const char *menu_data);
|
int menu_get_zipl_boot_index(const char *menu_data);
|
||||||
bool menu_is_enabled_zipl(void);
|
bool menu_is_enabled_zipl(void);
|
||||||
int menu_get_enum_boot_index(int entries);
|
int menu_get_enum_boot_index(bool *valid_entries);
|
||||||
bool menu_is_enabled_enum(void);
|
bool menu_is_enabled_enum(void);
|
||||||
|
|
||||||
|
#define MAX_BOOT_ENTRIES 31
|
||||||
|
|
||||||
static inline void fill_hex(char *out, unsigned char val)
|
static inline void fill_hex(char *out, unsigned char val)
|
||||||
{
|
{
|
||||||
const char hex[] = "0123456789abcdef";
|
const char hex[] = "0123456789abcdef";
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user