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