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 2042ab98e7SJanosch Frank void write_reset_psw(uint64_t psw) 2142ab98e7SJanosch Frank { 2242ab98e7SJanosch Frank *reset_psw = psw; 2342ab98e7SJanosch Frank } 2442ab98e7SJanosch Frank 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 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 42*455e3bc3SJared Rossi /* 43*455e3bc3SJared Rossi * The IPLB for QEMU SCSI type devices must be rebuilt during re-ipl. The 44*455e3bc3SJared Rossi * iplb.devno is set to the boot position of the target SCSI device. 45*455e3bc3SJared Rossi */ 469a848adfSThomas Huth if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { 47*455e3bc3SJared Rossi iplb.devno = qipl.index; 48*455e3bc3SJared Rossi if (!set_iplb(&iplb)) { 49*455e3bc3SJared Rossi panic("Failed to set IPLB"); 50*455e3bc3SJared Rossi } 519a848adfSThomas Huth } 529a848adfSThomas Huth 539a848adfSThomas Huth /* 549a848adfSThomas Huth * The IPL PSW is at address 0. We also must not overwrite the 559a848adfSThomas Huth * content of non-BIOS memory after we loaded the guest, so we 569a848adfSThomas Huth * save the original content and restore it in jump_to_IPL_2. 579a848adfSThomas Huth */ 5842ab98e7SJanosch Frank if (address) { 5926e0b96fSJanosch Frank save_psw = *reset_psw; 6042ab98e7SJanosch Frank write_reset_psw(RESET_PSW); 6126e0b96fSJanosch Frank ipl_continue = address; 6242ab98e7SJanosch Frank } 6342ab98e7SJanosch Frank debug_print_int("set IPL addr to", address ?: *reset_psw & PSW_MASK_SHORT_ADDR); 649a848adfSThomas Huth 659a848adfSThomas Huth /* Ensure the guest output starts fresh */ 669f427883SJared Rossi printf("\n"); 679a848adfSThomas Huth 689a848adfSThomas Huth /* 699a848adfSThomas Huth * HACK ALERT. 709a848adfSThomas Huth * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 719a848adfSThomas Huth * can then use r15 as its stack pointer. 729a848adfSThomas Huth */ 73052b66e7SThomas Huth asm volatile("lghi %%r1,1\n\t" 74052b66e7SThomas Huth "diag %%r1,%%r1,0x308\n\t" 759a848adfSThomas Huth : : : "1", "memory"); 760181e237SJared Rossi puts("IPL code jump failed"); 770181e237SJared Rossi return -1; 789a848adfSThomas Huth } 799a848adfSThomas Huth 809a848adfSThomas Huth void jump_to_low_kernel(void) 819a848adfSThomas Huth { 829a848adfSThomas Huth /* 839a848adfSThomas Huth * If it looks like a Linux binary, i.e. there is the "S390EP" magic from 849a848adfSThomas Huth * arch/s390/kernel/head.S here, then let's jump to the well-known Linux 859a848adfSThomas Huth * kernel start address (when jumping to the PSW-at-zero address instead, 869a848adfSThomas Huth * the kernel startup code fails when we booted from a network device). 879a848adfSThomas Huth */ 883d651996SEric Farman if (!memcmp((char *)S390EP, "S390EP", 6)) { 899a848adfSThomas Huth jump_to_IPL_code(KERN_IMAGE_START); 909a848adfSThomas Huth } 919a848adfSThomas Huth 92ff77712aSThomas Huth /* Trying to get PSW at zero address (pointed to by reset_psw) */ 93ff77712aSThomas Huth if (*reset_psw & RESET_PSW_MASK) { 9442ab98e7SJanosch Frank /* 9542ab98e7SJanosch Frank * Surely nobody will try running directly from lowcore, so 9642ab98e7SJanosch Frank * let's use 0 as an indication that we want to load the reset 9742ab98e7SJanosch Frank * psw at 0x0 and not jump to the entry. 9842ab98e7SJanosch Frank */ 9942ab98e7SJanosch Frank jump_to_IPL_code(0); 1009a848adfSThomas Huth } 1019a848adfSThomas Huth 1029a848adfSThomas Huth /* No other option left, so use the Linux kernel start address */ 1039a848adfSThomas Huth jump_to_IPL_code(KERN_IMAGE_START); 1049a848adfSThomas Huth } 105