192f2ca38SAlexander Graf /*
292f2ca38SAlexander Graf * S390 virtio-ccw loading program
392f2ca38SAlexander Graf *
492f2ca38SAlexander Graf * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
592f2ca38SAlexander Graf *
692f2ca38SAlexander Graf * This work is licensed under the terms of the GNU GPL, version 2 or (at
792f2ca38SAlexander Graf * your option) any later version. See the COPYING file in the top-level
892f2ca38SAlexander Graf * directory.
992f2ca38SAlexander Graf */
1092f2ca38SAlexander Graf
119f427883SJared Rossi #include <stdlib.h>
129f427883SJared Rossi #include <string.h>
139f427883SJared Rossi #include <stdio.h>
149bfc04f9SJanosch Frank #include "helper.h"
15c95df3d1SJason J. Herne #include "s390-arch.h"
1692f2ca38SAlexander Graf #include "s390-ccw.h"
17120d0410SJason J. Herne #include "cio.h"
1860612d5cSEugene (jno) Dvurechenski #include "virtio.h"
19cf30b7c4SThomas Huth #include "virtio-scsi.h"
20efa47d36SJason J. Herne #include "dasd-ipl.h"
2192f2ca38SAlexander Graf
22b88d7fa5SEugene (jno) Dvurechenski static SubChannelId blk_schid = { .one = 1 };
236673ded7SThomas Huth static char loadparm_str[LOADPARM_LEN + 1];
24118ee80fSCollin L. Walling QemuIplParameters qipl;
25a5f6e097SJason J. Herne IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
26f697bed2SJared Rossi bool have_iplb;
272880469cSJason J. Herne static uint16_t cutype;
289bfc04f9SJanosch Frank LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
29f2879a5cSChristian Borntraeger
309eaa654aSCollin L. Walling #define LOADPARM_PROMPT "PROMPT "
31074afe60SCollin Walling #define LOADPARM_EMPTY " "
3253b310ceSCollin L. Walling #define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL)
339eaa654aSCollin L. Walling
34f2879a5cSChristian Borntraeger /*
35a5f6e097SJason J. Herne * Principles of Operations (SA22-7832-09) chapter 17 requires that
36f2879a5cSChristian Borntraeger * a subsystem-identification is at 184-187 and bytes 188-191 are zero
37f2879a5cSChristian Borntraeger * after list-directed-IPL and ccw-IPL.
38f2879a5cSChristian Borntraeger */
write_subsystem_identification(void)39f2879a5cSChristian Borntraeger void write_subsystem_identification(void)
40f2879a5cSChristian Borntraeger {
418e5739ceSJared Rossi if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) {
428e5739ceSJared Rossi lowcore->subchannel_id = net_schid.sch_id;
438e5739ceSJared Rossi lowcore->subchannel_nr = net_schid.sch_no;
448e5739ceSJared Rossi } else {
45e6d393d0SJanosch Frank lowcore->subchannel_id = blk_schid.sch_id;
46e6d393d0SJanosch Frank lowcore->subchannel_nr = blk_schid.sch_no;
478e5739ceSJared Rossi }
48e6d393d0SJanosch Frank lowcore->io_int_parm = 0;
49f2879a5cSChristian Borntraeger }
50f2879a5cSChristian Borntraeger
write_iplb_location(void)519bfc04f9SJanosch Frank void write_iplb_location(void)
529bfc04f9SJanosch Frank {
53872882e7SJason J. Herne if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() != VIRTIO_ID_NET) {
549bfc04f9SJanosch Frank lowcore->ptr_iplb = ptr2u32(&iplb);
559bfc04f9SJanosch Frank }
56872882e7SJason J. Herne }
579bfc04f9SJanosch Frank
copy_qipl(void)58f697bed2SJared Rossi static void copy_qipl(void)
59f697bed2SJared Rossi {
60f697bed2SJared Rossi QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
61f697bed2SJared Rossi memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
62f697bed2SJared Rossi }
63f697bed2SJared Rossi
get_loadparm_index(void)6495fa1af8SFarhan Ali unsigned int get_loadparm_index(void)
6595fa1af8SFarhan Ali {
669f427883SJared Rossi return atoi(loadparm_str);
6795fa1af8SFarhan Ali }
6895fa1af8SFarhan Ali
is_dev_possibly_bootable(int dev_no,int sch_no)69d2cf4af1SThomas Huth static int is_dev_possibly_bootable(int dev_no, int sch_no)
7092f2ca38SAlexander Graf {
712880469cSJason J. Herne bool is_virtio;
72d2cf4af1SThomas Huth Schib schib;
73d2cf4af1SThomas Huth int r;
74ff151f4eSDominik Dingel
75d2cf4af1SThomas Huth blk_schid.sch_no = sch_no;
767b361db3SJason J. Herne r = stsch_err(blk_schid, &schib);
77d2cf4af1SThomas Huth if (r == 3 || r == -EIO) {
78d2cf4af1SThomas Huth return -ENODEV;
7922d67ab5SCornelia Huck }
807b361db3SJason J. Herne if (!schib.pmcw.dnv) {
81d2cf4af1SThomas Huth return false;
8292f2ca38SAlexander Graf }
83930072d2SJason J. Herne
843668cb7cSJason J. Herne enable_subchannel(blk_schid);
852880469cSJason J. Herne cutype = cu_type(blk_schid);
860181e237SJared Rossi if (cutype == CU_TYPE_UNKNOWN) {
870181e237SJared Rossi return -EIO;
880181e237SJared Rossi }
892880469cSJason J. Herne
902880469cSJason J. Herne /*
912880469cSJason J. Herne * Note: we always have to run virtio_is_supported() here to make
922880469cSJason J. Herne * sure that the vdev.senseid data gets pre-initialized correctly
932880469cSJason J. Herne */
942880469cSJason J. Herne is_virtio = virtio_is_supported(blk_schid);
952880469cSJason J. Herne
96d2cf4af1SThomas Huth /* No specific devno given, just return whether the device is possibly bootable */
972880469cSJason J. Herne if (dev_no < 0) {
982880469cSJason J. Herne switch (cutype) {
992880469cSJason J. Herne case CU_TYPE_VIRTIO:
1002880469cSJason J. Herne if (is_virtio) {
1012880469cSJason J. Herne /*
1022880469cSJason J. Herne * Skip net devices since no IPLB is created and therefore
1032880469cSJason J. Herne * no network bootloader has been loaded
1042880469cSJason J. Herne */
1052880469cSJason J. Herne if (virtio_get_device_type() != VIRTIO_ID_NET) {
1062880469cSJason J. Herne return true;
1072880469cSJason J. Herne }
1082880469cSJason J. Herne }
109d2cf4af1SThomas Huth return false;
1102880469cSJason J. Herne case CU_TYPE_DASD_3990:
1112880469cSJason J. Herne case CU_TYPE_DASD_2107:
1122880469cSJason J. Herne return true;
1132880469cSJason J. Herne default:
114d2cf4af1SThomas Huth return false;
11599b72e0fSFarhan Ali }
1162880469cSJason J. Herne }
117930072d2SJason J. Herne
1182880469cSJason J. Herne /* Caller asked for a specific devno */
1192880469cSJason J. Herne if (schib.pmcw.dev == dev_no) {
1200f79b89bSAlexander Yarygin return true;
1210f79b89bSAlexander Yarygin }
122d2cf4af1SThomas Huth
123d2cf4af1SThomas Huth return false;
124d2cf4af1SThomas Huth }
125d2cf4af1SThomas Huth
126d2cf4af1SThomas Huth /*
127d2cf4af1SThomas Huth * Find the subchannel connected to the given device (dev_no) and fill in the
128d2cf4af1SThomas Huth * subchannel information block (schib) with the connected subchannel's info.
129d2cf4af1SThomas Huth * NOTE: The global variable blk_schid is updated to contain the subchannel
130d2cf4af1SThomas Huth * information.
131d2cf4af1SThomas Huth *
132d2cf4af1SThomas Huth * If the caller gives dev_no=-1 then the user did not specify a boot device.
133d2cf4af1SThomas Huth * In this case we'll just use the first potentially bootable device we find.
134d2cf4af1SThomas Huth */
find_subch(int dev_no)135d2cf4af1SThomas Huth static bool find_subch(int dev_no)
136d2cf4af1SThomas Huth {
137d2cf4af1SThomas Huth int i, r;
138d2cf4af1SThomas Huth
139d2cf4af1SThomas Huth for (i = 0; i < 0x10000; i++) {
140d2cf4af1SThomas Huth r = is_dev_possibly_bootable(dev_no, i);
141d2cf4af1SThomas Huth if (r < 0) {
142d2cf4af1SThomas Huth break;
143d2cf4af1SThomas Huth }
144d2cf4af1SThomas Huth if (r == true) {
145d2cf4af1SThomas Huth return true;
146d2cf4af1SThomas Huth }
1470f79b89bSAlexander Yarygin }
1480f79b89bSAlexander Yarygin
1490f79b89bSAlexander Yarygin return false;
1500f79b89bSAlexander Yarygin }
1510f79b89bSAlexander Yarygin
menu_setup(void)1529eaa654aSCollin L. Walling static void menu_setup(void)
1539eaa654aSCollin L. Walling {
154a0e11b61SCollin Walling if (memcmp(loadparm_str, LOADPARM_PROMPT, LOADPARM_LEN) == 0) {
1559eaa654aSCollin L. Walling menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0);
1569eaa654aSCollin L. Walling return;
1579eaa654aSCollin L. Walling }
1589eaa654aSCollin L. Walling
1599eaa654aSCollin L. Walling /* If loadparm was set to any other value, then do not enable menu */
160a0e11b61SCollin Walling if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) {
161f697bed2SJared Rossi menu_set_parms(qipl.qipl_flags & ~BOOT_MENU_FLAG_MASK, 0);
1629eaa654aSCollin L. Walling return;
1639eaa654aSCollin L. Walling }
1649eaa654aSCollin L. Walling
1659eaa654aSCollin L. Walling switch (iplb.pbt) {
1669eaa654aSCollin L. Walling case S390_IPL_TYPE_CCW:
167ffb4a1c8SCollin L. Walling case S390_IPL_TYPE_QEMU_SCSI:
16853b310ceSCollin L. Walling menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK,
1699eaa654aSCollin L. Walling qipl.boot_menu_timeout);
1709eaa654aSCollin L. Walling return;
1719eaa654aSCollin L. Walling }
1729eaa654aSCollin L. Walling }
1739eaa654aSCollin L. Walling
17487f910c1SJason J. Herne /*
17587f910c1SJason J. Herne * Initialize the channel I/O subsystem so we can talk to our ipl/boot device.
17687f910c1SJason J. Herne */
css_setup(void)17787f910c1SJason J. Herne static void css_setup(void)
17887f910c1SJason J. Herne {
17987f910c1SJason J. Herne /*
18087f910c1SJason J. Herne * Unconditionally enable mss support. In every sane configuration this
18187f910c1SJason J. Herne * will succeed; and even if it doesn't, stsch_err() can handle it.
18287f910c1SJason J. Herne */
18387f910c1SJason J. Herne enable_mss_facility();
18487f910c1SJason J. Herne }
18587f910c1SJason J. Herne
186a5f6e097SJason J. Herne /*
187a5f6e097SJason J. Herne * Collect various pieces of information from the hypervisor/hardware that
188a5f6e097SJason J. Herne * we'll use to determine exactly how we'll boot.
189a5f6e097SJason J. Herne */
boot_setup(void)190a5f6e097SJason J. Herne static void boot_setup(void)
191a5f6e097SJason J. Herne {
192a5f6e097SJason J. Herne char lpmsg[] = "LOADPARM=[________]\n";
193a5f6e097SJason J. Herne
194*1056ca1eSJared Rossi if (have_iplb && memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
195bb185de4SJared Rossi ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
196bb185de4SJared Rossi } else {
197a5f6e097SJason J. Herne sclp_get_loadparm_ascii(loadparm_str);
198bb185de4SJared Rossi }
199bb185de4SJared Rossi
200f697bed2SJared Rossi if (have_iplb) {
201f697bed2SJared Rossi menu_setup();
202f697bed2SJared Rossi }
203f697bed2SJared Rossi
204a5f6e097SJason J. Herne memcpy(lpmsg + 10, loadparm_str, 8);
2059f427883SJared Rossi puts(lpmsg);
206a5f6e097SJason J. Herne
2073d651996SEric Farman /*
2083d651996SEric Farman * Clear out any potential S390EP magic (see jump_to_low_kernel()),
2093d651996SEric Farman * so we don't taint our decision-making process during a reboot.
2103d651996SEric Farman */
2113d651996SEric Farman memset((char *)S390EP, 0, 6);
212a5f6e097SJason J. Herne }
213a5f6e097SJason J. Herne
find_boot_device(void)2140181e237SJared Rossi static bool find_boot_device(void)
2150f79b89bSAlexander Yarygin {
21699b72e0fSFarhan Ali VDev *vdev = virtio_get_device();
2170181e237SJared Rossi bool found = false;
2180f79b89bSAlexander Yarygin
219d046c51dSAlexander Yarygin switch (iplb.pbt) {
220d046c51dSAlexander Yarygin case S390_IPL_TYPE_CCW:
221f697bed2SJared Rossi vdev->scsi_device_selected = false;
2227b361db3SJason J. Herne debug_print_int("device no. ", iplb.ccw.devno);
223d046c51dSAlexander Yarygin blk_schid.ssid = iplb.ccw.ssid & 0x3;
2240f79b89bSAlexander Yarygin debug_print_int("ssid ", blk_schid.ssid);
2257b361db3SJason J. Herne found = find_subch(iplb.ccw.devno);
226d046c51dSAlexander Yarygin break;
227b39b7718SEugene (jno) Dvurechenski case S390_IPL_TYPE_QEMU_SCSI:
228b39b7718SEugene (jno) Dvurechenski vdev->scsi_device_selected = true;
229b39b7718SEugene (jno) Dvurechenski vdev->selected_scsi_device.channel = iplb.scsi.channel;
230b39b7718SEugene (jno) Dvurechenski vdev->selected_scsi_device.target = iplb.scsi.target;
231b39b7718SEugene (jno) Dvurechenski vdev->selected_scsi_device.lun = iplb.scsi.lun;
232b39b7718SEugene (jno) Dvurechenski blk_schid.ssid = iplb.scsi.ssid & 0x3;
2337b361db3SJason J. Herne found = find_subch(iplb.scsi.devno);
234b39b7718SEugene (jno) Dvurechenski break;
235d046c51dSAlexander Yarygin default:
2360181e237SJared Rossi puts("Unsupported IPLB");
237d046c51dSAlexander Yarygin }
2387b361db3SJason J. Herne
2390181e237SJared Rossi return found;
240ff151f4eSDominik Dingel }
24192f2ca38SAlexander Graf
virtio_setup(void)242605751b5SThomas Huth static int virtio_setup(void)
2437b361db3SJason J. Herne {
2447b361db3SJason J. Herne VDev *vdev = virtio_get_device();
2458c797468SJared Rossi vdev->is_cdrom = false;
246cf30b7c4SThomas Huth int ret;
2477b361db3SJason J. Herne
248cf30b7c4SThomas Huth switch (vdev->senseid.cu_model) {
249cf30b7c4SThomas Huth case VIRTIO_ID_NET:
2509f427883SJared Rossi puts("Network boot device detected");
251cf30b7c4SThomas Huth return 0;
252cf30b7c4SThomas Huth case VIRTIO_ID_BLOCK:
253cf30b7c4SThomas Huth ret = virtio_blk_setup_device(blk_schid);
254cf30b7c4SThomas Huth break;
255cf30b7c4SThomas Huth case VIRTIO_ID_SCSI:
256cf30b7c4SThomas Huth ret = virtio_scsi_setup_device(blk_schid);
257cf30b7c4SThomas Huth break;
258cf30b7c4SThomas Huth default:
2590181e237SJared Rossi puts("\n! No IPL device available !\n");
2600181e237SJared Rossi return -1;
261605751b5SThomas Huth }
262cf30b7c4SThomas Huth
2630181e237SJared Rossi if (!ret && !virtio_ipl_disk_is_valid()) {
2640181e237SJared Rossi puts("No valid IPL device detected");
2650181e237SJared Rossi return -ENODEV;
26692f2ca38SAlexander Graf }
267605751b5SThomas Huth
268cf30b7c4SThomas Huth return ret;
26999b72e0fSFarhan Ali }
27092f2ca38SAlexander Graf
ipl_boot_device(void)271d1f060a8SThomas Huth static void ipl_boot_device(void)
27292f2ca38SAlexander Graf {
2733668cb7cSJason J. Herne switch (cutype) {
274efa47d36SJason J. Herne case CU_TYPE_DASD_3990:
275efa47d36SJason J. Herne case CU_TYPE_DASD_2107:
2760181e237SJared Rossi dasd_ipl(blk_schid, cutype);
277efa47d36SJason J. Herne break;
2783668cb7cSJason J. Herne case CU_TYPE_VIRTIO:
279f697bed2SJared Rossi if (virtio_setup() == 0) {
2800181e237SJared Rossi zipl_load();
281f697bed2SJared Rossi }
2823668cb7cSJason J. Herne break;
2833668cb7cSJason J. Herne default:
2849f427883SJared Rossi printf("Attempting to boot from unexpected device type 0x%X\n", cutype);
2853668cb7cSJason J. Herne }
286d1f060a8SThomas Huth }
287d1f060a8SThomas Huth
288869d0e2fSThomas Huth /*
289869d0e2fSThomas Huth * No boot device has been specified, so we have to scan through the
290869d0e2fSThomas Huth * channels to find one.
291869d0e2fSThomas Huth */
probe_boot_device(void)292869d0e2fSThomas Huth static void probe_boot_device(void)
293869d0e2fSThomas Huth {
294869d0e2fSThomas Huth int ssid, sch_no, ret;
295869d0e2fSThomas Huth
296869d0e2fSThomas Huth for (ssid = 0; ssid < 0x3; ssid++) {
297869d0e2fSThomas Huth blk_schid.ssid = ssid;
298869d0e2fSThomas Huth for (sch_no = 0; sch_no < 0x10000; sch_no++) {
299869d0e2fSThomas Huth ret = is_dev_possibly_bootable(-1, sch_no);
300869d0e2fSThomas Huth if (ret < 0) {
301869d0e2fSThomas Huth break;
302869d0e2fSThomas Huth }
303869d0e2fSThomas Huth if (ret == true) {
304869d0e2fSThomas Huth ipl_boot_device(); /* Only returns if unsuccessful */
305869d0e2fSThomas Huth }
306869d0e2fSThomas Huth }
307869d0e2fSThomas Huth }
308869d0e2fSThomas Huth
3099f427883SJared Rossi puts("Could not find a suitable boot device (none specified)");
310869d0e2fSThomas Huth }
311869d0e2fSThomas Huth
main(void)3122ba3cc47SThomas Huth void main(void)
313d1f060a8SThomas Huth {
314f697bed2SJared Rossi copy_qipl();
315d1f060a8SThomas Huth sclp_setup();
316d1f060a8SThomas Huth css_setup();
317f697bed2SJared Rossi have_iplb = store_iplb(&iplb);
318f697bed2SJared Rossi if (!have_iplb) {
319*1056ca1eSJared Rossi boot_setup();
320869d0e2fSThomas Huth probe_boot_device();
321869d0e2fSThomas Huth }
32260612d5cSEugene (jno) Dvurechenski
323f697bed2SJared Rossi while (have_iplb) {
324f697bed2SJared Rossi boot_setup();
325f697bed2SJared Rossi if (have_iplb && find_boot_device()) {
326f697bed2SJared Rossi ipl_boot_device();
327f697bed2SJared Rossi }
328f697bed2SJared Rossi have_iplb = load_next_iplb();
329f697bed2SJared Rossi }
330f697bed2SJared Rossi
331f697bed2SJared Rossi panic("No suitable device for IPL. Halting...");
332f697bed2SJared Rossi
33392f2ca38SAlexander Graf }
334