summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/s390x/ipl.c4
-rw-r--r--pc-bios/s390-ccw.imgbin30520 -> 34568 bytes
-rw-r--r--pc-bios/s390-ccw/Makefile4
-rw-r--r--pc-bios/s390-ccw/bootmap.c79
-rw-r--r--pc-bios/s390-ccw/bootmap.h6
-rw-r--r--pc-bios/s390-ccw/iplb.h3
-rw-r--r--pc-bios/s390-ccw/jump2ipl.c91
-rw-r--r--pc-bios/s390-ccw/libc.c2
-rw-r--r--pc-bios/s390-ccw/libc.h2
-rw-r--r--pc-bios/s390-ccw/main.c14
-rw-r--r--pc-bios/s390-ccw/menu.c58
-rw-r--r--pc-bios/s390-ccw/netboot.mak3
-rw-r--r--pc-bios/s390-ccw/netmain.c168
-rw-r--r--pc-bios/s390-ccw/s390-ccw.h8
-rw-r--r--pc-bios/s390-netboot.imgbin83856 -> 87872 bytes
15 files changed, 307 insertions, 135 deletions
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index fb554ab156..150f6c0582 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -373,6 +373,10 @@ int s390_ipl_set_loadparm(uint8_t *loadparm)
loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]];
}
+ if (i < 8) {
+ memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */
+ }
+
g_free(lp);
return 0;
}
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img
index fdd6809c70..450a076dc0 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differ
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 1712c2d95d..439e3cc9c9 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -9,7 +9,9 @@ $(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 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 += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 9287b7a70f..7aef65ab67 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -29,14 +29,6 @@
/* Scratch space */
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"
"\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;
}
-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)
*/
@@ -297,7 +242,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr,
}
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");
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
@@ -565,6 +510,8 @@ static void ipl_scsi(void)
int program_table_entries = 0;
BootMapTable *prog_table = (void *)sec;
unsigned int loadparm = get_loadparm_index();
+ bool valid_entries[MAX_BOOT_ENTRIES] = {false};
+ size_t i;
/* Grab the MBR */
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");
IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
- while (program_table_entries <= MAX_TABLE_ENTRIES) {
- if (!prog_table->entry[program_table_entries].scsi.blockno) {
- break;
+ for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
+ if (prog_table->entry[i].scsi.blockno) {
+ valid_entries[i] = true;
+ program_table_entries++;
}
- program_table_entries++;
}
debug_print_int("program table entries", program_table_entries);
IPL_assert(program_table_entries != 0, "Empty Program Table");
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);
- 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");
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)),
blks_to_load);
- /* Trying to get PSW at zero address */
- 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);
+ jump_to_low_kernel();
}
static uint32_t find_iso_bc(void)
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 07eb600b00..a085212077 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -57,8 +57,6 @@ typedef union BootMapPointer {
ExtEckdBlockPtr xeckd;
} __attribute__ ((packed)) BootMapPointer;
-#define MAX_TABLE_ENTRIES 30
-
/* aka Program Table */
typedef struct BootMapTable {
uint8_t magic[4];
@@ -355,10 +353,6 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
#define ISO_SECTOR_SIZE 2048
/* El Torito specifies boot image size in 512 byte blocks */
#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
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 5357a36d51..ded20c834e 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -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 rc asm("1") = 0;
+ unsigned long subcode = store ? 6 : 5;
asm volatile ("diag %0,%2,0x308\n"
: "+d" (addr), "+d" (rc)
- : "d" (store ? 6 : 5)
+ : "d" (subcode)
: "memory", "cc");
return rc == 0x01;
}
diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c
new file mode 100644
index 0000000000..266f1502b9
--- /dev/null
+++ b/pc-bios/s390-ccw/jump2ipl.c
@@ -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);
+}
diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c
index 38ea77d7aa..a786566c4c 100644
--- a/pc-bios/s390-ccw/libc.c
+++ b/pc-bios/s390-ccw/libc.c
@@ -63,7 +63,7 @@ uint64_t atoui(const char *str)
*/
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;
IPL_assert(str != NULL, "uitoa: no space allocated to store string");
diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h
index 63ece70c6b..818517ff5d 100644
--- a/pc-bios/s390-ccw/libc.h
+++ b/pc-bios/s390-ccw/libc.h
@@ -12,7 +12,7 @@
#ifndef S390_CCW_LIBC_H
#define S390_CCW_LIBC_H
-typedef long size_t;
+typedef unsigned long size_t;
typedef int bool;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 9d9f8cf4d3..26f9adf84a 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -15,11 +15,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 };
+static char loadparm_str[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
QemuIplParameters qipl;
#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)
/*
@@ -45,7 +45,7 @@ void panic(const char *string)
unsigned int get_loadparm_index(void)
{
- return atoui(loadparm);
+ return atoui(loadparm_str);
}
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)
{
- if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) {
+ if (memcmp(loadparm_str, 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) {
+ if (memcmp(loadparm_str, LOADPARM_EMPTY, 8) != 0) {
return;
}
@@ -116,8 +116,8 @@ static void virtio_setup(void)
*/
enable_mss_facility();
- sclp_get_loadparm_ascii(loadparm);
- memcpy(ldp + 10, loadparm, 8);
+ sclp_get_loadparm_ascii(loadparm_str);
+ memcpy(ldp + 10, loadparm_str, 8);
sclp_print(ldp);
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c
index 96eec81e84..82a4ae6315 100644
--- a/pc-bios/s390-ccw/menu.c
+++ b/pc-bios/s390-ccw/menu.c
@@ -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;
bool retry = false;
@@ -168,7 +168,8 @@ static int get_boot_index(int entries)
boot_menu_prompt(retry);
boot_index = get_index();
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(uitoa(boot_index, tmp, sizeof(tmp)));
@@ -176,7 +177,8 @@ static int get_boot_index(int entries)
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];
@@ -185,12 +187,15 @@ static void zipl_println(const char *data, size_t len)
buf[len + 1] = '\0';
sclp_print(buf);
+
+ return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf);
}
int menu_get_zipl_boot_index(const char *menu_data)
{
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_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;
}
- /* Print and count all menu items, including the banner */
- for (entries = 0; *menu_data; entries++) {
+ /* Print banner */
+ sclp_print("s390-ccw zIPL Boot Menu\n\n");
+ menu_data += strlen(menu_data) + 1;
+
+ /* Print entries */
+ while (*menu_data) {
len = strlen(menu_data);
- zipl_println(menu_data, len);
+ entry = zipl_print_entry(menu_data, len);
menu_data += len + 1;
- if (entries < 2) {
+ valid_entries[entry] = true;
+
+ if (entry == 0) {
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(int entries)
+int menu_get_enum_boot_index(bool *valid_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)));
- sclp_print(" entries detected. Select from boot index 0 to ");
- sclp_print(uitoa(entries - 1, tmp, sizeof(tmp)));
- sclp_print(".\n\n");
+ for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
+ if (valid_entries[i]) {
+ if (i < 10) {
+ 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)
diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index a25d238144..4f64128c6c 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,7 +1,8 @@
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
LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index d86d46b03f..600024155b 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -39,8 +39,12 @@
extern char _start[];
+#define KERNEL_ADDR ((void *)0L)
+#define KERNEL_MAX_SIZE ((long)_start)
+
char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE)));
IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE)));
+static char cfgbuf[2048];
static SubChannelId net_schid = { .one = 1 };
static int ip_version = 4;
@@ -128,17 +132,23 @@ static void seed_rng(uint8_t mac[])
srand(seed);
}
-static int tftp_load(filename_ip_t *fnip, void *buffer, int len,
- unsigned int retries, int ip_vers)
+static int tftp_load(filename_ip_t *fnip, void *buffer, int len)
{
tftp_err_t tftp_err;
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) {
- printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename,
- rc / 1024);
+ if (rc < 0) {
+ /* Make sure that error messages are put into a new line */
+ 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) {
puts("unknown TFTP error");
} else if (rc == -2) {
@@ -199,20 +209,19 @@ static int tftp_load(filename_ip_t *fnip, void *buffer, int len,
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];
int rc;
- memset(&fn_ip, 0, sizeof(filename_ip_t));
+ memset(fn_ip, 0, sizeof(filename_ip_t));
rc = virtio_net_init(mac);
if (rc < 0) {
puts("Could not initialize network device");
return -101;
}
- fn_ip.fd = rc;
+ fn_ip->fd = rc;
printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
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 */
seed_rng(mac);
- rc = dhcp(&fn_ip, DEFAULT_BOOT_RETRIES);
+ rc = dhcp(fn_ip, DEFAULT_BOOT_RETRIES);
if (rc >= 0) {
if (ip_version == 4) {
- set_ipv4_address(fn_ip.own_ip);
+ set_ipv4_address(fn_ip->own_ip);
}
} else {
puts("Could not get IP address");
@@ -232,18 +241,18 @@ static int net_load(char *buffer, int len)
if (ip_version == 4) {
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 >> 8) & 0xFF, fn_ip.own_ip & 0xFF);
+ (fn_ip->own_ip >> 24) & 0xFF, (fn_ip->own_ip >> 16) & 0xFF,
+ (fn_ip->own_ip >> 8) & 0xFF, fn_ip->own_ip & 0xFF);
} else if (ip_version == 6) {
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);
}
if (rc == -2) {
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 >> 8) & 0xFF, fn_ip.server_ip & 0xFF);
+ (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF,
+ (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF);
return -102;
}
if (rc == -4 || rc == -3) {
@@ -251,28 +260,108 @@ static int net_load(char *buffer, int len)
return -107;
}
+ printf(" Using TFTP server: ");
if (ip_version == 4) {
- printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n",
- fn_ip.filename,
- (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF,
- (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF);
+ printf("%d.%d.%d.%d\n",
+ (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF,
+ (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF);
} else if (ip_version == 6) {
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);
}
- /* Do the TFTP load and print error message if necessary */
- rc = tftp_load(&fn_ip, buffer, len, DEFAULT_TFTP_RETRIES, ip_version);
+ if (strlen((char *)fn_ip->filename) > 0) {
+ printf(" Bootfile name: '%s'\n", fn_ip->filename);
+ }
+ return rc;
+}
+
+static void net_release(filename_ip_t *fn_ip)
+{
if (ip_version == 4) {
- dhcp_send_release(fn_ip.fd);
+ 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)
{
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)
{
int i, r;
@@ -344,17 +442,29 @@ static void virtio_setup(void)
void main(void)
{
- int rc;
+ filename_ip_t fn_ip;
+ int rc, fnlen;
sclp_setup();
sclp_print("Network boot starting...\n");
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) {
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");
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index fd18da22c6..9828aa233d 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -87,13 +87,19 @@ ulong get_second(void);
/* bootmap.c */
void zipl_load(void);
+/* jump2ipl.c */
+void jump_to_IPL_code(uint64_t address);
+void jump_to_low_kernel(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);
+int menu_get_enum_boot_index(bool *valid_entries);
bool menu_is_enabled_enum(void);
+#define MAX_BOOT_ENTRIES 31
+
static inline void fill_hex(char *out, unsigned char val)
{
const char hex[] = "0123456789abcdef";
diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img
index 31f3d141cd..ef561efd2e 100644
--- a/pc-bios/s390-netboot.img
+++ b/pc-bios/s390-netboot.img
Binary files differ