Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / seabios / src / boot.c
1 // Code to load disk image and start system boot.
2 //
3 // Copyright (C) 2008-2013  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "block.h" // struct drive_s
9 #include "bregs.h" // struct bregs
10 #include "config.h" // CONFIG_*
11 #include "fw/paravirt.h" // qemu_cfg_show_boot_menu
12 #include "hw/pci.h" // pci_bdf_to_*
13 #include "hw/rtc.h" // rtc_read
14 #include "hw/usb.h" // struct usbdevice_s
15 #include "list.h" // hlist_node
16 #include "malloc.h" // free
17 #include "output.h" // dprintf
18 #include "romfile.h" // romfile_loadint
19 #include "std/disk.h" // struct mbr_s
20 #include "string.h" // memset
21 #include "util.h" // irqtimer_calc
22
23
24 /****************************************************************
25  * Boot priority ordering
26  ****************************************************************/
27
28 static char **Bootorder VARVERIFY32INIT;
29 static int BootorderCount;
30
31 static void
32 loadBootOrder(void)
33 {
34     if (!CONFIG_BOOTORDER)
35         return;
36
37     char *f = romfile_loadfile("bootorder", NULL);
38     if (!f)
39         return;
40
41     int i = 0;
42     BootorderCount = 1;
43     while (f[i]) {
44         if (f[i] == '\n')
45             BootorderCount++;
46         i++;
47     }
48     Bootorder = malloc_tmphigh(BootorderCount*sizeof(char*));
49     if (!Bootorder) {
50         warn_noalloc();
51         free(f);
52         BootorderCount = 0;
53         return;
54     }
55
56     dprintf(1, "boot order:\n");
57     i = 0;
58     do {
59         Bootorder[i] = f;
60         f = strchr(f, '\n');
61         if (f)
62             *(f++) = '\0';
63         Bootorder[i] = nullTrailingSpace(Bootorder[i]);
64         dprintf(1, "%d: %s\n", i+1, Bootorder[i]);
65         i++;
66     } while (f);
67 }
68
69 // See if 'str' starts with 'glob' - if glob contains an '*' character
70 // it will match any number of characters in str that aren't a '/' or
71 // the next glob character.
72 static char *
73 glob_prefix(const char *glob, const char *str)
74 {
75     for (;;) {
76         if (!*glob && (!*str || *str == '/'))
77             return (char*)str;
78         if (*glob == '*') {
79             if (!*str || *str == '/' || *str == glob[1])
80                 glob++;
81             else
82                 str++;
83             continue;
84         }
85         if (*glob != *str)
86             return NULL;
87         glob++;
88         str++;
89     }
90 }
91
92 // Search the bootorder list for the given glob pattern.
93 static int
94 find_prio(const char *glob)
95 {
96     dprintf(1, "Searching bootorder for: %s\n", glob);
97     int i;
98     for (i = 0; i < BootorderCount; i++)
99         if (glob_prefix(glob, Bootorder[i]))
100             return i+1;
101     return -1;
102 }
103
104 #define FW_PCI_DOMAIN "/pci@i0cf8"
105
106 static char *
107 build_pci_path(char *buf, int max, const char *devname, struct pci_device *pci)
108 {
109     // Build the string path of a bdf - for example: /pci@i0cf8/isa@1,2
110     char *p = buf;
111     if (pci->parent) {
112         p = build_pci_path(p, max, "pci-bridge", pci->parent);
113     } else {
114         if (pci->rootbus)
115             p += snprintf(p, max, "/pci-root@%x", pci->rootbus);
116         p += snprintf(p, buf+max-p, "%s", FW_PCI_DOMAIN);
117     }
118
119     int dev = pci_bdf_to_dev(pci->bdf), fn = pci_bdf_to_fn(pci->bdf);
120     p += snprintf(p, buf+max-p, "/%s@%x", devname, dev);
121     if (fn)
122         p += snprintf(p, buf+max-p, ",%x", fn);
123     return p;
124 }
125
126 int bootprio_find_pci_device(struct pci_device *pci)
127 {
128     if (CONFIG_CSM)
129         return csm_bootprio_pci(pci);
130     if (!CONFIG_BOOTORDER)
131         return -1;
132     // Find pci device - for example: /pci@i0cf8/ethernet@5
133     char desc[256];
134     build_pci_path(desc, sizeof(desc), "*", pci);
135     return find_prio(desc);
136 }
137
138 int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun)
139 {
140     if (!CONFIG_BOOTORDER)
141         return -1;
142     if (!pci)
143         // support only pci machine for now
144         return -1;
145     // Find scsi drive - for example: /pci@i0cf8/scsi@5/channel@0/disk@1,0
146     char desc[256], *p;
147     p = build_pci_path(desc, sizeof(desc), "*", pci);
148     snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%x,%x", target, lun);
149     return find_prio(desc);
150 }
151
152 int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave)
153 {
154     if (CONFIG_CSM)
155         return csm_bootprio_ata(pci, chanid, slave);
156     if (!CONFIG_BOOTORDER)
157         return -1;
158     if (!pci)
159         // support only pci machine for now
160         return -1;
161     // Find ata drive - for example: /pci@i0cf8/ide@1,1/drive@1/disk@0
162     char desc[256], *p;
163     p = build_pci_path(desc, sizeof(desc), "*", pci);
164     snprintf(p, desc+sizeof(desc)-p, "/drive@%x/disk@%x", chanid, slave);
165     return find_prio(desc);
166 }
167
168 int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid)
169 {
170     if (CONFIG_CSM)
171         return csm_bootprio_fdc(pci, port, fdid);
172     if (!CONFIG_BOOTORDER)
173         return -1;
174     if (!pci)
175         // support only pci machine for now
176         return -1;
177     // Find floppy - for example: /pci@i0cf8/isa@1/fdc@03f1/floppy@0
178     char desc[256], *p;
179     p = build_pci_path(desc, sizeof(desc), "isa", pci);
180     snprintf(p, desc+sizeof(desc)-p, "/fdc@%04x/floppy@%x", port, fdid);
181     return find_prio(desc);
182 }
183
184 int bootprio_find_pci_rom(struct pci_device *pci, int instance)
185 {
186     if (!CONFIG_BOOTORDER)
187         return -1;
188     // Find pci rom - for example: /pci@i0cf8/scsi@3:rom2
189     char desc[256], *p;
190     p = build_pci_path(desc, sizeof(desc), "*", pci);
191     if (instance)
192         snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance);
193     return find_prio(desc);
194 }
195
196 int bootprio_find_named_rom(const char *name, int instance)
197 {
198     if (!CONFIG_BOOTORDER)
199         return -1;
200     // Find named rom - for example: /rom@genroms/linuxboot.bin
201     char desc[256], *p;
202     p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name);
203     if (instance)
204         snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance);
205     return find_prio(desc);
206 }
207
208 static char *
209 build_usb_path(char *buf, int max, struct usbhub_s *hub)
210 {
211     if (!hub->usbdev)
212         // Root hub - nothing to add.
213         return buf;
214     char *p = build_usb_path(buf, max, hub->usbdev->hub);
215     p += snprintf(p, buf+max-p, "/hub@%x", hub->usbdev->port+1);
216     return p;
217 }
218
219 int bootprio_find_usb(struct usbdevice_s *usbdev, int lun)
220 {
221     if (!CONFIG_BOOTORDER)
222         return -1;
223     // Find usb - for example: /pci@i0cf8/usb@1,2/storage@1/channel@0/disk@0,0
224     char desc[256], *p;
225     p = build_pci_path(desc, sizeof(desc), "usb", usbdev->hub->cntl->pci);
226     p = build_usb_path(p, desc+sizeof(desc)-p, usbdev->hub);
227     snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%x"
228              , usbdev->port+1, lun);
229     int ret = find_prio(desc);
230     if (ret >= 0)
231         return ret;
232     // Try usb-host/redir - for example: /pci@i0cf8/usb@1,2/usb-host@1
233     snprintf(p, desc+sizeof(desc)-p, "/usb-*@%x", usbdev->port+1);
234     return find_prio(desc);
235 }
236
237
238 /****************************************************************
239  * Boot setup
240  ****************************************************************/
241
242 static int BootRetryTime;
243 static int CheckFloppySig = 1;
244
245 #define DEFAULT_PRIO           9999
246
247 static int DefaultFloppyPrio = 101;
248 static int DefaultCDPrio     = 102;
249 static int DefaultHDPrio     = 103;
250 static int DefaultBEVPrio    = 104;
251
252 void
253 boot_init(void)
254 {
255     if (! CONFIG_BOOT)
256         return;
257
258     if (CONFIG_QEMU) {
259         // On emulators, get boot order from nvram.
260         if (rtc_read(CMOS_BIOS_BOOTFLAG1) & 1)
261             CheckFloppySig = 0;
262         u32 bootorder = (rtc_read(CMOS_BIOS_BOOTFLAG2)
263                          | ((rtc_read(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
264         DefaultFloppyPrio = DefaultCDPrio = DefaultHDPrio
265             = DefaultBEVPrio = DEFAULT_PRIO;
266         int i;
267         for (i=101; i<104; i++) {
268             u32 val = bootorder & 0x0f;
269             bootorder >>= 4;
270             switch (val) {
271             case 1: DefaultFloppyPrio = i; break;
272             case 2: DefaultHDPrio = i;     break;
273             case 3: DefaultCDPrio = i;     break;
274             case 4: DefaultBEVPrio = i;    break;
275             }
276         }
277     }
278
279     BootRetryTime = romfile_loadint("etc/boot-fail-wait", 60*1000);
280
281     loadBootOrder();
282 }
283
284
285 /****************************************************************
286  * BootList handling
287  ****************************************************************/
288
289 struct bootentry_s {
290     int type;
291     union {
292         u32 data;
293         struct segoff_s vector;
294         struct drive_s *drive;
295     };
296     int priority;
297     const char *description;
298     struct hlist_node node;
299 };
300 static struct hlist_head BootList VARVERIFY32INIT;
301
302 #define IPL_TYPE_FLOPPY      0x01
303 #define IPL_TYPE_HARDDISK    0x02
304 #define IPL_TYPE_CDROM       0x03
305 #define IPL_TYPE_CBFS        0x20
306 #define IPL_TYPE_BEV         0x80
307 #define IPL_TYPE_BCV         0x81
308 #define IPL_TYPE_HALT        0xf0
309
310 static void
311 bootentry_add(int type, int prio, u32 data, const char *desc)
312 {
313     if (! CONFIG_BOOT)
314         return;
315     struct bootentry_s *be = malloc_tmp(sizeof(*be));
316     if (!be) {
317         warn_noalloc();
318         return;
319     }
320     be->type = type;
321     be->priority = prio;
322     be->data = data;
323     be->description = desc ?: "?";
324     dprintf(3, "Registering bootable: %s (type:%d prio:%d data:%x)\n"
325             , be->description, type, prio, data);
326
327     // Add entry in sorted order.
328     struct hlist_node **pprev;
329     struct bootentry_s *pos;
330     hlist_for_each_entry_pprev(pos, pprev, &BootList, node) {
331         if (be->priority < pos->priority)
332             break;
333         if (be->priority > pos->priority)
334             continue;
335         if (be->type < pos->type)
336             break;
337         if (be->type > pos->type)
338             continue;
339         if (be->type <= IPL_TYPE_CDROM
340             && (be->drive->type < pos->drive->type
341                 || (be->drive->type == pos->drive->type
342                     && be->drive->cntl_id < pos->drive->cntl_id)))
343             break;
344     }
345     hlist_add(&be->node, pprev);
346 }
347
348 // Return the given priority if it's set - defaultprio otherwise.
349 static inline int defPrio(int priority, int defaultprio) {
350     return (priority < 0) ? defaultprio : priority;
351 }
352
353 // Add a BEV vector for a given pnp compatible option rom.
354 void
355 boot_add_bev(u16 seg, u16 bev, u16 desc, int prio)
356 {
357     bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio)
358                   , SEGOFF(seg, bev).segoff
359                   , desc ? MAKE_FLATPTR(seg, desc) : "Unknown");
360     DefaultBEVPrio = DEFAULT_PRIO;
361 }
362
363 // Add a bcv entry for an expansion card harddrive or legacy option rom
364 void
365 boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio)
366 {
367     bootentry_add(IPL_TYPE_BCV, defPrio(prio, DefaultHDPrio)
368                   , SEGOFF(seg, ip).segoff
369                   , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom");
370 }
371
372 void
373 boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio)
374 {
375     bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
376                   , (u32)drive_g, desc);
377 }
378
379 void
380 boot_add_hd(struct drive_s *drive_g, const char *desc, int prio)
381 {
382     bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
383                   , (u32)drive_g, desc);
384 }
385
386 void
387 boot_add_cd(struct drive_s *drive_g, const char *desc, int prio)
388 {
389     bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
390                   , (u32)drive_g, desc);
391 }
392
393 // Add a CBFS payload entry
394 void
395 boot_add_cbfs(void *data, const char *desc, int prio)
396 {
397     bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
398 }
399
400
401 /****************************************************************
402  * Keyboard calls
403  ****************************************************************/
404
405 // See if a keystroke is pending in the keyboard buffer.
406 static int
407 check_for_keystroke(void)
408 {
409     struct bregs br;
410     memset(&br, 0, sizeof(br));
411     br.flags = F_IF|F_ZF;
412     br.ah = 1;
413     call16_int(0x16, &br);
414     return !(br.flags & F_ZF);
415 }
416
417 // Return a keystroke - waiting forever if necessary.
418 static int
419 get_raw_keystroke(void)
420 {
421     struct bregs br;
422     memset(&br, 0, sizeof(br));
423     br.flags = F_IF;
424     call16_int(0x16, &br);
425     return br.ah;
426 }
427
428 // Read a keystroke - waiting up to 'msec' milliseconds.
429 static int
430 get_keystroke(int msec)
431 {
432     u32 end = irqtimer_calc(msec);
433     for (;;) {
434         if (check_for_keystroke())
435             return get_raw_keystroke();
436         if (irqtimer_check(end))
437             return -1;
438         yield_toirq();
439     }
440 }
441
442
443 /****************************************************************
444  * Boot menu and BCV execution
445  ****************************************************************/
446
447 #define DEFAULT_BOOTMENU_WAIT 2500
448
449 // Show IPL option menu.
450 void
451 interactive_bootmenu(void)
452 {
453     // XXX - show available drives?
454
455     if (! CONFIG_BOOTMENU || !romfile_loadint("etc/show-boot-menu", 1))
456         return;
457
458     while (get_keystroke(0) >= 0)
459         ;
460
461     char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL);
462     int menukey = romfile_loadint("etc/boot-menu-key", 0x86);
463     printf("%s", bootmsg ?: "\nPress F12 for boot menu.\n\n");
464     free(bootmsg);
465
466     u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT);
467     enable_bootsplash();
468     int scan_code = get_keystroke(menutime);
469     disable_bootsplash();
470     if (scan_code != menukey)
471         return;
472
473     while (get_keystroke(0) >= 0)
474         ;
475
476     printf("Select boot device:\n\n");
477     wait_threads();
478
479     // Show menu items
480     int maxmenu = 0;
481     struct bootentry_s *pos;
482     hlist_for_each_entry(pos, &BootList, node) {
483         char desc[60];
484         maxmenu++;
485         printf("%d. %s\n", maxmenu
486                , strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
487     }
488
489     // Get key press
490     for (;;) {
491         scan_code = get_keystroke(1000);
492         if (scan_code >= 1 && scan_code <= maxmenu+1)
493             break;
494     }
495     printf("\n");
496     if (scan_code == 0x01)
497         // ESC
498         return;
499
500     // Find entry and make top priority.
501     int choice = scan_code - 1;
502     hlist_for_each_entry(pos, &BootList, node) {
503         if (! --choice)
504             break;
505     }
506     hlist_del(&pos->node);
507     pos->priority = 0;
508     hlist_add_head(&pos->node, &BootList);
509 }
510
511 // BEV (Boot Execution Vector) list
512 struct bev_s {
513     int type;
514     u32 vector;
515 };
516 static struct bev_s BEV[20];
517 static int BEVCount;
518 static int HaveHDBoot, HaveFDBoot;
519
520 static void
521 add_bev(int type, u32 vector)
522 {
523     if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
524         return;
525     if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
526         return;
527     if (BEVCount >= ARRAY_SIZE(BEV))
528         return;
529     struct bev_s *bev = &BEV[BEVCount++];
530     bev->type = type;
531     bev->vector = vector;
532 }
533
534 // Prepare for boot - show menu and run bcvs.
535 void
536 bcv_prepboot(void)
537 {
538     if (! CONFIG_BOOT)
539         return;
540
541     int haltprio = find_prio("HALT");
542     if (haltprio >= 0)
543         bootentry_add(IPL_TYPE_HALT, haltprio, 0, "HALT");
544
545     // Map drives and populate BEV list
546     struct bootentry_s *pos;
547     hlist_for_each_entry(pos, &BootList, node) {
548         switch (pos->type) {
549         case IPL_TYPE_BCV:
550             call_bcv(pos->vector.seg, pos->vector.offset);
551             add_bev(IPL_TYPE_HARDDISK, 0);
552             break;
553         case IPL_TYPE_FLOPPY:
554             map_floppy_drive(pos->drive);
555             add_bev(IPL_TYPE_FLOPPY, 0);
556             break;
557         case IPL_TYPE_HARDDISK:
558             map_hd_drive(pos->drive);
559             add_bev(IPL_TYPE_HARDDISK, 0);
560             break;
561         case IPL_TYPE_CDROM:
562             map_cd_drive(pos->drive);
563             // NO BREAK
564         default:
565             add_bev(pos->type, pos->data);
566             break;
567         }
568     }
569
570     // If nothing added a floppy/hd boot - add it manually.
571     add_bev(IPL_TYPE_FLOPPY, 0);
572     add_bev(IPL_TYPE_HARDDISK, 0);
573 }
574
575
576 /****************************************************************
577  * Boot code (int 18/19)
578  ****************************************************************/
579
580 // Jump to a bootup entry point.
581 static void
582 call_boot_entry(struct segoff_s bootsegip, u8 bootdrv)
583 {
584     dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset);
585     struct bregs br;
586     memset(&br, 0, sizeof(br));
587     br.flags = F_IF;
588     br.code = bootsegip;
589     // Set the magic number in ax and the boot drive in dl.
590     br.dl = bootdrv;
591     br.ax = 0xaa55;
592     farcall16(&br);
593 }
594
595 // Boot from a disk (either floppy or harddrive)
596 static void
597 boot_disk(u8 bootdrv, int checksig)
598 {
599     u16 bootseg = 0x07c0;
600
601     // Read sector
602     struct bregs br;
603     memset(&br, 0, sizeof(br));
604     br.flags = F_IF;
605     br.dl = bootdrv;
606     br.es = bootseg;
607     br.ah = 2;
608     br.al = 1;
609     br.cl = 1;
610     call16_int(0x13, &br);
611
612     if (br.flags & F_CF) {
613         printf("Boot failed: could not read the boot disk\n\n");
614         return;
615     }
616
617     if (checksig) {
618         struct mbr_s *mbr = (void*)0;
619         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
620             printf("Boot failed: not a bootable disk\n\n");
621             return;
622         }
623     }
624
625     /* Canonicalize bootseg:bootip */
626     u16 bootip = (bootseg & 0x0fff) << 4;
627     bootseg &= 0xf000;
628
629     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
630 }
631
632 // Boot from a CD-ROM
633 static void
634 boot_cdrom(struct drive_s *drive_g)
635 {
636     if (! CONFIG_CDROM_BOOT)
637         return;
638     printf("Booting from DVD/CD...\n");
639
640     int status = cdrom_boot(drive_g);
641     if (status) {
642         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
643         return;
644     }
645
646     u8 bootdrv = CDEmu.emulated_drive;
647     u16 bootseg = CDEmu.load_segment;
648     /* Canonicalize bootseg:bootip */
649     u16 bootip = (bootseg & 0x0fff) << 4;
650     bootseg &= 0xf000;
651
652     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
653 }
654
655 // Boot from a CBFS payload
656 static void
657 boot_cbfs(struct cbfs_file *file)
658 {
659     if (!CONFIG_COREBOOT_FLASH)
660         return;
661     printf("Booting from CBFS...\n");
662     cbfs_run_payload(file);
663 }
664
665 // Boot from a BEV entry on an optionrom.
666 static void
667 boot_rom(u32 vector)
668 {
669     printf("Booting from ROM...\n");
670     struct segoff_s so;
671     so.segoff = vector;
672     call_boot_entry(so, 0);
673 }
674
675 // Unable to find bootable device - warn user and eventually retry.
676 static void
677 boot_fail(void)
678 {
679     if (BootRetryTime == (u32)-1)
680         printf("No bootable device.\n");
681     else
682         printf("No bootable device.  Retrying in %d seconds.\n"
683                , BootRetryTime/1000);
684     // Wait for 'BootRetryTime' milliseconds and then reboot.
685     u32 end = irqtimer_calc(BootRetryTime);
686     for (;;) {
687         if (BootRetryTime != (u32)-1 && irqtimer_check(end))
688             break;
689         yield_toirq();
690     }
691     printf("Rebooting.\n");
692     reset();
693 }
694
695 // Determine next boot method and attempt a boot using it.
696 static void
697 do_boot(int seq_nr)
698 {
699     if (! CONFIG_BOOT)
700         panic("Boot support not compiled in.\n");
701
702     if (seq_nr >= BEVCount)
703         boot_fail();
704
705     // Boot the given BEV type.
706     struct bev_s *ie = &BEV[seq_nr];
707     switch (ie->type) {
708     case IPL_TYPE_FLOPPY:
709         printf("Booting from Floppy...\n");
710         boot_disk(0x00, CheckFloppySig);
711         break;
712     case IPL_TYPE_HARDDISK:
713         printf("Booting from Hard Disk...\n");
714         boot_disk(0x80, 1);
715         break;
716     case IPL_TYPE_CDROM:
717         boot_cdrom((void*)ie->vector);
718         break;
719     case IPL_TYPE_CBFS:
720         boot_cbfs((void*)ie->vector);
721         break;
722     case IPL_TYPE_BEV:
723         boot_rom(ie->vector);
724         break;
725     case IPL_TYPE_HALT:
726         boot_fail();
727         break;
728     }
729
730     // Boot failed: invoke the boot recovery function
731     struct bregs br;
732     memset(&br, 0, sizeof(br));
733     br.flags = F_IF;
734     call16_int(0x18, &br);
735 }
736
737 int BootSequence VARLOW = -1;
738
739 // Boot Failure recovery: try the next device.
740 void VISIBLE32FLAT
741 handle_18(void)
742 {
743     debug_enter(NULL, DEBUG_HDL_18);
744     int seq = BootSequence + 1;
745     BootSequence = seq;
746     do_boot(seq);
747 }
748
749 // INT 19h Boot Load Service Entry Point
750 void VISIBLE32FLAT
751 handle_19(void)
752 {
753     debug_enter(NULL, DEBUG_HDL_19);
754     BootSequence = 0;
755     do_boot(0);
756 }