]>
Commit | Line | Data |
---|---|---|
92f2ca38 AG |
1 | /* |
2 | * S390 virtio-ccw loading program | |
3 | * | |
4 | * Copyright (c) 2013 Alexander Graf <agraf@suse.de> | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or (at | |
7 | * your option) any later version. See the COPYING file in the top-level | |
8 | * directory. | |
9 | */ | |
10 | ||
90806fec | 11 | #include "libc.h" |
9bfc04f9 | 12 | #include "helper.h" |
c95df3d1 | 13 | #include "s390-arch.h" |
92f2ca38 | 14 | #include "s390-ccw.h" |
120d0410 | 15 | #include "cio.h" |
60612d5c | 16 | #include "virtio.h" |
efa47d36 | 17 | #include "dasd-ipl.h" |
92f2ca38 | 18 | |
92f2ca38 | 19 | char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); |
b88d7fa5 | 20 | static SubChannelId blk_schid = { .one = 1 }; |
6673ded7 | 21 | static char loadparm_str[LOADPARM_LEN + 1]; |
118ee80f | 22 | QemuIplParameters qipl; |
a5f6e097 JH |
23 | IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); |
24 | static bool have_iplb; | |
2880469c | 25 | static uint16_t cutype; |
9bfc04f9 | 26 | LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ |
f2879a5c | 27 | |
9eaa654a | 28 | #define LOADPARM_PROMPT "PROMPT " |
074afe60 | 29 | #define LOADPARM_EMPTY " " |
53b310ce | 30 | #define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL) |
9eaa654a | 31 | |
f2879a5c | 32 | /* |
a5f6e097 | 33 | * Principles of Operations (SA22-7832-09) chapter 17 requires that |
f2879a5c CB |
34 | * a subsystem-identification is at 184-187 and bytes 188-191 are zero |
35 | * after list-directed-IPL and ccw-IPL. | |
36 | */ | |
37 | void write_subsystem_identification(void) | |
38 | { | |
e6d393d0 JF |
39 | lowcore->subchannel_id = blk_schid.sch_id; |
40 | lowcore->subchannel_nr = blk_schid.sch_no; | |
41 | lowcore->io_int_parm = 0; | |
f2879a5c CB |
42 | } |
43 | ||
9bfc04f9 JF |
44 | void write_iplb_location(void) |
45 | { | |
872882e7 JH |
46 | if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() != VIRTIO_ID_NET) { |
47 | lowcore->ptr_iplb = ptr2u32(&iplb); | |
48 | } | |
9bfc04f9 JF |
49 | } |
50 | ||
95fa1af8 FA |
51 | unsigned int get_loadparm_index(void) |
52 | { | |
074afe60 | 53 | return atoui(loadparm_str); |
95fa1af8 FA |
54 | } |
55 | ||
d2cf4af1 TH |
56 | static int is_dev_possibly_bootable(int dev_no, int sch_no) |
57 | { | |
58 | bool is_virtio; | |
59 | Schib schib; | |
60 | int r; | |
61 | ||
62 | blk_schid.sch_no = sch_no; | |
63 | r = stsch_err(blk_schid, &schib); | |
64 | if (r == 3 || r == -EIO) { | |
65 | return -ENODEV; | |
66 | } | |
67 | if (!schib.pmcw.dnv) { | |
68 | return false; | |
69 | } | |
70 | ||
71 | enable_subchannel(blk_schid); | |
72 | cutype = cu_type(blk_schid); | |
73 | ||
74 | /* | |
75 | * Note: we always have to run virtio_is_supported() here to make | |
76 | * sure that the vdev.senseid data gets pre-initialized correctly | |
77 | */ | |
78 | is_virtio = virtio_is_supported(blk_schid); | |
79 | ||
80 | /* No specific devno given, just return whether the device is possibly bootable */ | |
81 | if (dev_no < 0) { | |
82 | switch (cutype) { | |
83 | case CU_TYPE_VIRTIO: | |
84 | if (is_virtio) { | |
85 | /* | |
86 | * Skip net devices since no IPLB is created and therefore | |
87 | * no network bootloader has been loaded | |
88 | */ | |
89 | if (virtio_get_device_type() != VIRTIO_ID_NET) { | |
90 | return true; | |
91 | } | |
92 | } | |
93 | return false; | |
94 | case CU_TYPE_DASD_3990: | |
95 | case CU_TYPE_DASD_2107: | |
96 | return true; | |
97 | default: | |
98 | return false; | |
99 | } | |
100 | } | |
101 | ||
102 | /* Caller asked for a specific devno */ | |
103 | if (schib.pmcw.dev == dev_no) { | |
104 | return true; | |
105 | } | |
106 | ||
107 | return false; | |
108 | } | |
109 | ||
930072d2 JH |
110 | /* |
111 | * Find the subchannel connected to the given device (dev_no) and fill in the | |
112 | * subchannel information block (schib) with the connected subchannel's info. | |
113 | * NOTE: The global variable blk_schid is updated to contain the subchannel | |
114 | * information. | |
2880469c JH |
115 | * |
116 | * If the caller gives dev_no=-1 then the user did not specify a boot device. | |
117 | * In this case we'll just use the first potentially bootable device we find. | |
930072d2 | 118 | */ |
7b361db3 | 119 | static bool find_subch(int dev_no) |
0f79b89b AY |
120 | { |
121 | int i, r; | |
122 | ||
123 | for (i = 0; i < 0x10000; i++) { | |
d2cf4af1 TH |
124 | r = is_dev_possibly_bootable(dev_no, i); |
125 | if (r < 0) { | |
0f79b89b AY |
126 | break; |
127 | } | |
d2cf4af1 | 128 | if (r == true) { |
0f79b89b AY |
129 | return true; |
130 | } | |
131 | } | |
132 | ||
133 | return false; | |
134 | } | |
135 | ||
9eaa654a CW |
136 | static void menu_setup(void) |
137 | { | |
a0e11b61 | 138 | if (memcmp(loadparm_str, LOADPARM_PROMPT, LOADPARM_LEN) == 0) { |
9eaa654a CW |
139 | menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); |
140 | return; | |
141 | } | |
142 | ||
143 | /* If loadparm was set to any other value, then do not enable menu */ | |
a0e11b61 | 144 | if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) { |
9eaa654a CW |
145 | return; |
146 | } | |
147 | ||
148 | switch (iplb.pbt) { | |
149 | case S390_IPL_TYPE_CCW: | |
ffb4a1c8 | 150 | case S390_IPL_TYPE_QEMU_SCSI: |
53b310ce | 151 | menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK, |
9eaa654a CW |
152 | qipl.boot_menu_timeout); |
153 | return; | |
154 | } | |
155 | } | |
156 | ||
87f910c1 JH |
157 | /* |
158 | * Initialize the channel I/O subsystem so we can talk to our ipl/boot device. | |
159 | */ | |
160 | static void css_setup(void) | |
161 | { | |
162 | /* | |
163 | * Unconditionally enable mss support. In every sane configuration this | |
164 | * will succeed; and even if it doesn't, stsch_err() can handle it. | |
165 | */ | |
166 | enable_mss_facility(); | |
167 | } | |
168 | ||
a5f6e097 JH |
169 | /* |
170 | * Collect various pieces of information from the hypervisor/hardware that | |
171 | * we'll use to determine exactly how we'll boot. | |
172 | */ | |
173 | static void boot_setup(void) | |
174 | { | |
175 | char lpmsg[] = "LOADPARM=[________]\n"; | |
176 | ||
177 | sclp_get_loadparm_ascii(loadparm_str); | |
178 | memcpy(lpmsg + 10, loadparm_str, 8); | |
179 | sclp_print(lpmsg); | |
180 | ||
3d651996 EF |
181 | /* |
182 | * Clear out any potential S390EP magic (see jump_to_low_kernel()), | |
183 | * so we don't taint our decision-making process during a reboot. | |
184 | */ | |
185 | memset((char *)S390EP, 0, 6); | |
186 | ||
a5f6e097 JH |
187 | have_iplb = store_iplb(&iplb); |
188 | } | |
189 | ||
7b361db3 | 190 | static void find_boot_device(void) |
92f2ca38 | 191 | { |
99b72e0f | 192 | VDev *vdev = virtio_get_device(); |
7b361db3 | 193 | bool found; |
118ee80f | 194 | |
7b361db3 JH |
195 | switch (iplb.pbt) { |
196 | case S390_IPL_TYPE_CCW: | |
197 | debug_print_int("device no. ", iplb.ccw.devno); | |
198 | blk_schid.ssid = iplb.ccw.ssid & 0x3; | |
199 | debug_print_int("ssid ", blk_schid.ssid); | |
200 | found = find_subch(iplb.ccw.devno); | |
201 | break; | |
202 | case S390_IPL_TYPE_QEMU_SCSI: | |
203 | vdev->scsi_device_selected = true; | |
204 | vdev->selected_scsi_device.channel = iplb.scsi.channel; | |
205 | vdev->selected_scsi_device.target = iplb.scsi.target; | |
206 | vdev->selected_scsi_device.lun = iplb.scsi.lun; | |
207 | blk_schid.ssid = iplb.scsi.ssid & 0x3; | |
208 | found = find_subch(iplb.scsi.devno); | |
209 | break; | |
210 | default: | |
211 | panic("List-directed IPL not supported yet!\n"); | |
92f2ca38 AG |
212 | } |
213 | ||
7b361db3 JH |
214 | IPL_assert(found, "Boot device not found\n"); |
215 | } | |
216 | ||
605751b5 | 217 | static int virtio_setup(void) |
7b361db3 JH |
218 | { |
219 | VDev *vdev = virtio_get_device(); | |
220 | QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; | |
221 | ||
222 | memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); | |
223 | ||
224 | if (have_iplb) { | |
225 | menu_setup(); | |
226 | } | |
92f2ca38 | 227 | |
99b72e0f FA |
228 | if (virtio_get_device_type() == VIRTIO_ID_NET) { |
229 | sclp_print("Network boot device detected\n"); | |
118ee80f | 230 | vdev->netboot_start_addr = qipl.netboot_start_addr; |
99b72e0f | 231 | } else { |
605751b5 TH |
232 | int ret = virtio_blk_setup_device(blk_schid); |
233 | if (ret) { | |
234 | return ret; | |
235 | } | |
99b72e0f FA |
236 | IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); |
237 | } | |
605751b5 TH |
238 | |
239 | return 0; | |
92f2ca38 AG |
240 | } |
241 | ||
d1f060a8 | 242 | static void ipl_boot_device(void) |
92f2ca38 | 243 | { |
3668cb7c | 244 | switch (cutype) { |
efa47d36 JH |
245 | case CU_TYPE_DASD_3990: |
246 | case CU_TYPE_DASD_2107: | |
247 | dasd_ipl(blk_schid, cutype); /* no return */ | |
248 | break; | |
3668cb7c | 249 | case CU_TYPE_VIRTIO: |
605751b5 | 250 | if (virtio_setup() == 0) { |
5dc739f3 | 251 | zipl_load(); /* Only returns in case of errors */ |
605751b5 | 252 | } |
3668cb7c JH |
253 | break; |
254 | default: | |
255 | print_int("Attempting to boot from unexpected device type", cutype); | |
d1f060a8 | 256 | panic("\nBoot failed.\n"); |
3668cb7c | 257 | } |
d1f060a8 TH |
258 | } |
259 | ||
869d0e2f TH |
260 | /* |
261 | * No boot device has been specified, so we have to scan through the | |
262 | * channels to find one. | |
263 | */ | |
264 | static void probe_boot_device(void) | |
265 | { | |
266 | int ssid, sch_no, ret; | |
267 | ||
268 | for (ssid = 0; ssid < 0x3; ssid++) { | |
269 | blk_schid.ssid = ssid; | |
270 | for (sch_no = 0; sch_no < 0x10000; sch_no++) { | |
271 | ret = is_dev_possibly_bootable(-1, sch_no); | |
272 | if (ret < 0) { | |
273 | break; | |
274 | } | |
275 | if (ret == true) { | |
276 | ipl_boot_device(); /* Only returns if unsuccessful */ | |
277 | } | |
278 | } | |
279 | } | |
280 | ||
281 | sclp_print("Could not find a suitable boot device (none specified)\n"); | |
282 | } | |
283 | ||
d1f060a8 TH |
284 | int main(void) |
285 | { | |
286 | sclp_setup(); | |
287 | css_setup(); | |
288 | boot_setup(); | |
869d0e2f TH |
289 | if (have_iplb) { |
290 | find_boot_device(); | |
869d0e2f TH |
291 | ipl_boot_device(); |
292 | } else { | |
293 | probe_boot_device(); | |
294 | } | |
60612d5c | 295 | |
c9262e8a | 296 | panic("Failed to load OS from hard disk\n"); |
60612d5c | 297 | return 0; /* make compiler happy */ |
92f2ca38 | 298 | } |