]>
Commit | Line | Data |
---|---|---|
9a848adf TH |
1 | /* |
2 | * QEMU s390-ccw firmware - jump to IPL code | |
3 | * | |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or (at | |
5 | * your option) any later version. See the COPYING file in the top-level | |
6 | * directory. | |
7 | */ | |
8 | ||
9 | #include "libc.h" | |
10 | #include "s390-ccw.h" | |
11 | ||
12 | #define KERN_IMAGE_START 0x010000UL | |
13 | #define PSW_MASK_64 0x0000000100000000ULL | |
14 | #define PSW_MASK_32 0x0000000080000000ULL | |
5c6f0d5f JF |
15 | #define PSW_MASK_SHORTPSW 0x0008000000000000ULL |
16 | #define RESET_PSW_MASK (PSW_MASK_SHORTPSW | PSW_MASK_32 | PSW_MASK_64) | |
9a848adf TH |
17 | |
18 | typedef struct ResetInfo { | |
5c6f0d5f | 19 | uint64_t ipl_psw; |
9a848adf TH |
20 | uint32_t ipl_continue; |
21 | } ResetInfo; | |
22 | ||
23 | static ResetInfo save; | |
24 | ||
25 | static void jump_to_IPL_2(void) | |
26 | { | |
27 | ResetInfo *current = 0; | |
28 | ||
29 | void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; | |
30 | *current = save; | |
31 | ipl(); /* should not return */ | |
32 | } | |
33 | ||
34 | void jump_to_IPL_code(uint64_t address) | |
35 | { | |
36 | /* store the subsystem information _after_ the bootmap was loaded */ | |
37 | write_subsystem_identification(); | |
9bfc04f9 | 38 | write_iplb_location(); |
9a848adf TH |
39 | |
40 | /* prevent unknown IPL types in the guest */ | |
41 | if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { | |
42 | iplb.pbt = S390_IPL_TYPE_CCW; | |
43 | set_iplb(&iplb); | |
44 | } | |
45 | ||
46 | /* | |
47 | * The IPL PSW is at address 0. We also must not overwrite the | |
48 | * content of non-BIOS memory after we loaded the guest, so we | |
49 | * save the original content and restore it in jump_to_IPL_2. | |
50 | */ | |
51 | ResetInfo *current = 0; | |
52 | ||
53 | save = *current; | |
5c6f0d5f JF |
54 | |
55 | current->ipl_psw = (uint64_t) &jump_to_IPL_2; | |
56 | current->ipl_psw |= RESET_PSW_MASK; | |
9a848adf TH |
57 | current->ipl_continue = address & 0x7fffffff; |
58 | ||
59 | debug_print_int("set IPL addr to", current->ipl_continue); | |
60 | ||
61 | /* Ensure the guest output starts fresh */ | |
62 | sclp_print("\n"); | |
63 | ||
64 | /* | |
65 | * HACK ALERT. | |
66 | * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 | |
67 | * can then use r15 as its stack pointer. | |
68 | */ | |
69 | asm volatile("lghi 1,1\n\t" | |
70 | "diag 1,1,0x308\n\t" | |
71 | : : : "1", "memory"); | |
72 | panic("\n! IPL returns !\n"); | |
73 | } | |
74 | ||
75 | void jump_to_low_kernel(void) | |
76 | { | |
77 | /* | |
78 | * If it looks like a Linux binary, i.e. there is the "S390EP" magic from | |
79 | * arch/s390/kernel/head.S here, then let's jump to the well-known Linux | |
80 | * kernel start address (when jumping to the PSW-at-zero address instead, | |
81 | * the kernel startup code fails when we booted from a network device). | |
82 | */ | |
83 | if (!memcmp((char *)0x10008, "S390EP", 6)) { | |
84 | jump_to_IPL_code(KERN_IMAGE_START); | |
85 | } | |
86 | ||
87 | /* Trying to get PSW at zero address */ | |
5c6f0d5f | 88 | if (*((uint64_t *)0) & RESET_PSW_MASK) { |
9a848adf TH |
89 | jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); |
90 | } | |
91 | ||
92 | /* No other option left, so use the Linux kernel start address */ | |
93 | jump_to_IPL_code(KERN_IMAGE_START); | |
94 | } |