aboutsummaryrefslogtreecommitdiff
path: root/pc-bios/s390-ccw/jump2ipl.c
diff options
context:
space:
mode:
authorThomas Huth <thuth@redhat.com>2018-04-20 11:30:42 +0200
committerThomas Huth <thuth@redhat.com>2018-05-02 11:27:14 +0200
commit9a848adf45d6732e62551decb3c0255173090767 (patch)
treeaab37fe0d824198aa9777e3df7dccdb3aca0779d /pc-bios/s390-ccw/jump2ipl.c
parent0c18822953011ec0a3038c8a5eca1803b72a213e (diff)
pc-bios/s390-ccw/net: Use diag308 to reset machine before jumping to the OS
The netboot firmware so far simply jumped directly into the OS kernel after the download has been completed. This, however, bears the risk that the virtio-net device still might be active in the background and incoming packets are still placed into the buffers - which could destroy memory of the now-running Linux kernel in case it did not take over the device fast enough. Also the SCLP console is not put into a well-defined state here. We should hand over the system in a clean state when jumping into the kernel, so let's use the same mechanism as it's done in the main s390-ccw firmware and reset the machine with diag308 into a clean state before jumping into the OS kernel code. To be able to share the code with the main s390-ccw firmware, the related functions are now extracted from bootmap.c into a new file called jump2ipl.c. Since we now also set the boot device schid at address 184 for the network boot device, this patch also slightly changes the way how we detect the entry points for non-ELF binary images: The code now looks for the "S390EP" magic first and then jumps to 0x10000 in case it has been found. This is necessary for booting from network devices, since the normal kernel code (where the PSW at ddress 0 points to) tries to do a block load from the boot device. This of course fails for a virtio-net device and causes the kernel to abort with a panic-PSW silently. Acked-by: Christian Borntraeger <borntraeger@de.ibm.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
Diffstat (limited to 'pc-bios/s390-ccw/jump2ipl.c')
-rw-r--r--pc-bios/s390-ccw/jump2ipl.c91
1 files changed, 91 insertions, 0 deletions
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);
+}