debuggers.hg

view stubdom/grub/mini-os.c @ 22848:6341fe0f4e5a

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