diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2008-06-18 09:36:47 +0100 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2008-06-18 09:36:47 +0100 |
commit | a2c7db64f561821fd528614e68c4d92718210126 (patch) | |
tree | 79c7e1c3ef49b5b4272fd12d36420e6fb8d23cd1 /stubdom/grub/mini-os.c | |
parent | 7074b13cee246f09b3b0a2a6da139b2e047cf4a4 (diff) | |
download | xen-a2c7db64f561821fd528614e68c4d92718210126.tar.gz xen-a2c7db64f561821fd528614e68c4d92718210126.tar.bz2 xen-a2c7db64f561821fd528614e68c4d92718210126.zip |
Add PV-GRUB
This fetches GRUB1 sources, applies the {graphical, print function,
save default, and ext3_256byte} patches from debian, and applies a
patch to make it work on x86_64 and port it to Mini-OS. By using
libxc, PV-GRUB can then "kexec" the loaded kernel from inside the
domain itself, hence permitting to avoid the security-concerned
pygrub.
Signed-off-by: Samuel Thibault <samuel.thibault@eu.citrix.com>
Diffstat (limited to 'stubdom/grub/mini-os.c')
-rw-r--r-- | stubdom/grub/mini-os.c | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/stubdom/grub/mini-os.c b/stubdom/grub/mini-os.c new file mode 100644 index 0000000000..49e1f21f86 --- /dev/null +++ b/stubdom/grub/mini-os.c @@ -0,0 +1,702 @@ +/* + * Mini-OS support for GRUB. + * + * Samuel Thibault <Samuel.Thibault@eu.citrix.com>, May 2008 + */ +#include <sys/types.h> +#include <sys/time.h> +#include <stdarg.h> +#include <stdlib.h> +#include <malloc.h> +#include <unistd.h> + +#include <hypervisor.h> +#include <blkfront.h> +#include <netfront.h> +#include <fbfront.h> +#include <semaphore.h> + +#include <osdep.h> +#include <shared.h> +#include <nic.h> +#include <etherboot.h> +#include <terminfo.h> +#include <term.h> + +#include "mini-os.h" + +extern const char *preset_menu; +char config_file[DEFAULT_FILE_BUFLEN] = "(hd0,0)/boot/grub/menu.lst"; +unsigned long boot_drive = NETWORK_DRIVE; +unsigned long install_partition = 0xFFFFFF; + +char version_string[] = VERSION; + +/* Variables from asm.S */ +int saved_entryno; + +/* + * Disk + */ + +struct blkfront_dev **blk_dev; +int blk_nb; +static struct blkfront_info *blk_info; + +static int vbdcmp(const void *_vbd1, const void *_vbd2) { + char *vbd1 = *(char **)_vbd1; + char *vbd2 = *(char **)_vbd2; + int vbdn1 = atoi(vbd1); + int vbdn2 = atoi(vbd2); + return vbdn1 - vbdn2; +} + +void init_disk (void) +{ + char **list; + char *msg; + int i; + char *path; + + msg = xenbus_ls(XBT_NIL, "device/vbd", &list); + if (msg) { + printk("Error %s while reading list of disks\n", msg); + free(msg); + return; + } + blk_nb = 0; + while (list[blk_nb]) + blk_nb++; + blk_dev = malloc(blk_nb * sizeof(*blk_dev)); + blk_info = malloc(blk_nb * sizeof(*blk_info)); + + qsort(list, blk_nb, sizeof(*list), vbdcmp); + + for (i = 0; i < blk_nb; i++) { + printk("vbd %s is hd%d\n", list[i], i); + asprintf(&path, "device/vbd/%s", list[i]); + blk_dev[i] = init_blkfront(path, &blk_info[i]); + free(path); + free(list[i]); + } +} + +/* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return + non-zero, otherwise zero. */ +int get_diskinfo (int drive, struct geometry *geometry) +{ + int i; + if (!(drive & 0x80)) + return -1; + + i = drive - 0x80; + if (i >= blk_nb) + return -1; + + /* Bogus geometry */ + geometry->cylinders = 65535; + geometry->heads = 255; + geometry->sectors = 63; + + geometry->total_sectors = blk_info[i].sectors; + geometry->sector_size = blk_info[i].sector_size; + geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION; + if (blk_info[i].info & VDISK_CDROM) + geometry->flags |= BIOSDISK_FLAG_CDROM; + return 0; +} + +/* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY + from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it, + else if READ is BIOSDISK_WRITE, then write it. If an geometry error + occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then + return the error number. Otherwise, return 0. */ +int +biosdisk (int read, int drive, struct geometry *geometry, + unsigned int sector, int nsec, int segment) +{ + void *addr = (void *) ((unsigned long)segment << 4); + struct blkfront_aiocb aiocb; + int i; + + if (!(drive & 0x80)) + return -1; + + i = drive - 0x80; + if (i >= blk_nb) + return -1; + + aiocb.aio_dev = blk_dev[i]; + aiocb.aio_buf = addr; + aiocb.aio_nbytes = (size_t)nsec * blk_info[i].sector_size; + aiocb.aio_offset = (off_t)sector * blk_info[i].sector_size; + aiocb.aio_cb = NULL; + + blkfront_io(&aiocb, read == BIOSDISK_WRITE); + + return 0; +} + +static int +load_file(char *name, void **ptr, long *size) +{ + char *buf = NULL; + int allocated = 1 * 1024 * 1024; + int len, filled = 0; + + if (!grub_open (name)) + return -1; + + buf = malloc(allocated); + + errnum = 0; + while (1) { + len = grub_read (buf + filled, allocated - filled); + if (! len) { + if (!errnum) + break; + grub_close (); + return -1; + } + filled += len; + if (filled < allocated) + break; + allocated *= 2; + buf = realloc(buf, allocated); + } + grub_close (); + *ptr = buf; + *size = filled; + return 0; +} + +void *kernel_image, *module_image; +long kernel_size, module_size; +char *kernel_arg, *module_arg; + +kernel_t +load_image (char *kernel, char *arg, kernel_t suggested_type, + unsigned long load_flags) +{ + arg = skip_to(0, arg); + if (kernel_image) + free(kernel_image); + kernel_image = NULL; + if (load_file (kernel, &kernel_image, &kernel_size)) + return KERNEL_TYPE_NONE; + if (kernel_arg) + free(kernel_arg); + kernel_arg = strdup(arg); + return KERNEL_TYPE_PV; +} + +int +load_initrd (char *initrd) +{ + if (module_image) + free(module_image); + module_image = NULL; + load_file (initrd, &module_image, &module_size); + return ! errnum; +} + +int +load_module (char *module, char *arg) +{ + if (module_image) + free(module_image); + module_image = NULL; + load_file (module, &module_image, &module_size); + if (module_arg) + free(module_arg); + module_arg = strdup(arg); + return ! errnum; +} + +void +pv_boot (void) +{ + kexec(kernel_image, kernel_size, module_image, module_size, kernel_arg); +} + +/* + * Network + */ + +struct netfront_dev *net_dev; + +int +minios_probe (struct nic *nic) +{ + char *ip; + + if (net_dev) + return 1; + + /* Clear the ARP table. */ + grub_memset ((char *) arptable, 0, + MAX_ARP * sizeof (struct arptable_t)); + + net_dev = init_netfront(NULL, (void*) -1, nic->node_addr, &ip); + if (!net_dev) + return 0; + + return 1; +} + +/* reset adapter */ +static void minios_reset(struct nic *nic) +{ + /* TODO? */ +} + +static void minios_disable(struct nic *nic) +{ +} + +/* Wait for a frame */ +static int minios_poll(struct nic *nic) +{ + return !! (nic->packetlen = netfront_receive(net_dev, (void*) nic->packet, ETH_FRAME_LEN)); +} + +/* Transmit a frame */ +struct frame { + uint8_t dest[ETH_ALEN]; + uint8_t src[ETH_ALEN]; + uint16_t type; + unsigned char data[]; +}; +static void minios_transmit (struct nic *nic, const char *d, unsigned int t, + unsigned int s, const char *p) +{ + struct frame *frame = alloca(sizeof(frame) + s); + + memcpy(frame->dest, d, ETH_ALEN); + memcpy(frame->src, nic->node_addr, ETH_ALEN); + frame->type = htons(t); + memcpy(frame->data, p, s); + + netfront_xmit(net_dev, (void*) frame, sizeof(*frame) + s); +} + +static char packet[ETH_FRAME_LEN]; + +struct nic nic = { + .reset = minios_reset, + .poll = minios_poll, + .transmit = minios_transmit, + .disable = minios_disable, + .flags = 0, + .rom_info = NULL, + .node_addr = arptable[ARP_CLIENT].node, + .packet = packet, + .packetlen = 0, + .priv_data = NULL, +}; + +int +eth_probe (void) +{ + return minios_probe(&nic); +} + +int +eth_poll (void) +{ + return minios_poll (&nic); +} + +void +eth_disable (void) +{ + minios_disable (&nic); +} + +void +eth_transmit (const char *d, unsigned int t, + unsigned int s, const void *p) +{ + minios_transmit (&nic, d, t, s, p); + if (t == IP) + twiddle(); +} + +/* + * Console + */ +void +serial_hw_put (int _c) +{ + char c = _c; + console_print(&c, 1); +} + +int +serial_hw_fetch (void) +{ + char key; + + if (!xencons_ring_avail()) + return -1; + + read(STDIN_FILENO, &key, 1); + switch (key) { + case 0x7f: key = '\b'; break; + } + return key; +} + +/* + * PVFB + */ +struct kbdfront_dev *kbd_dev; +struct fbfront_dev *fb_dev; +static union xenkbd_in_event ev; +static int has_ev; +int console_checkkey (void) +{ + if (has_ev) + return 1; + has_ev = kbdfront_receive(kbd_dev, &ev, 1); + return has_ev; +} + +/* static QWERTY layout, that's what most PC BIOSes do anyway */ +static char linux2ascii[] = { + [ 1 ] = 27, + [ 2 ] = '1', + [ 3 ] = '2', + [ 4 ] = '3', + [ 5 ] = '4', + [ 6 ] = '5', + [ 7 ] = '6', + [ 8 ] = '7', + [ 9 ] = '8', + [ 10 ] = '9', + [ 11 ] = '0', + [ 12 ] = '-', + [ 13 ] = '=', + [ 14 ] = '\b', + [ 15 ] = '\t', + [ 16 ] = 'q', + [ 17 ] = 'w', + [ 18 ] = 'e', + [ 19 ] = 'r', + [ 20 ] = 't', + [ 21 ] = 'y', + [ 22 ] = 'u', + [ 23 ] = 'i', + [ 24 ] = 'o', + [ 25 ] = 'p', + [ 26 ] = '[', + [ 27 ] = ']', + [ 28 ] = '\n', + + [ 30 ] = 'a', + [ 31 ] = 's', + [ 32 ] = 'd', + [ 33 ] = 'f', + [ 34 ] = 'g', + [ 35 ] = 'h', + [ 36 ] = 'j', + [ 37 ] = 'k', + [ 38 ] = 'l', + [ 39 ] = ';', + [ 40 ] = '\'', + [ 41 ] = '`', + + [ 43 ] = '\\', + [ 44 ] = 'z', + [ 45 ] = 'x', + [ 46 ] = 'c', + [ 47 ] = 'v', + [ 48 ] = 'b', + [ 49 ] = 'n', + [ 50 ] = 'm', + [ 51 ] = ',', + [ 52 ] = '.', + [ 53 ] = '/', + + [ 55 ] = '*', + [ 57 ] = ' ', + + [ 71 ] = '7', + [ 72 ] = '8', + [ 73 ] = '9', + [ 74 ] = '-', + [ 75 ] = '4', + [ 76 ] = '5', + [ 77 ] = '6', + [ 78 ] = '+', + [ 79 ] = '1', + [ 80 ] = '2', + [ 81 ] = '3', + [ 82 ] = '0', + [ 83 ] = '.', + + [ 86 ] = '<', + + [ 96 ] = '\n', + + [ 98 ] = '/', + + [ 102 ] = 1, /* home */ + [ 103 ] = 16, /* up */ + [ 104 ] = 7, /* page up */ + [ 105 ] = 2, /* left */ + [ 106 ] = 6, /* right */ + [ 107 ] = 5, /* end */ + [ 108 ] = 14, /* down */ + [ 109 ] = 3, /* page down */ + + [ 111 ] = 4, /* delete */ +}; + +static char linux2ascii_shifted[] = { + [ 1 ] = 27, + [ 2 ] = '!', + [ 3 ] = '@', + [ 4 ] = '#', + [ 5 ] = '$', + [ 6 ] = '%', + [ 7 ] = '^', + [ 8 ] = '&', + [ 9 ] = '*', + [ 10 ] = '(', + [ 11 ] = ')', + [ 12 ] = '_', + [ 13 ] = '+', + [ 14 ] = '\b', + [ 15 ] = '\t', + [ 16 ] = 'Q', + [ 17 ] = 'W', + [ 18 ] = 'E', + [ 19 ] = 'R', + [ 20 ] = 'T', + [ 21 ] = 'Y', + [ 22 ] = 'U', + [ 23 ] = 'I', + [ 24 ] = 'O', + [ 25 ] = 'P', + [ 26 ] = '{', + [ 27 ] = '}', + [ 28 ] = '\n', + + [ 30 ] = 'A', + [ 31 ] = 'S', + [ 32 ] = 'D', + [ 33 ] = 'F', + [ 34 ] = 'G', + [ 35 ] = 'H', + [ 36 ] = 'J', + [ 37 ] = 'K', + [ 38 ] = 'L', + [ 39 ] = ':', + [ 40 ] = '"', + [ 41 ] = '~', + + [ 43 ] = '|', + [ 44 ] = 'Z', + [ 45 ] = 'X', + [ 46 ] = 'C', + [ 47 ] = 'V', + [ 48 ] = 'B', + [ 49 ] = 'N', + [ 50 ] = 'M', + [ 51 ] = '<', + [ 52 ] = '>', + [ 53 ] = '?', + + [ 55 ] = '*', + [ 57 ] = ' ', + + [ 71 ] = '7', + [ 72 ] = '8', + [ 73 ] = '9', + [ 74 ] = '-', + [ 75 ] = '4', + [ 76 ] = '5', + [ 77 ] = '6', + [ 78 ] = '+', + [ 79 ] = '1', + [ 80 ] = '2', + [ 81 ] = '3', + [ 82 ] = '0', + [ 83 ] = '.', + + [ 86 ] = '>', + + [ 96 ] = '\n', + + [ 98 ] = '/', + + [ 102 ] = 1, /* home */ + [ 103 ] = 16, /* up */ + [ 104 ] = 7, /* page up */ + [ 105 ] = 2, /* left */ + [ 106 ] = 6, /* right */ + [ 107 ] = 5, /* end */ + [ 108 ] = 14, /* down */ + [ 109 ] = 3, /* page down */ + + [ 111 ] = 4, /* delete */ +}; + +int console_getkey (void) +{ + static int shift, control, alt, caps_lock; + + if (!has_ev) + has_ev = kbdfront_receive(kbd_dev, &ev, 1); + if (!has_ev) + return 0; + + has_ev = 0; + if (ev.type != XENKBD_TYPE_KEY) + return 0; + + if (ev.key.keycode == 42 || ev.key.keycode == 54) { + caps_lock = 0; + shift = ev.key.pressed; + return 0; + } + if (ev.key.keycode == 58) { + caps_lock ^= 1; + return 0; + } + if (ev.key.keycode == 29 || ev.key.keycode == 97) { + control = ev.key.pressed; + return 0; + } + if (ev.key.keycode == 56) { + alt = ev.key.pressed; + return 0; + } + + if (!ev.key.pressed) + return 0; + + if (ev.key.keycode < sizeof(linux2ascii) / sizeof(*linux2ascii)) { + char val; + if (shift || caps_lock) + val = linux2ascii_shifted[ev.key.keycode]; + else + val = linux2ascii[ev.key.keycode]; + if (control) + val &= ~0x60; + return val; + } + + return 0; +} + +static void kbd_thread(void *p) +{ + struct semaphore *sem = p; + + kbd_dev = init_kbdfront(NULL, 1); + up(sem); +} + +struct fbfront_dev *fb_open(void *fb, int width, int height, int depth) +{ + unsigned long *mfns; + int linesize = width * (depth / 8); + int memsize = linesize * height; + int numpages = (memsize + PAGE_SIZE - 1) / PAGE_SIZE; + DECLARE_MUTEX_LOCKED(sem); + int i; + + create_thread("kbdfront", kbd_thread, &sem); + + mfns = malloc(numpages * sizeof(*mfns)); + for (i = 0; i < numpages; i++) { + memset(fb + i * PAGE_SIZE, 0, PAGE_SIZE); + mfns[i] = virtual_to_mfn(fb + i * PAGE_SIZE); + } + fb_dev = init_fbfront(NULL, mfns, width, height, depth, linesize, numpages); + free(mfns); + + if (!fb_dev) + return NULL; + + down(&sem); + if (!kbd_dev) + return NULL; + + return fb_dev; +} + +void kbd_close(void *foo) +{ + shutdown_kbdfront(kbd_dev); + kbd_dev = NULL; +} + +void fb_close(void) +{ + create_thread("kbdfront close", kbd_close, NULL); + shutdown_fbfront(fb_dev); + fb_dev = NULL; +} + +/* + * Misc + */ + +int getrtsecs (void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec; +} + +int currticks (void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000000ULL + tv.tv_usec) * TICKS_PER_SEC) / 1000000; +} + +void __attribute__ ((noreturn)) grub_reboot (void) +{ + for ( ;; ) + { + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_reboot }; + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + } +} + +#define SCRATCH_MEMSIZE (4 * 1024 * 1024) + +/* Note: not allocating it dynamically permits to make sure it lays below 4G + * for grub's 32bit pointers to work */ +char grub_scratch_mem[SCRATCH_MEMSIZE] __attribute__((aligned(PAGE_SIZE))); + +int main(int argc, char *argv[]) +{ + if (argc > 1) { + strncpy(config_file, argv[1], sizeof(config_file) - 1); + config_file[sizeof(config_file) - 1] = 0; + if (!strncmp(config_file, "(nd)", 4)) + preset_menu = "dhcp"; + } else + preset_menu = "dhcp --with-configfile"; + + mbi.drives_addr = BOOTSEC_LOCATION + (60 * 1024); + mbi.drives_length = 0; + + mbi.boot_loader_name = (unsigned long) "GNU GRUB " VERSION; + mbi.mem_lower = (start_info.nr_pages * PAGE_SIZE) / 1024; + mbi.mem_upper = 0; + saved_drive = boot_drive; + saved_partition = install_partition; + + init_disk(); + + /* Try to make sure the client part got launched */ + sleep(1); + cmain(); + printk("cmain returned!\n"); +} |