1 /*
2  * Mini-OS support for GRUB.
3  *
4  * Samuel Thibault <Samuel.Thibault@eu.citrix.com>, May 2008
5  */
6 #include <sys/types.h>
7 #include <sys/time.h>
8 #include <stdarg.h>
9 #include <stdlib.h>
10 #include <malloc.h>
11 #include <unistd.h>
12 
13 #include <hypervisor.h>
14 #include <blkfront.h>
15 #include <netfront.h>
16 #include <fbfront.h>
17 #include <semaphore.h>
18 
19 #include <osdep.h>
20 #include <shared.h>
21 #include <nic.h>
22 #include <etherboot.h>
23 #include <terminfo.h>
24 #include <term.h>
25 
26 #include "mini-os.h"
27 
28 extern const char *preset_menu;
29 char config_file[DEFAULT_FILE_BUFLEN] = "(hd0,0)/boot/grub/menu.lst";
30 unsigned long boot_drive = NETWORK_DRIVE;
31 unsigned long install_partition = 0xFFFFFF;
32 
33 char version_string[] = VERSION;
34 
35 /* Variables from asm.S */
36 int saved_entryno;
37 
38 /*
39  * Disk
40  */
41 
42 struct blkfront_dev **blk_dev;
43 int blk_nb;
44 static struct blkfront_info *blk_info;
45 
vbdcmp(const void * _vbd1,const void * _vbd2)46 static int vbdcmp(const void *_vbd1, const void *_vbd2) {
47     char *vbd1 = *(char **)_vbd1;
48     char *vbd2 = *(char **)_vbd2;
49     int vbdn1 = atoi(vbd1);
50     int vbdn2 = atoi(vbd2);
51     return vbdn1 - vbdn2;
52 }
53 
init_disk(void)54 void init_disk (void)
55 {
56     char **list;
57     char *msg;
58     int i;
59     char *path;
60 
61     msg = xenbus_ls(XBT_NIL, "device/vbd", &list);
62     if (msg) {
63         printk("Error %s while reading list of disks\n", msg);
64         free(msg);
65         return;
66     }
67     blk_nb = 0;
68     while (list[blk_nb])
69         blk_nb++;
70     blk_dev = malloc(blk_nb * sizeof(*blk_dev));
71     blk_info = malloc(blk_nb * sizeof(*blk_info));
72 
73     qsort(list, blk_nb, sizeof(*list), vbdcmp);
74 
75     for (i = 0; i < blk_nb; i++) {
76         printk("vbd %s is hd%d\n", list[i], i);
77         asprintf(&path, "device/vbd/%s", list[i]);
78         blk_dev[i] = init_blkfront(path, &blk_info[i]);
79         free(path);
80         free(list[i]);
81     }
82 }
83 
84 /* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return
85    non-zero, otherwise zero.  */
get_diskinfo(int drive,struct geometry * geometry)86 int get_diskinfo (int drive, struct geometry *geometry)
87 {
88     int i;
89     if (!(drive & 0x80))
90         return -1;
91 
92     i = drive - 0x80;
93     if (i >= blk_nb)
94         return -1;
95 
96     /* Bogus geometry */
97     geometry->cylinders = 65535;
98     geometry->heads = 255;
99     geometry->sectors = 63;
100 
101     geometry->total_sectors = blk_info[i].sectors;
102     geometry->sector_size = blk_info[i].sector_size;
103     geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
104     if (blk_info[i].info & VDISK_CDROM)
105         geometry->flags |= BIOSDISK_FLAG_CDROM;
106     return 0;
107 }
108 
109 /* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY
110    from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it,
111    else if READ is BIOSDISK_WRITE, then write it. If an geometry error
112    occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then
113    return the error number. Otherwise, return 0.  */
114 int
biosdisk(int read,int drive,struct geometry * geometry,unsigned int sector,int nsec,int segment)115 biosdisk (int read, int drive, struct geometry *geometry,
116           unsigned int sector, int nsec, int segment)
117 {
118     void *addr = (void *) ((unsigned long)segment << 4);
119     struct blkfront_aiocb aiocb;
120     int i;
121 
122     if (!(drive & 0x80))
123         return -1;
124 
125     i = drive - 0x80;
126     if (i >= blk_nb)
127         return -1;
128 
129     if (sector + nsec > geometry->total_sectors)
130       return -1;
131 
132     aiocb.aio_dev = blk_dev[i];
133     aiocb.aio_buf = addr;
134     aiocb.aio_nbytes = (size_t)nsec * blk_info[i].sector_size;
135     aiocb.aio_offset = (off_t)sector * blk_info[i].sector_size;
136     aiocb.aio_cb = NULL;
137 
138     blkfront_io(&aiocb, read == BIOSDISK_WRITE);
139 
140     return 0;
141 }
142 
143 static int
load_file(char * name,void ** ptr,long * size)144 load_file(char *name, void **ptr, long *size)
145 {
146     char *buf = NULL;
147     int allocated = 1 * 1024 * 1024;
148     int len, filled = 0;
149 
150     if (!grub_open (name))
151         return -1;
152 
153     buf = malloc(allocated);
154 
155     errnum = 0;
156     while (1) {
157         len = grub_read (buf + filled, allocated - filled);
158         if (! len) {
159             if (!errnum)
160                 break;
161             grub_close ();
162             return -1;
163         }
164         filled += len;
165         if (filled < allocated)
166             break;
167         allocated *= 2;
168         buf = realloc(buf, allocated);
169     }
170     grub_close ();
171     *ptr = buf;
172     *size = filled;
173     return 0;
174 }
175 
176 void *kernel_image, *module_image;
177 long  kernel_size, module_size;
178 char *kernel_arg, *module_arg;
179 void *multiboot_next_module;
180 struct xen_multiboot_mod_list *multiboot_next_module_header;
181 
182 kernel_t
load_image(char * kernel,char * arg,kernel_t suggested_type,unsigned long load_flags)183 load_image (char *kernel, char *arg, kernel_t suggested_type,
184             unsigned long load_flags)
185 {
186     arg = skip_to(0, arg);
187     if (kernel_image)
188         free(kernel_image);
189     kernel_image = NULL;
190     if (load_file (kernel, &kernel_image, &kernel_size))
191         return KERNEL_TYPE_NONE;
192     if (kernel_arg)
193         free(kernel_arg);
194     kernel_arg = strdup(arg);
195     return KERNEL_TYPE_PV;
196 }
197 
198 int
load_initrd(char * initrd)199 load_initrd (char *initrd)
200 {
201     if (module_image)
202         free(module_image);
203     module_image = NULL;
204     multiboot_next_module = NULL;
205     multiboot_next_module_header = NULL;
206     load_file (initrd, &module_image, &module_size);
207     return ! errnum;
208 }
209 
210 int
load_module(char * module,char * arg)211 load_module (char *module, char *arg)
212 {
213     void *new_module, *new_module_image;
214     long new_module_size, rounded_new_module_size;
215 
216     if (load_file (module, &new_module, &new_module_size))
217         return 0;
218     if (strlen(arg) >= PAGE_SIZE) {
219         /* Too big module command line */
220         errnum = ERR_WONT_FIT;
221         return 0;
222     }
223     rounded_new_module_size = (new_module_size + PAGE_SIZE - 1) & PAGE_MASK;
224 
225     if (module_image && !multiboot_next_module_header) {
226         /* Initrd already loaded, drop it */
227         free(module_image);
228         if (module_arg)
229             free(module_arg);
230         module_image = NULL;
231     }
232     if (!module_image)
233         /* Reserve one page for the header */
234         multiboot_next_module = (void*) PAGE_SIZE;
235 
236     /* Allocate more room for the new module plus its arg */
237     new_module_image = realloc(module_image,
238             (multiboot_next_module - module_image) + rounded_new_module_size + PAGE_SIZE);
239 
240     /* Update pointers */
241     multiboot_next_module += new_module_image - module_image;
242     multiboot_next_module_header = (void*) multiboot_next_module_header + (new_module_image - module_image);
243     module_image = new_module_image;
244 
245     if ((void*) (multiboot_next_module_header+1) - module_image > PAGE_SIZE) {
246         /* Too many modules */
247         errnum = ERR_WONT_FIT;
248         return 0;
249     }
250 
251     /* Copy module */
252     memcpy(multiboot_next_module, new_module, new_module_size);
253     multiboot_next_module_header->mod_start = multiboot_next_module - module_image;
254     multiboot_next_module_header->mod_end = multiboot_next_module_header->mod_start + new_module_size - 1;
255     multiboot_next_module += rounded_new_module_size;
256 
257     /* Copy cmdline */
258     strcpy(multiboot_next_module, arg);
259     multiboot_next_module_header->cmdline = multiboot_next_module - module_image;
260     multiboot_next_module += PAGE_SIZE;
261 
262     /* Pad */
263     multiboot_next_module_header->pad = 0;
264 
265     multiboot_next_module_header++;
266 
267     return 1;
268 }
269 
270 void
pv_boot(void)271 pv_boot (void)
272 {
273     unsigned long flags = 0;
274     if (multiboot_next_module_header) {
275         /* Termination entry */
276         multiboot_next_module_header->mod_start = 0;
277         /* Total size */
278         module_size = multiboot_next_module - module_image;
279         /* It's a multiboot module */
280         flags |= SIF_MULTIBOOT_MOD;
281     }
282     kexec(kernel_image, kernel_size, module_image, module_size, kernel_arg, flags);
283 }
284 
285 /*
286  * Network
287  */
288 
289 struct netfront_dev *net_dev;
290 
291 int
minios_probe(struct nic * nic)292 minios_probe (struct nic *nic)
293 {
294     char *ip;
295 
296     if (net_dev)
297         return 1;
298 
299     /* Clear the ARP table.  */
300     grub_memset ((char *) arptable, 0,
301                  MAX_ARP * sizeof (struct arptable_t));
302 
303     net_dev = init_netfront(NULL, (void*) -1, nic->node_addr, &ip);
304     if (!net_dev)
305         return 0;
306 
307     return 1;
308 }
309 
310 /* reset adapter */
minios_reset(struct nic * nic)311 static void minios_reset(struct nic *nic)
312 {
313     /* TODO? */
314 }
315 
minios_disable(struct nic * nic)316 static void minios_disable(struct nic *nic)
317 {
318 }
319 
320 /* Wait for a frame */
minios_poll(struct nic * nic)321 static int minios_poll(struct nic *nic)
322 {
323     return !! (nic->packetlen = netfront_receive(net_dev, (void*) nic->packet, ETH_FRAME_LEN));
324 }
325 
326 /* Transmit a frame */
327 struct frame {
328         uint8_t dest[ETH_ALEN];
329         uint8_t src[ETH_ALEN];
330         uint16_t type;
331         unsigned char data[];
332 };
minios_transmit(struct nic * nic,const char * d,unsigned int t,unsigned int s,const char * p)333 static void minios_transmit (struct nic *nic, const char *d, unsigned int t,
334                              unsigned int s, const char *p)
335 {
336     struct frame *frame = alloca(sizeof(*frame) + s);
337 
338     memcpy(frame->dest, d, ETH_ALEN);
339     memcpy(frame->src, nic->node_addr, ETH_ALEN);
340     frame->type = htons(t);
341     memcpy(frame->data, p, s);
342 
343     netfront_xmit(net_dev, (void*) frame, sizeof(*frame) + s);
344 }
345 
346 static char packet[ETH_FRAME_LEN];
347 
348 struct nic nic = {
349     .reset = minios_reset,
350     .poll = minios_poll,
351     .transmit = minios_transmit,
352     .disable = minios_disable,
353     .flags = 0,
354     .rom_info = NULL,
355     .node_addr = arptable[ARP_CLIENT].node,
356     .packet = packet,
357     .packetlen = 0,
358     .priv_data = NULL,
359 };
360 
361 int
eth_probe(void)362 eth_probe (void)
363 {
364     return minios_probe(&nic);
365 }
366 
367 int
eth_poll(void)368 eth_poll (void)
369 {
370     return minios_poll (&nic);
371 }
372 
373 void
eth_disable(void)374 eth_disable (void)
375 {
376     minios_disable (&nic);
377 }
378 
379 void
eth_transmit(const char * d,unsigned int t,unsigned int s,const void * p)380 eth_transmit (const char *d, unsigned int t,
381               unsigned int s, const void *p)
382 {
383     minios_transmit (&nic, d, t, s, p);
384     if (t == IP)
385         twiddle();
386 }
387 
388 /*
389  * Console
390  */
391 void
serial_hw_put(int _c)392 serial_hw_put (int _c)
393 {
394   char c = _c;
395   console_print(NULL, &c, 1);
396 }
397 
398 int
serial_hw_fetch(void)399 serial_hw_fetch (void)
400 {
401     char key;
402 
403     if (!xencons_ring_avail(NULL))
404         return -1;
405 
406     read(STDIN_FILENO, &key, 1);
407     switch (key) {
408     case 0x7f: key = '\b'; break;
409     }
410     return key;
411 }
412 
413 /*
414  * PVFB
415  */
416 struct kbdfront_dev *kbd_dev;
417 struct fbfront_dev *fb_dev;
418 static union xenkbd_in_event ev;
419 static int has_ev;
console_checkkey(void)420 int console_checkkey (void)
421 {
422     if (has_ev)
423         return 1;
424     has_ev = kbdfront_receive(kbd_dev, &ev, 1);
425     return has_ev;
426 }
427 
428 /* static QWERTY layout, that's what most PC BIOSes do anyway */
429 static char linux2ascii[] = {
430     [ 1 ] = 27,
431     [ 2 ] = '1',
432     [ 3 ] = '2',
433     [ 4 ] = '3',
434     [ 5 ] = '4',
435     [ 6 ] = '5',
436     [ 7 ] = '6',
437     [ 8 ] = '7',
438     [ 9 ] = '8',
439     [ 10 ] = '9',
440     [ 11 ] = '0',
441     [ 12 ] = '-',
442     [ 13 ] = '=',
443     [ 14 ] = '\b',
444     [ 15 ] = '\t',
445     [ 16 ] = 'q',
446     [ 17 ] = 'w',
447     [ 18 ] = 'e',
448     [ 19 ] = 'r',
449     [ 20 ] = 't',
450     [ 21 ] = 'y',
451     [ 22 ] = 'u',
452     [ 23 ] = 'i',
453     [ 24 ] = 'o',
454     [ 25 ] = 'p',
455     [ 26 ] = '[',
456     [ 27 ] = ']',
457     [ 28 ] = '\n',
458 
459     [ 30 ] = 'a',
460     [ 31 ] = 's',
461     [ 32 ] = 'd',
462     [ 33 ] = 'f',
463     [ 34 ] = 'g',
464     [ 35 ] = 'h',
465     [ 36 ] = 'j',
466     [ 37 ] = 'k',
467     [ 38 ] = 'l',
468     [ 39 ] = ';',
469     [ 40 ] = '\'',
470     [ 41 ] = '`',
471 
472     [ 43 ] = '\\',
473     [ 44 ] = 'z',
474     [ 45 ] = 'x',
475     [ 46 ] = 'c',
476     [ 47 ] = 'v',
477     [ 48 ] = 'b',
478     [ 49 ] = 'n',
479     [ 50 ] = 'm',
480     [ 51 ] = ',',
481     [ 52 ] = '.',
482     [ 53 ] = '/',
483 
484     [ 55 ] = '*',
485     [ 57 ] = ' ',
486 
487     [ 71 ] = '7',
488     [ 72 ] = '8',
489     [ 73 ] = '9',
490     [ 74 ] = '-',
491     [ 75 ] = '4',
492     [ 76 ] = '5',
493     [ 77 ] = '6',
494     [ 78 ] = '+',
495     [ 79 ] = '1',
496     [ 80 ] = '2',
497     [ 81 ] = '3',
498     [ 82 ] = '0',
499     [ 83 ] = '.',
500 
501     [ 86 ] = '<',
502 
503     [ 96 ] = '\n',
504 
505     [ 98 ] = '/',
506 
507     [ 102 ] = 1,  /* home */
508     [ 103 ] = 16, /* up */
509     [ 104 ] = 7,  /* page up */
510     [ 105 ] = 2,  /* left */
511     [ 106 ] = 6,  /* right */
512     [ 107 ] = 5,  /* end */
513     [ 108 ] = 14, /* down */
514     [ 109 ] = 3,  /* page down */
515 
516     [ 111 ] = 4,  /* delete */
517 };
518 
519 static char linux2ascii_shifted[] = {
520     [ 1 ] = 27,
521     [ 2 ] = '!',
522     [ 3 ] = '@',
523     [ 4 ] = '#',
524     [ 5 ] = '$',
525     [ 6 ] = '%',
526     [ 7 ] = '^',
527     [ 8 ] = '&',
528     [ 9 ] = '*',
529     [ 10 ] = '(',
530     [ 11 ] = ')',
531     [ 12 ] = '_',
532     [ 13 ] = '+',
533     [ 14 ] = '\b',
534     [ 15 ] = '\t',
535     [ 16 ] = 'Q',
536     [ 17 ] = 'W',
537     [ 18 ] = 'E',
538     [ 19 ] = 'R',
539     [ 20 ] = 'T',
540     [ 21 ] = 'Y',
541     [ 22 ] = 'U',
542     [ 23 ] = 'I',
543     [ 24 ] = 'O',
544     [ 25 ] = 'P',
545     [ 26 ] = '{',
546     [ 27 ] = '}',
547     [ 28 ] = '\n',
548 
549     [ 30 ] = 'A',
550     [ 31 ] = 'S',
551     [ 32 ] = 'D',
552     [ 33 ] = 'F',
553     [ 34 ] = 'G',
554     [ 35 ] = 'H',
555     [ 36 ] = 'J',
556     [ 37 ] = 'K',
557     [ 38 ] = 'L',
558     [ 39 ] = ':',
559     [ 40 ] = '"',
560     [ 41 ] = '~',
561 
562     [ 43 ] = '|',
563     [ 44 ] = 'Z',
564     [ 45 ] = 'X',
565     [ 46 ] = 'C',
566     [ 47 ] = 'V',
567     [ 48 ] = 'B',
568     [ 49 ] = 'N',
569     [ 50 ] = 'M',
570     [ 51 ] = '<',
571     [ 52 ] = '>',
572     [ 53 ] = '?',
573 
574     [ 55 ] = '*',
575     [ 57 ] = ' ',
576 
577     [ 71 ] = '7',
578     [ 72 ] = '8',
579     [ 73 ] = '9',
580     [ 74 ] = '-',
581     [ 75 ] = '4',
582     [ 76 ] = '5',
583     [ 77 ] = '6',
584     [ 78 ] = '+',
585     [ 79 ] = '1',
586     [ 80 ] = '2',
587     [ 81 ] = '3',
588     [ 82 ] = '0',
589     [ 83 ] = '.',
590 
591     [ 86 ] = '>',
592 
593     [ 96 ] = '\n',
594 
595     [ 98 ] = '/',
596 
597     [ 102 ] = 1,  /* home */
598     [ 103 ] = 16, /* up */
599     [ 104 ] = 7,  /* page up */
600     [ 105 ] = 2,  /* left */
601     [ 106 ] = 6,  /* right */
602     [ 107 ] = 5,  /* end */
603     [ 108 ] = 14, /* down */
604     [ 109 ] = 3,  /* page down */
605 
606     [ 111 ] = 4,  /* delete */
607 };
608 
console_getkey(void)609 int console_getkey (void)
610 {
611     static int shift, control, alt, caps_lock;
612 
613     if (!has_ev)
614         has_ev = kbdfront_receive(kbd_dev, &ev, 1);
615     if (!has_ev)
616         return 0;
617 
618     has_ev = 0;
619     if (ev.type != XENKBD_TYPE_KEY)
620         return 0;
621 
622     if (ev.key.keycode == 42 || ev.key.keycode == 54) {
623         caps_lock = 0;
624         shift = ev.key.pressed;
625         return 0;
626     }
627     if (ev.key.keycode == 58) {
628         caps_lock ^= 1;
629         return 0;
630     }
631     if (ev.key.keycode == 29 || ev.key.keycode == 97) {
632         control = ev.key.pressed;
633         return 0;
634     }
635     if (ev.key.keycode == 56) {
636         alt = ev.key.pressed;
637         return 0;
638     }
639 
640     if (!ev.key.pressed)
641         return 0;
642 
643     if (ev.key.keycode < sizeof(linux2ascii) / sizeof(*linux2ascii)) {
644         char val;
645         if (shift || caps_lock)
646             val = linux2ascii_shifted[ev.key.keycode];
647         else
648             val = linux2ascii[ev.key.keycode];
649         if (control)
650             val &= ~0x60;
651         return val;
652     }
653 
654     return 0;
655 }
656 
657 static DECLARE_MUTEX_LOCKED(kbd_sem);
kbd_thread(void * p)658 static void kbd_thread(void *p)
659 {
660     kbd_dev = init_kbdfront(NULL, 1);
661     up(&kbd_sem);
662 }
663 
fb_open(void * fb,int width,int height,int depth)664 struct fbfront_dev *fb_open(void *fb, int width, int height, int depth)
665 {
666     unsigned long *mfns;
667     int linesize = width * (depth / 8);
668     int memsize = linesize * height;
669     int numpages = (memsize + PAGE_SIZE - 1) / PAGE_SIZE;
670     int i;
671 
672     create_thread("kbdfront", kbd_thread, &kbd_sem);
673 
674     mfns = malloc(numpages * sizeof(*mfns));
675     for (i = 0; i < numpages; i++) {
676         memset(fb + i * PAGE_SIZE, 0, PAGE_SIZE);
677         mfns[i] = virtual_to_mfn(fb + i * PAGE_SIZE);
678     }
679     fb_dev = init_fbfront(NULL, mfns, width, height, depth, linesize, numpages);
680     free(mfns);
681 
682     if (!fb_dev)
683         return NULL;
684 
685     down(&kbd_sem);
686     if (!kbd_dev)
687         return NULL;
688 
689     return fb_dev;
690 }
691 
kbd_close(void * foo)692 void kbd_close(void *foo)
693 {
694     shutdown_kbdfront(kbd_dev);
695     kbd_dev = NULL;
696 }
697 
fb_close(void)698 void fb_close(void)
699 {
700     create_thread("kbdfront close", kbd_close, NULL);
701     shutdown_fbfront(fb_dev);
702     fb_dev = NULL;
703 }
704 
705 /*
706  * Misc
707  */
708 
getrtsecs(void)709 int getrtsecs (void)
710 {
711     struct timeval tv;
712     gettimeofday(&tv, NULL);
713     return tv.tv_sec % 10 + ((tv.tv_sec / 10) % 6) * 0x10;
714 }
715 
currticks(void)716 int currticks (void)
717 {
718     struct timeval tv;
719     gettimeofday(&tv, NULL);
720     return ((tv.tv_sec * 1000000ULL + tv.tv_usec) * TICKS_PER_SEC) / 1000000;
721 }
722 
grub_reboot(void)723 void __attribute__ ((noreturn)) grub_reboot (void)
724 {
725     for ( ;; )
726     {
727         struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_reboot };
728         HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown);
729     }
730 }
731 
732 #define SCRATCH_MEMSIZE (4 * 1024 * 1024)
733 
734 /* Note: not allocating it dynamically permits to make sure it lays below 4G
735  * for grub's 32bit pointers to work */
736 char grub_scratch_mem[SCRATCH_MEMSIZE] __attribute__((aligned(PAGE_SIZE)));
737 
main(int argc,char ** argv)738 int main(int argc, char **argv)
739 {
740     if (argc > 1 && memcmp(argv[1], "--vtpm-label=", 13) == 0) {
741         vtpm_label = argv[1] + 13;
742         argc--;
743         argv++;
744     }
745 
746     if (argc > 1) {
747         strncpy(config_file, argv[1], sizeof(config_file) - 1);
748         config_file[sizeof(config_file) - 1] = 0;
749         if (!strncmp(config_file, "(nd)", 4))
750             preset_menu = "dhcp";
751     } else if (start_info.mod_len)
752         preset_menu = (void*) start_info.mod_start;
753     else
754         preset_menu = "dhcp --with-configfile";
755 
756     mbi.drives_addr = BOOTSEC_LOCATION + (60 * 1024);
757     mbi.drives_length = 0;
758 
759     mbi.boot_loader_name = (unsigned long) "GNU GRUB " VERSION;
760     mbi.mem_lower = (start_info.nr_pages * PAGE_SIZE) / 1024;
761     mbi.mem_upper = 0;
762     saved_drive = boot_drive;
763     saved_partition = install_partition;
764 
765     init_disk();
766 
767     /* Try to make sure the client part got launched */
768     sleep(1);
769     cmain();
770     printk("cmain returned!\n");
771 }
772