.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
/* 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";
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)
*/
(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)
#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
--- /dev/null
+/*
+ * 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);
+}
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
}
}
+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;
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");
/* 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);