19a848adfSThomas Huth /*
29a848adfSThomas Huth * QEMU s390-ccw firmware - jump to IPL code
39a848adfSThomas Huth *
49a848adfSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or (at
59a848adfSThomas Huth * your option) any later version. See the COPYING file in the top-level
69a848adfSThomas Huth * directory.
79a848adfSThomas Huth */
89a848adfSThomas Huth
99f427883SJared Rossi #include <string.h>
109f427883SJared Rossi #include <stdio.h>
119a848adfSThomas Huth #include "s390-ccw.h"
12fe75c657SJanosch Frank #include "s390-arch.h"
139a848adfSThomas Huth
149a848adfSThomas Huth #define KERN_IMAGE_START 0x010000UL
15fe75c657SJanosch Frank #define RESET_PSW_MASK (PSW_MASK_SHORTPSW | PSW_MASK_64)
1642ab98e7SJanosch Frank #define RESET_PSW ((uint64_t)&jump_to_IPL_addr | RESET_PSW_MASK)
179a848adfSThomas Huth
1826e0b96fSJanosch Frank static uint64_t *reset_psw = 0, save_psw, ipl_continue;
199a848adfSThomas Huth
write_reset_psw(uint64_t psw)2042ab98e7SJanosch Frank void write_reset_psw(uint64_t psw)
2142ab98e7SJanosch Frank {
2242ab98e7SJanosch Frank *reset_psw = psw;
2342ab98e7SJanosch Frank }
2442ab98e7SJanosch Frank
jump_to_IPL_addr(void)2526e0b96fSJanosch Frank static void jump_to_IPL_addr(void)
269a848adfSThomas Huth {
2726e0b96fSJanosch Frank __attribute__((noreturn)) void (*ipl)(void) = (void *)ipl_continue;
289a848adfSThomas Huth
2926e0b96fSJanosch Frank /* Restore reset PSW */
3042ab98e7SJanosch Frank write_reset_psw(save_psw);
3126e0b96fSJanosch Frank
3226e0b96fSJanosch Frank ipl();
3326e0b96fSJanosch Frank /* should not return */
349a848adfSThomas Huth }
359a848adfSThomas Huth
jump_to_IPL_code(uint64_t address)360181e237SJared Rossi int jump_to_IPL_code(uint64_t address)
379a848adfSThomas Huth {
389a848adfSThomas Huth /* store the subsystem information _after_ the bootmap was loaded */
399a848adfSThomas Huth write_subsystem_identification();
409bfc04f9SJanosch Frank write_iplb_location();
419a848adfSThomas Huth
42455e3bc3SJared Rossi /*
43455e3bc3SJared Rossi * The IPLB for QEMU SCSI type devices must be rebuilt during re-ipl. The
44455e3bc3SJared Rossi * iplb.devno is set to the boot position of the target SCSI device.
45455e3bc3SJared Rossi */
469a848adfSThomas Huth if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
47455e3bc3SJared Rossi iplb.devno = qipl.index;
48455e3bc3SJared Rossi }
49*f697bed2SJared Rossi
50*f697bed2SJared Rossi if (have_iplb && !set_iplb(&iplb)) {
51*f697bed2SJared Rossi panic("Failed to set IPLB");
529a848adfSThomas Huth }
539a848adfSThomas Huth
549a848adfSThomas Huth /*
559a848adfSThomas Huth * The IPL PSW is at address 0. We also must not overwrite the
569a848adfSThomas Huth * content of non-BIOS memory after we loaded the guest, so we
579a848adfSThomas Huth * save the original content and restore it in jump_to_IPL_2.
589a848adfSThomas Huth */
5942ab98e7SJanosch Frank if (address) {
6026e0b96fSJanosch Frank save_psw = *reset_psw;
6142ab98e7SJanosch Frank write_reset_psw(RESET_PSW);
6226e0b96fSJanosch Frank ipl_continue = address;
6342ab98e7SJanosch Frank }
6442ab98e7SJanosch Frank debug_print_int("set IPL addr to", address ?: *reset_psw & PSW_MASK_SHORT_ADDR);
659a848adfSThomas Huth
669a848adfSThomas Huth /* Ensure the guest output starts fresh */
679f427883SJared Rossi printf("\n");
689a848adfSThomas Huth
699a848adfSThomas Huth /*
709a848adfSThomas Huth * HACK ALERT.
719a848adfSThomas Huth * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2
729a848adfSThomas Huth * can then use r15 as its stack pointer.
739a848adfSThomas Huth */
74052b66e7SThomas Huth asm volatile("lghi %%r1,1\n\t"
75052b66e7SThomas Huth "diag %%r1,%%r1,0x308\n\t"
769a848adfSThomas Huth : : : "1", "memory");
770181e237SJared Rossi puts("IPL code jump failed");
780181e237SJared Rossi return -1;
799a848adfSThomas Huth }
809a848adfSThomas Huth
jump_to_low_kernel(void)819a848adfSThomas Huth void jump_to_low_kernel(void)
829a848adfSThomas Huth {
839a848adfSThomas Huth /*
849a848adfSThomas Huth * If it looks like a Linux binary, i.e. there is the "S390EP" magic from
859a848adfSThomas Huth * arch/s390/kernel/head.S here, then let's jump to the well-known Linux
869a848adfSThomas Huth * kernel start address (when jumping to the PSW-at-zero address instead,
879a848adfSThomas Huth * the kernel startup code fails when we booted from a network device).
889a848adfSThomas Huth */
893d651996SEric Farman if (!memcmp((char *)S390EP, "S390EP", 6)) {
909a848adfSThomas Huth jump_to_IPL_code(KERN_IMAGE_START);
919a848adfSThomas Huth }
929a848adfSThomas Huth
93ff77712aSThomas Huth /* Trying to get PSW at zero address (pointed to by reset_psw) */
94ff77712aSThomas Huth if (*reset_psw & RESET_PSW_MASK) {
9542ab98e7SJanosch Frank /*
9642ab98e7SJanosch Frank * Surely nobody will try running directly from lowcore, so
9742ab98e7SJanosch Frank * let's use 0 as an indication that we want to load the reset
9842ab98e7SJanosch Frank * psw at 0x0 and not jump to the entry.
9942ab98e7SJanosch Frank */
10042ab98e7SJanosch Frank jump_to_IPL_code(0);
1019a848adfSThomas Huth }
1029a848adfSThomas Huth
1039a848adfSThomas Huth /* No other option left, so use the Linux kernel start address */
1049a848adfSThomas Huth jump_to_IPL_code(KERN_IMAGE_START);
1059a848adfSThomas Huth }
106