ia64/xen-unstable

view stubdom/grub/mini-os.c @ 18026:f454f2cac170

x86 hvm: New boot option 'softtsc' to cause RDTSC to be trapped-and-emulated.

Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jul 10 15:45:18 2008 +0100 (2008-07-10)
parents c8d9ade45781
children 9361e140fee3
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;
177 kernel_t
178 load_image (char *kernel, char *arg, kernel_t suggested_type,
179 unsigned long load_flags)
180 {
181 arg = skip_to(0, arg);
182 if (kernel_image)
183 free(kernel_image);
184 kernel_image = NULL;
185 if (load_file (kernel, &kernel_image, &kernel_size))
186 return KERNEL_TYPE_NONE;
187 if (kernel_arg)
188 free(kernel_arg);
189 kernel_arg = strdup(arg);
190 return KERNEL_TYPE_PV;
191 }
193 int
194 load_initrd (char *initrd)
195 {
196 if (module_image)
197 free(module_image);
198 module_image = NULL;
199 load_file (initrd, &module_image, &module_size);
200 return ! errnum;
201 }
203 int
204 load_module (char *module, char *arg)
205 {
206 if (module_image)
207 free(module_image);
208 module_image = NULL;
209 load_file (module, &module_image, &module_size);
210 if (module_arg)
211 free(module_arg);
212 module_arg = strdup(arg);
213 return ! errnum;
214 }
216 void
217 pv_boot (void)
218 {
219 kexec(kernel_image, kernel_size, module_image, module_size, kernel_arg);
220 }
222 /*
223 * Network
224 */
226 struct netfront_dev *net_dev;
228 int
229 minios_probe (struct nic *nic)
230 {
231 char *ip;
233 if (net_dev)
234 return 1;
236 /* Clear the ARP table. */
237 grub_memset ((char *) arptable, 0,
238 MAX_ARP * sizeof (struct arptable_t));
240 net_dev = init_netfront(NULL, (void*) -1, nic->node_addr, &ip);
241 if (!net_dev)
242 return 0;
244 return 1;
245 }
247 /* reset adapter */
248 static void minios_reset(struct nic *nic)
249 {
250 /* TODO? */
251 }
253 static void minios_disable(struct nic *nic)
254 {
255 }
257 /* Wait for a frame */
258 static int minios_poll(struct nic *nic)
259 {
260 return !! (nic->packetlen = netfront_receive(net_dev, (void*) nic->packet, ETH_FRAME_LEN));
261 }
263 /* Transmit a frame */
264 struct frame {
265 uint8_t dest[ETH_ALEN];
266 uint8_t src[ETH_ALEN];
267 uint16_t type;
268 unsigned char data[];
269 };
270 static void minios_transmit (struct nic *nic, const char *d, unsigned int t,
271 unsigned int s, const char *p)
272 {
273 struct frame *frame = alloca(sizeof(frame) + s);
275 memcpy(frame->dest, d, ETH_ALEN);
276 memcpy(frame->src, nic->node_addr, ETH_ALEN);
277 frame->type = htons(t);
278 memcpy(frame->data, p, s);
280 netfront_xmit(net_dev, (void*) frame, sizeof(*frame) + s);
281 }
283 static char packet[ETH_FRAME_LEN];
285 struct nic nic = {
286 .reset = minios_reset,
287 .poll = minios_poll,
288 .transmit = minios_transmit,
289 .disable = minios_disable,
290 .flags = 0,
291 .rom_info = NULL,
292 .node_addr = arptable[ARP_CLIENT].node,
293 .packet = packet,
294 .packetlen = 0,
295 .priv_data = NULL,
296 };
298 int
299 eth_probe (void)
300 {
301 return minios_probe(&nic);
302 }
304 int
305 eth_poll (void)
306 {
307 return minios_poll (&nic);
308 }
310 void
311 eth_disable (void)
312 {
313 minios_disable (&nic);
314 }
316 void
317 eth_transmit (const char *d, unsigned int t,
318 unsigned int s, const void *p)
319 {
320 minios_transmit (&nic, d, t, s, p);
321 if (t == IP)
322 twiddle();
323 }
325 /*
326 * Console
327 */
328 void
329 serial_hw_put (int _c)
330 {
331 char c = _c;
332 console_print(&c, 1);
333 }
335 int
336 serial_hw_fetch (void)
337 {
338 char key;
340 if (!xencons_ring_avail())
341 return -1;
343 read(STDIN_FILENO, &key, 1);
344 switch (key) {
345 case 0x7f: key = '\b'; break;
346 }
347 return key;
348 }
350 /*
351 * PVFB
352 */
353 struct kbdfront_dev *kbd_dev;
354 struct fbfront_dev *fb_dev;
355 static union xenkbd_in_event ev;
356 static int has_ev;
357 int console_checkkey (void)
358 {
359 if (has_ev)
360 return 1;
361 has_ev = kbdfront_receive(kbd_dev, &ev, 1);
362 return has_ev;
363 }
365 /* static QWERTY layout, that's what most PC BIOSes do anyway */
366 static char linux2ascii[] = {
367 [ 1 ] = 27,
368 [ 2 ] = '1',
369 [ 3 ] = '2',
370 [ 4 ] = '3',
371 [ 5 ] = '4',
372 [ 6 ] = '5',
373 [ 7 ] = '6',
374 [ 8 ] = '7',
375 [ 9 ] = '8',
376 [ 10 ] = '9',
377 [ 11 ] = '0',
378 [ 12 ] = '-',
379 [ 13 ] = '=',
380 [ 14 ] = '\b',
381 [ 15 ] = '\t',
382 [ 16 ] = 'q',
383 [ 17 ] = 'w',
384 [ 18 ] = 'e',
385 [ 19 ] = 'r',
386 [ 20 ] = 't',
387 [ 21 ] = 'y',
388 [ 22 ] = 'u',
389 [ 23 ] = 'i',
390 [ 24 ] = 'o',
391 [ 25 ] = 'p',
392 [ 26 ] = '[',
393 [ 27 ] = ']',
394 [ 28 ] = '\n',
396 [ 30 ] = 'a',
397 [ 31 ] = 's',
398 [ 32 ] = 'd',
399 [ 33 ] = 'f',
400 [ 34 ] = 'g',
401 [ 35 ] = 'h',
402 [ 36 ] = 'j',
403 [ 37 ] = 'k',
404 [ 38 ] = 'l',
405 [ 39 ] = ';',
406 [ 40 ] = '\'',
407 [ 41 ] = '`',
409 [ 43 ] = '\\',
410 [ 44 ] = 'z',
411 [ 45 ] = 'x',
412 [ 46 ] = 'c',
413 [ 47 ] = 'v',
414 [ 48 ] = 'b',
415 [ 49 ] = 'n',
416 [ 50 ] = 'm',
417 [ 51 ] = ',',
418 [ 52 ] = '.',
419 [ 53 ] = '/',
421 [ 55 ] = '*',
422 [ 57 ] = ' ',
424 [ 71 ] = '7',
425 [ 72 ] = '8',
426 [ 73 ] = '9',
427 [ 74 ] = '-',
428 [ 75 ] = '4',
429 [ 76 ] = '5',
430 [ 77 ] = '6',
431 [ 78 ] = '+',
432 [ 79 ] = '1',
433 [ 80 ] = '2',
434 [ 81 ] = '3',
435 [ 82 ] = '0',
436 [ 83 ] = '.',
438 [ 86 ] = '<',
440 [ 96 ] = '\n',
442 [ 98 ] = '/',
444 [ 102 ] = 1, /* home */
445 [ 103 ] = 16, /* up */
446 [ 104 ] = 7, /* page up */
447 [ 105 ] = 2, /* left */
448 [ 106 ] = 6, /* right */
449 [ 107 ] = 5, /* end */
450 [ 108 ] = 14, /* down */
451 [ 109 ] = 3, /* page down */
453 [ 111 ] = 4, /* delete */
454 };
456 static char linux2ascii_shifted[] = {
457 [ 1 ] = 27,
458 [ 2 ] = '!',
459 [ 3 ] = '@',
460 [ 4 ] = '#',
461 [ 5 ] = '$',
462 [ 6 ] = '%',
463 [ 7 ] = '^',
464 [ 8 ] = '&',
465 [ 9 ] = '*',
466 [ 10 ] = '(',
467 [ 11 ] = ')',
468 [ 12 ] = '_',
469 [ 13 ] = '+',
470 [ 14 ] = '\b',
471 [ 15 ] = '\t',
472 [ 16 ] = 'Q',
473 [ 17 ] = 'W',
474 [ 18 ] = 'E',
475 [ 19 ] = 'R',
476 [ 20 ] = 'T',
477 [ 21 ] = 'Y',
478 [ 22 ] = 'U',
479 [ 23 ] = 'I',
480 [ 24 ] = 'O',
481 [ 25 ] = 'P',
482 [ 26 ] = '{',
483 [ 27 ] = '}',
484 [ 28 ] = '\n',
486 [ 30 ] = 'A',
487 [ 31 ] = 'S',
488 [ 32 ] = 'D',
489 [ 33 ] = 'F',
490 [ 34 ] = 'G',
491 [ 35 ] = 'H',
492 [ 36 ] = 'J',
493 [ 37 ] = 'K',
494 [ 38 ] = 'L',
495 [ 39 ] = ':',
496 [ 40 ] = '"',
497 [ 41 ] = '~',
499 [ 43 ] = '|',
500 [ 44 ] = 'Z',
501 [ 45 ] = 'X',
502 [ 46 ] = 'C',
503 [ 47 ] = 'V',
504 [ 48 ] = 'B',
505 [ 49 ] = 'N',
506 [ 50 ] = 'M',
507 [ 51 ] = '<',
508 [ 52 ] = '>',
509 [ 53 ] = '?',
511 [ 55 ] = '*',
512 [ 57 ] = ' ',
514 [ 71 ] = '7',
515 [ 72 ] = '8',
516 [ 73 ] = '9',
517 [ 74 ] = '-',
518 [ 75 ] = '4',
519 [ 76 ] = '5',
520 [ 77 ] = '6',
521 [ 78 ] = '+',
522 [ 79 ] = '1',
523 [ 80 ] = '2',
524 [ 81 ] = '3',
525 [ 82 ] = '0',
526 [ 83 ] = '.',
528 [ 86 ] = '>',
530 [ 96 ] = '\n',
532 [ 98 ] = '/',
534 [ 102 ] = 1, /* home */
535 [ 103 ] = 16, /* up */
536 [ 104 ] = 7, /* page up */
537 [ 105 ] = 2, /* left */
538 [ 106 ] = 6, /* right */
539 [ 107 ] = 5, /* end */
540 [ 108 ] = 14, /* down */
541 [ 109 ] = 3, /* page down */
543 [ 111 ] = 4, /* delete */
544 };
546 int console_getkey (void)
547 {
548 static int shift, control, alt, caps_lock;
550 if (!has_ev)
551 has_ev = kbdfront_receive(kbd_dev, &ev, 1);
552 if (!has_ev)
553 return 0;
555 has_ev = 0;
556 if (ev.type != XENKBD_TYPE_KEY)
557 return 0;
559 if (ev.key.keycode == 42 || ev.key.keycode == 54) {
560 caps_lock = 0;
561 shift = ev.key.pressed;
562 return 0;
563 }
564 if (ev.key.keycode == 58) {
565 caps_lock ^= 1;
566 return 0;
567 }
568 if (ev.key.keycode == 29 || ev.key.keycode == 97) {
569 control = ev.key.pressed;
570 return 0;
571 }
572 if (ev.key.keycode == 56) {
573 alt = ev.key.pressed;
574 return 0;
575 }
577 if (!ev.key.pressed)
578 return 0;
580 if (ev.key.keycode < sizeof(linux2ascii) / sizeof(*linux2ascii)) {
581 char val;
582 if (shift || caps_lock)
583 val = linux2ascii_shifted[ev.key.keycode];
584 else
585 val = linux2ascii[ev.key.keycode];
586 if (control)
587 val &= ~0x60;
588 return val;
589 }
591 return 0;
592 }
594 static void kbd_thread(void *p)
595 {
596 struct semaphore *sem = p;
598 kbd_dev = init_kbdfront(NULL, 1);
599 up(sem);
600 }
602 struct fbfront_dev *fb_open(void *fb, int width, int height, int depth)
603 {
604 unsigned long *mfns;
605 int linesize = width * (depth / 8);
606 int memsize = linesize * height;
607 int numpages = (memsize + PAGE_SIZE - 1) / PAGE_SIZE;
608 DECLARE_MUTEX_LOCKED(sem);
609 int i;
611 create_thread("kbdfront", kbd_thread, &sem);
613 mfns = malloc(numpages * sizeof(*mfns));
614 for (i = 0; i < numpages; i++) {
615 memset(fb + i * PAGE_SIZE, 0, PAGE_SIZE);
616 mfns[i] = virtual_to_mfn(fb + i * PAGE_SIZE);
617 }
618 fb_dev = init_fbfront(NULL, mfns, width, height, depth, linesize, numpages);
619 free(mfns);
621 if (!fb_dev)
622 return NULL;
624 down(&sem);
625 if (!kbd_dev)
626 return NULL;
628 return fb_dev;
629 }
631 void kbd_close(void *foo)
632 {
633 shutdown_kbdfront(kbd_dev);
634 kbd_dev = NULL;
635 }
637 void fb_close(void)
638 {
639 create_thread("kbdfront close", kbd_close, NULL);
640 shutdown_fbfront(fb_dev);
641 fb_dev = NULL;
642 }
644 /*
645 * Misc
646 */
648 int getrtsecs (void)
649 {
650 struct timeval tv;
651 gettimeofday(&tv, NULL);
652 return tv.tv_sec;
653 }
655 int currticks (void)
656 {
657 struct timeval tv;
658 gettimeofday(&tv, NULL);
659 return ((tv.tv_sec * 1000000ULL + tv.tv_usec) * TICKS_PER_SEC) / 1000000;
660 }
662 void __attribute__ ((noreturn)) grub_reboot (void)
663 {
664 for ( ;; )
665 {
666 struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_reboot };
667 HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown);
668 }
669 }
671 #define SCRATCH_MEMSIZE (4 * 1024 * 1024)
673 /* Note: not allocating it dynamically permits to make sure it lays below 4G
674 * for grub's 32bit pointers to work */
675 char grub_scratch_mem[SCRATCH_MEMSIZE] __attribute__((aligned(PAGE_SIZE)));
677 int main(int argc, char *argv[])
678 {
679 if (argc > 1) {
680 strncpy(config_file, argv[1], sizeof(config_file) - 1);
681 config_file[sizeof(config_file) - 1] = 0;
682 if (!strncmp(config_file, "(nd)", 4))
683 preset_menu = "dhcp";
684 } else
685 preset_menu = "dhcp --with-configfile";
687 mbi.drives_addr = BOOTSEC_LOCATION + (60 * 1024);
688 mbi.drives_length = 0;
690 mbi.boot_loader_name = (unsigned long) "GNU GRUB " VERSION;
691 mbi.mem_lower = (start_info.nr_pages * PAGE_SIZE) / 1024;
692 mbi.mem_upper = 0;
693 saved_drive = boot_drive;
694 saved_partition = install_partition;
696 init_disk();
698 /* Try to make sure the client part got launched */
699 sleep(1);
700 cmain();
701 printk("cmain returned!\n");
702 }