/* Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int main(int argc, char **argv) { int i; VM *vm = malloc(sizeof(VM)); strcpy(vm->filename, "retroImage"); init_vm(vm); dev_init_input(); /* Parse the command line arguments */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--with") == 0) dev_include(argv[++i]); if (strcmp(argv[i], "--image") == 0) strcpy(vm->filename, argv[++i]); if (strcmp(argv[i], "--shrink") == 0) vm->shrink = 1; } dev_init_output(); if (vm_load_image(vm, vm->filename) == -1) { dev_cleanup(); printf("Sorry, unable to find %s\n", vm->filename); exit(1); } for (vm->ip = 0; vm->ip < IMAGE_SIZE; vm->ip++) vm_process(vm); dev_cleanup(); return 0; }
int __elfN(ofw_exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp; Elf_Ehdr *e; int error; intptr_t entry; if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { return(EFTYPE); } e = (Elf_Ehdr *)&fmp->md_data; entry = e->e_entry; if ((error = md_load(fp->f_args, &mdp)) != 0) return (error); printf("Kernel entry at 0x%lx ...\n", e->e_entry); dev_cleanup(); ofw_release_heap(); OF_chain((void *)reloc, end - (char *)reloc, (void *)entry, (void *)mdp, sizeof(mdp)); panic("exec returned"); }
static int beri_elf64_exec(struct preloaded_file *fp) { void (*entry)(register_t, register_t, register_t, register_t); struct file_metadata *md; vm_offset_t mdp; Elf_Ehdr *ehdr; int error; md = file_findmetadata(fp, MODINFOMD_ELFHDR); if (md == NULL) { printf("%s: file_findmetadata failed\n"); return (EFTYPE); } ehdr = (Elf_Ehdr *)md->md_data; error = md_load64(fp->f_args, &mdp); if (error) { printf("%s: md_load64 failed\n"); return (error); } entry = (void *)ehdr->e_entry; printf("Kernel entry at %p\n", entry); dev_cleanup(); /* XXXRW: Required? */ printf("Kernel args: %s\n", fp->f_args); /* * Configure bootinfo for the loaded kernel. Some values are * inherited from the bootinfo passed to us by boot2 (e.g., DTB * pointer); others are local to the loader (e.g., kernel boot flags). */ bzero(&bootinfo, sizeof(bootinfo)); bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); bootinfo.bi_boot2opts = boot2_bootinfo.bi_boot2opts; /* NB: bi_kernelname used only by boot2. */ /* NB: bi_nfs_diskless not yet. */ bootinfo.bi_dtb = boot2_bootinfo.bi_dtb; bootinfo.bi_memsize = boot2_bootinfo.bi_memsize; bootinfo.bi_modulep = mdp; /* * Flush the instruction cache before running any instructions from * the loaded kernel. */ beri_icache_sync(); /* * XXXRW: For now, pass 'memsize' rather than dtb or bootinfo. This * is the 'old' ABI spoken by Miniboot and the kernel. To pass in * boot2opts, modules, etc, we will need to fix this to pass in at * least bootinfop. */ (*entry)(boot2_argc, (register_t)boot2_argv, (register_t)boot2_envv, &bootinfo); panic("exec returned"); }
/* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such * preparations as are required, and do so. */ static int elf32_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t entry, bootinfop, modulep, kernend; int boothowto, err, bootdev; if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend); if (err != 0) return(err); entry = ehdr->e_entry & 0xffffff; #ifdef DEBUG printf("Start @ 0x%lx ...\n", entry); #endif dev_cleanup(); __exec((void *)entry, boothowto, bootdev, 0, 0, 0, bootinfop, modulep, kernend); panic("exec returned"); }
static int __elfN(arm_exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t modulep, kernend; Elf_Ehdr *e; int error; void (*entry)(void *); if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return (EFTYPE); e = (Elf_Ehdr *)&fmp->md_data; if ((error = bi_load(fp->f_args, &modulep, &kernend)) != 0) return (error); entry = efi_translate(e->e_entry); printf("Kernel entry at 0x%x...\n", (unsigned)entry); printf("Kernel args: %s\n", fp->f_args); printf("modulep: %#x\n", modulep); printf("relocation_offset %llx\n", __elfN(relocation_offset)); dev_cleanup(); (*entry)((void *)modulep); panic("exec returned"); }
int __elfN(uboot_exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp; Elf_Ehdr *e; int error; void (*entry)(void *); if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return (EFTYPE); e = (Elf_Ehdr *)&fmp->md_data; if ((error = md_load(fp->f_args, &mdp)) != 0) return (error); entry = (void *)e->e_entry; printf("Kernel entry at 0x%p...\n", entry); dev_cleanup(); printf("Kernel args: %s\n", fp->f_args); (*entry)((void *)mdp); panic("exec returned"); }
int ppc64_elf_exec(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp; Elf_Ehdr *e; int error; int (*entry)(u_long, u_long, u_long, void *, u_long); if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { return(EFTYPE); } e = (Elf_Ehdr *)&fmp->md_data; /* Handle function descriptor for ELFv1 kernels */ if ((e->e_flags & 3) == 2) entry = e->e_entry; else entry = (void *)(uintptr_t)(*(uint64_t *)e->e_entry); if ((error = md_load64(fp->f_args, &mdp)) != 0) return (error); printf("Kernel entry at %p ...\n", entry); dev_cleanup(); entry(0 /* FDT */, 0 /* Phys. mem offset */, 0 /* OF entry */, (void *)mdp, sizeof(mdp)); panic("exec returned"); }
int vm_save_image(VM *vm, char *image) { FILE *fp; int x; if ((fp = fopen(image, "wb")) == NULL) { fprintf(stderr, "Sorry, but I couldn't open %s\n", image); dev_cleanup(); exit(-1); } if (vm->shrink == 0) x = fwrite(&vm->image, sizeof(int), IMAGE_SIZE, fp); else x = fwrite(&vm->image, sizeof(int), vm->image[3], fp); fclose(fp); return x; }
/* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such * preparations as are required, and do so. */ static int elf32_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t entry, bootinfop, modulep, kernend; int boothowto, err, bootdev; uint32_t stack[1024]; if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend); if (err != 0) return(err); entry = ehdr->e_entry & 0xffffff; #ifdef DEBUG printf("Start @ 0x%lx ...\n", entry); #endif dev_cleanup(); /* * Build a scratch stack at physical 0x1000 */ stack[0] = boothowto; stack[1] = bootdev; stack[2] = 0; stack[3] = 0; stack[4] = 0; stack[5] = bootinfop; stack[6] = modulep; stack[7] = kernend; CALLBACK(copyin, stack, 0x1000, sizeof(stack)); CALLBACK(setreg, 4, 0x1000); CALLBACK(exec, entry); panic("exec returned"); }
int ppc64_ofw_elf_exec(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp, dtbp; Elf_Ehdr *e; int error; intptr_t entry; if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { return(EFTYPE); } e = (Elf_Ehdr *)&fmp->md_data; /* Handle function descriptor for ELFv1 kernels */ if ((e->e_flags & 3) == 2) entry = e->e_entry; else entry = *(uint64_t *)e->e_entry; if ((error = md_load64(fp->f_args, &mdp, &dtbp)) != 0) return (error); printf("Kernel entry at 0x%lx ...\n", entry); dev_cleanup(); ofw_release_heap(); if (dtbp != 0) { OF_quiesce(); ((int (*)(u_long, u_long, u_long, void *, u_long))entry)(dtbp, 0, 0, mdp, sizeof(mdp)); } else { OF_chain((void *)reloc, end - (char *)reloc, (void *)entry, (void *)mdp, sizeof(mdp)); } panic("exec returned"); }
/****************************************************** * Main entry point into the VM ******************************************************/ int main(int argc, char **argv) { int a, i, endian; char *opstat_path = 0; FILE *opstats = 0; VM *vm = malloc(sizeof(VM)); endian = 0; strcpy(vm->filename, "retroImage"); init_vm(vm); dev_init(INPUT); vm->shrink = 0; vm->trace = 0; vm->trace_stacks = 0; /* Parse the command line arguments */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--trace") == 0) { vm->trace = -1; } else if (strcmp(argv[i], "--trace-stacks") == 0) { vm->trace_stacks = -1; } else if (strcmp(argv[i], "--endian") == 0) { endian = -1; } else if (strcmp(argv[i], "--with") == 0) { i++; dev_include(argv[i]); } else if (strcmp(argv[i], "--opstats") == 0) { i++; opstat_path = argv[i]; init_stats(&opstats, opstat_path, call_stats_please); } else if (strcmp(argv[i], "--callstats") == 0) { call_stats_please = 1; if (opstat_path) { init_stats(&opstats, opstat_path, call_stats_please); } } else if (strcmp(argv[i], "--shrink") == 0) { vm->shrink = 1; } else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-help") == 0) || (strcmp(argv[i], "/help") == 0) || (strcmp(argv[i], "/?") == 0) || (strcmp(argv[i], "/h") == 0) || (strcmp(argv[i], "-h") == 0)) { fprintf(stderr, "%s [options] [imagename]\n", argv[0]); fprintf(stderr, "Valid options are:\n"); fprintf(stderr, " --about Display some information about Ngaro\n"); fprintf(stderr, " --trace Trace instructions being executed\n"); fprintf(stderr, " --trace-stacks Also trace data and return stack contents\n"); fprintf(stderr, " --endian Load an image with a different endianness\n"); fprintf(stderr, " --shrink Shrink the image to the current heap size when saving\n"); fprintf(stderr, " --with [file] Treat [file] as an input source\n"); fprintf(stderr, " --opstats [file] Write statistics about VM operations to [file]\n"); fprintf(stderr, " --callstats Include how many times each address is called (slow)\n"); fprintf(stderr, " Also includes distribution of stack depths.\n"); exit(0); } else if ((strcmp(argv[i], "--about") == 0) || (strcmp(argv[i], "--version") == 0)) { fprintf(stderr, "Retro Language [VM: C, console]\n\n"); exit(0); } else { strcpy(vm->filename, argv[i]); } } dev_init(OUTPUT); a = vm_load_image(vm, vm->filename); if (a == -1) { dev_cleanup(); printf("Sorry, unable to find %s\n", vm->filename); exit(1); } /* Swap endian if --endian was passed */ if (endian == -1) swapEndian(vm); /* Process the image */ if (opstats == 0) { for (vm->ip = 0; vm->ip < IMAGE_SIZE; vm->ip++) vm_process(vm); } else { for (vm->ip = 0; vm->ip < IMAGE_SIZE; vm->ip++) { collect_stats(vm); vm_process(vm); } report_stats(opstats); } /* Once done, cleanup */ dev_cleanup(); return 0; }
static int elf64_exec(struct preloaded_file *fp) { vm_offset_t modulep, kernendp; vm_offset_t clean_addr; size_t clean_size; struct file_metadata *md; ACPI_TABLE_RSDP *rsdp; Elf_Ehdr *ehdr; char buf[24]; int err, revision; void (*entry)(vm_offset_t); rsdp = efi_get_table(&acpi20_guid); if (rsdp == NULL) { rsdp = efi_get_table(&acpi_guid); } if (rsdp != NULL) { sprintf(buf, "0x%016llx", (unsigned long long)rsdp); setenv("hint.acpi.0.rsdp", buf, 1); revision = rsdp->Revision; if (revision == 0) revision = 1; sprintf(buf, "%d", revision); setenv("hint.acpi.0.revision", buf, 1); strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId)); buf[sizeof(rsdp->OemId)] = '\0'; setenv("hint.acpi.0.oem", buf, 1); sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress); setenv("hint.acpi.0.rsdt", buf, 1); if (revision >= 2) { /* XXX extended checksum? */ sprintf(buf, "0x%016llx", (unsigned long long)rsdp->XsdtPhysicalAddress); setenv("hint.acpi.0.xsdt", buf, 1); sprintf(buf, "%d", rsdp->Length); setenv("hint.acpi.0.xsdt_length", buf, 1); } } if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); entry = efi_translate(ehdr->e_entry); err = bi_load(fp->f_args, &modulep, &kernendp); if (err != 0) return (err); dev_cleanup(); /* Clean D-cache under kernel area and invalidate whole I-cache */ clean_addr = (vm_offset_t)efi_translate(fp->f_addr); clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr; cpu_flush_dcache((void *)clean_addr, clean_size); cpu_inval_icache(NULL, 0); (*entry)(modulep); panic("exec returned"); }
static int multiboot_exec(struct preloaded_file *fp) { struct preloaded_file *mfp; vm_offset_t entry; struct file_metadata *md; struct multiboot_info *mb_info = NULL; struct multiboot_mod_list *mb_mod = NULL; multiboot_memory_map_t *mmap; struct bios_smap *smap; struct devdesc *rootdev; char *cmdline = NULL; size_t len; int error, num, i; int rootfs = 0; /* flag for rootfs */ int xen = 0; /* flag for xen */ int kernel = 0; /* flag for kernel */ /* Set up base for mb_malloc. */ for (mfp = fp; mfp->f_next != NULL; mfp = mfp->f_next); /* Start info block from new page. */ last_addr = roundup(mfp->f_addr + mfp->f_size, MULTIBOOT_MOD_ALIGN); /* Allocate the multiboot struct and fill the basic details. */ mb_info = (struct multiboot_info *)PTOV(mb_malloc(sizeof (*mb_info))); bzero(mb_info, sizeof(struct multiboot_info)); mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; mb_info->mem_lower = bios_basemem / 1024; mb_info->mem_upper = bios_extmem / 1024; mb_info->boot_loader_name = mb_malloc(strlen(bootprog_info) + 1); i386_copyin(bootprog_info, mb_info->boot_loader_name, strlen(bootprog_info) + 1); i386_getdev((void **)(&rootdev), NULL, NULL); if (rootdev == NULL) { printf("can't determine root device\n"); error = EINVAL; goto error; } /* * Boot image command line. If args were not provided, we need to set * args here, and that depends on image type... * Fortunately we only have following options: * 64 or 32 bit unix or xen. So we just check if f_name has unix. */ /* Do we boot xen? */ if (strstr(fp->f_name, "unix") == NULL) xen = 1; entry = fp->f_addr; num = 0; for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) { num++; if (mfp->f_type != NULL && strcmp(mfp->f_type, "rootfs") == 0) rootfs++; if (mfp->f_type != NULL && strcmp(mfp->f_type, "kernel") == 0) kernel++; } if (num == 0 || rootfs == 0) { /* We need at least one module - rootfs. */ printf("No rootfs module provided, aborting\n"); error = EINVAL; goto error; } if (xen == 1 && kernel == 0) { printf("No kernel module provided for xen, aborting\n"); error = EINVAL; goto error; } mb_mod = (struct multiboot_mod_list *) PTOV(last_addr); last_addr += roundup(sizeof(*mb_mod) * num, MULTIBOOT_INFO_ALIGN); bzero(mb_mod, sizeof(*mb_mod) * num); num = 0; for (mfp = fp->f_next; mfp != NULL; mfp = mfp->f_next) { mb_mod[num].mod_start = mfp->f_addr; mb_mod[num].mod_end = mfp->f_addr + mfp->f_size; if (strcmp(mfp->f_type, "kernel") == 0) { cmdline = NULL; error = mb_kernel_cmdline(mfp, rootdev, &cmdline); if (error != 0) goto error; } else { len = strlen(mfp->f_name) + 1; len += strlen(mfp->f_type) + 5 + 1; if (mfp->f_args != NULL) { len += strlen(mfp->f_args) + 1; } cmdline = malloc(len); if (cmdline == NULL) { error = ENOMEM; goto error; } if (mfp->f_args != NULL) snprintf(cmdline, len, "%s type=%s %s", mfp->f_name, mfp->f_type, mfp->f_args); else snprintf(cmdline, len, "%s type=%s", mfp->f_name, mfp->f_type); } mb_mod[num].cmdline = mb_malloc(strlen(cmdline)+1); i386_copyin(cmdline, mb_mod[num].cmdline, strlen(cmdline)+1); free(cmdline); num++; } mb_info->mods_count = num; mb_info->mods_addr = VTOP(mb_mod); mb_info->flags |= MULTIBOOT_INFO_MODS; md = file_findmetadata(fp, MODINFOMD_SMAP); if (md == NULL) { printf("no memory smap\n"); error = EINVAL; goto error; } num = md->md_size / sizeof(struct bios_smap); /* number of entries */ mmap = (multiboot_memory_map_t *)PTOV(mb_malloc(sizeof(*mmap) * num)); mb_info->mmap_length = num * sizeof(*mmap); smap = (struct bios_smap *)md->md_data; for (i = 0; i < num; i++) { mmap[i].size = sizeof(*smap); mmap[i].addr = smap[i].base; mmap[i].len = smap[i].length; mmap[i].type = smap[i].type; } mb_info->mmap_addr = VTOP(mmap); mb_info->flags |= MULTIBOOT_INFO_MEM_MAP; if (strstr(getenv("loaddev"), "net") != NULL && bootp_response != NULL) { mb_info->drives_length = bootp_response_size; mb_info->drives_addr = mb_malloc(bootp_response_size); i386_copyin(bootp_response, mb_info->drives_addr, bootp_response_size); mb_info->flags &= ~MULTIBOOT_INFO_DRIVE_INFO; } /* * Set the image command line. Need to do this as last thing, * as illumos kernel dboot_startkern will check cmdline * address as last check to find first free address. */ if (fp->f_args == NULL) { if (xen) cmdline = getenv("xen_cmdline"); else cmdline = getenv("boot-args"); if (cmdline != NULL) { fp->f_args = strdup(cmdline); if (fp->f_args == NULL) { error = ENOMEM; goto error; } } } /* * If the image is xen, we just use f_name + f_args for commandline * for unix, we need to add zfs-bootfs. */ if (xen) { len = strlen(fp->f_name) + 1; if (fp->f_args != NULL) len += strlen(fp->f_args) + 1; if (fp->f_args != NULL) { if((cmdline = malloc(len)) == NULL) { error = ENOMEM; goto error; } snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); } else { cmdline = strdup(fp->f_name); if (cmdline == NULL) { error = ENOMEM; goto error; } } } else { cmdline = NULL; if ((error = mb_kernel_cmdline(fp, rootdev, &cmdline)) != 0) goto error; } mb_info->cmdline = mb_malloc(strlen(cmdline)+1); i386_copyin(cmdline, mb_info->cmdline, strlen(cmdline)+1); mb_info->flags |= MULTIBOOT_INFO_CMDLINE; free(cmdline); cmdline = NULL; dev_cleanup(); __exec((void *)VTOP(multiboot_tramp), MULTIBOOT_BOOTLOADER_MAGIC, (void *)entry, (void *)VTOP(mb_info)); panic("exec returned"); error: free(cmdline); return (error); }
/* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such * preparations as are required, and do so. */ static int elf64_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t modulep, kernend; int err; int i; uint32_t stack[1024]; p4_entry_t PT4[512]; p3_entry_t PT3[512]; p2_entry_t PT2[512]; uint64_t gdtr[3]; if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); err = bi_load64(fp->f_args, &modulep, &kernend); if (err != 0) return(err); bzero(PT4, PAGE_SIZE); bzero(PT3, PAGE_SIZE); bzero(PT2, PAGE_SIZE); /* * Build a scratch stack at physical 0x1000, page tables: * PT4 at 0x2000, * PT3 at 0x3000, * PT2 at 0x4000, * gdtr at 0x5000, */ /* * This is kinda brutal, but every single 1GB VM memory segment * points to the same first 1GB of physical memory. But it is * more than adequate. */ for (i = 0; i < 512; i++) { /* Each slot of the level 4 pages points to the same level 3 page */ PT4[i] = (p4_entry_t) 0x3000; PT4[i] |= PG_V | PG_RW | PG_U; /* Each slot of the level 3 pages points to the same level 2 page */ PT3[i] = (p3_entry_t) 0x4000; PT3[i] |= PG_V | PG_RW | PG_U; /* The level 2 page slots are mapped with 2MB pages for 1GB. */ PT2[i] = i * (2 * 1024 * 1024); PT2[i] |= PG_V | PG_RW | PG_PS | PG_U; } #ifdef DEBUG printf("Start @ %#llx ...\n", ehdr->e_entry); #endif dev_cleanup(); stack[0] = 0; /* return address */ stack[1] = modulep; stack[2] = kernend; CALLBACK(copyin, stack, 0x1000, sizeof(stack)); CALLBACK(copyin, PT4, 0x2000, sizeof(PT4)); CALLBACK(copyin, PT3, 0x3000, sizeof(PT3)); CALLBACK(copyin, PT2, 0x4000, sizeof(PT2)); CALLBACK(setreg, 4, 0x1000); CALLBACK(setmsr, MSR_EFER, EFER_LMA | EFER_LME); CALLBACK(setcr, 4, CR4_PAE | CR4_VMXE); CALLBACK(setcr, 3, 0x2000); CALLBACK(setcr, 0, CR0_PG | CR0_PE | CR0_NE); setup_freebsd_gdt(gdtr); CALLBACK(copyin, gdtr, 0x5000, sizeof(gdtr)); CALLBACK(setgdt, 0x5000, sizeof(gdtr)); CALLBACK(exec, ehdr->e_entry); panic("exec returned"); }
static int multiboot_exec(struct preloaded_file *fp) { vm_offset_t module_start, last_addr, metadata_size; vm_offset_t modulep, kernend, entry; struct file_metadata *md; Elf_Ehdr *ehdr; struct multiboot_info *mb_info = NULL; struct multiboot_mod_list *mb_mod = NULL; char *cmdline = NULL; size_t len; int error, mod_num; /* * Don't pass the memory size found by the bootloader, the memory * available to Dom0 will be lower than that. */ unsetenv("smbios.memory.enabled"); /* Allocate the multiboot struct and fill the basic details. */ mb_info = malloc(sizeof(struct multiboot_info)); if (mb_info == NULL) { error = ENOMEM; goto error; } bzero(mb_info, sizeof(struct multiboot_info)); mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; mb_info->mem_lower = bios_basemem / 1024; mb_info->mem_upper = bios_extmem / 1024; mb_info->boot_loader_name = VTOP(mbl_name); /* Set the Xen command line. */ if (fp->f_args == NULL) { /* Add the Xen command line if it is set. */ cmdline = getenv("xen_cmdline"); if (cmdline != NULL) { fp->f_args = strdup(cmdline); if (fp->f_args == NULL) { error = ENOMEM; goto error; } } } if (fp->f_args != NULL) { len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; cmdline = malloc(len); if (cmdline == NULL) { error = ENOMEM; goto error; } snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); mb_info->cmdline = VTOP(cmdline); mb_info->flags |= MULTIBOOT_INFO_CMDLINE; } /* Find the entry point of the Xen kernel and save it for later */ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { printf("Unable to find %s entry point\n", fp->f_name); error = EINVAL; goto error; } ehdr = (Elf_Ehdr *)&(md->md_data); entry = ehdr->e_entry & 0xffffff; /* * Prepare the multiboot module list, Xen assumes the first * module is the Dom0 kernel, and the second one is the initramfs. * This is not optimal for FreeBSD, that doesn't have a initramfs * but instead loads modules dynamically and creates the metadata * info on-the-fly. * * As expected, the first multiboot module is going to be the * FreeBSD kernel loaded as a raw file. The second module is going * to contain the metadata info and the loaded modules. * * On native FreeBSD loads all the modules and then places the * metadata info at the end, but this is painful when running on Xen, * because it relocates the second multiboot module wherever it * likes. In order to workaround this limitation the metadata * information is placed at the start of the second module and * the original modulep value is saved together with the other * metadata, so we can relocate everything. * * Native layout: * fp->f_addr + fp->f_size * +---------+----------------+------------+ * | | | | * | Kernel | Modules | Metadata | * | | | | * +---------+----------------+------------+ * fp->f_addr modulep kernend * * Xen layout: * * Initial: * fp->f_addr + fp->f_size * +---------+----------+----------------+------------+ * | | | | | * | Kernel | Reserved | Modules | Metadata | * | | | | dry run | * +---------+----------+----------------+------------+ * fp->f_addr * * After metadata polacement (ie: final): * fp->f_addr + fp->f_size * +-----------+---------+----------+----------------+ * | | | | | * | Kernel | Free | Metadata | Modules | * | | | | | * +-----------+---------+----------+----------------+ * fp->f_addr modulep kernend * \__________/ \__________________________/ * Multiboot module 0 Multiboot module 1 */ fp = file_findfile(NULL, "elf kernel"); if (fp == NULL) { printf("No FreeBSD kernel provided, aborting\n"); error = EINVAL; goto error; } if (fp->f_metadata != NULL) { printf("FreeBSD kernel already contains metadata, aborting\n"); error = EINVAL; goto error; } mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); if (mb_mod == NULL) { error = ENOMEM; goto error; } bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES); /* * Calculate how much memory is needed for the metatdata. We did * an approximation of the maximum size when loading the kernel, * but now we know the exact size, so we can release some of this * preallocated memory if not needed. */ last_addr = roundup(max_addr(), PAGE_SIZE); mod_num = num_modules(fp); /* * Place the metadata after the last used address in order to * calculate it's size, this will not be used. */ error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0); if (error != 0) { printf("bi_load64 failed: %d\n", error); goto error; } metadata_size = roundup(kernend - last_addr, PAGE_SIZE); /* Check that the size is not greater than what we have reserved */ if (metadata_size > METADATA_RESV_SIZE(mod_num)) { printf("Required memory for metadata is greater than reserved " "space, please increase METADATA_FIXED_SIZE and " "METADATA_MODULE_SIZE and rebuild the loader\n"); error = ENOMEM; goto error; } /* Clean the metadata added to the kernel in the bi_load64 dry run */ file_removemetadata(fp); /* * This is the position where the second multiboot module * will be placed. */ module_start = fp->f_addr + fp->f_size - metadata_size; error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0); if (error != 0) { printf("bi_load64 failed: %d\n", error); goto error; } mb_mod[0].mod_start = fp->f_addr; mb_mod[0].mod_end = fp->f_addr + fp->f_size; mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num); mb_mod[1].mod_start = module_start; mb_mod[1].mod_end = last_addr; mb_info->mods_count = NUM_MODULES; mb_info->mods_addr = VTOP(mb_mod); mb_info->flags |= MULTIBOOT_INFO_MODS; dev_cleanup(); __exec((void *)VTOP(multiboot_tramp), (void *)entry, (void *)VTOP(mb_info)); panic("exec returned"); error: if (mb_mod) free(mb_mod); if (mb_info) free(mb_info); if (cmdline) free(cmdline); return (error); }
/* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such * preparations as are required, and do so. */ static int elf64_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t modulep, kernend, trampcode, trampstack; int err, i; ACPI_TABLE_RSDP *rsdp; char buf[24]; int revision; rsdp = efi_get_table(&acpi20_guid); if (rsdp == NULL) { rsdp = efi_get_table(&acpi_guid); } if (rsdp != NULL) { sprintf(buf, "0x%016llx", (unsigned long long)rsdp); setenv("hint.acpi.0.rsdp", buf, 1); revision = rsdp->Revision; if (revision == 0) revision = 1; sprintf(buf, "%d", revision); setenv("hint.acpi.0.revision", buf, 1); strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId)); buf[sizeof(rsdp->OemId)] = '\0'; setenv("hint.acpi.0.oem", buf, 1); sprintf(buf, "0x%016x", rsdp->RsdtPhysicalAddress); setenv("hint.acpi.0.rsdt", buf, 1); if (revision >= 2) { /* XXX extended checksum? */ sprintf(buf, "0x%016llx", (unsigned long long)rsdp->XsdtPhysicalAddress); setenv("hint.acpi.0.xsdt", buf, 1); sprintf(buf, "%d", rsdp->Length); setenv("hint.acpi.0.xsdt_length", buf, 1); } } if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); trampcode = (vm_offset_t)0x0000000040000000; err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 1, (EFI_PHYSICAL_ADDRESS *)&trampcode); bzero((void *)trampcode, EFI_PAGE_SIZE); trampstack = trampcode + EFI_PAGE_SIZE - 8; bcopy((void *)&amd64_tramp, (void *)trampcode, amd64_tramp_size); trampoline = (void *)trampcode; PT4 = (p4_entry_t *)0x0000000040000000; err = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, 3, (EFI_PHYSICAL_ADDRESS *)&PT4); bzero(PT4, 3 * EFI_PAGE_SIZE); PT3 = &PT4[512]; PT2 = &PT3[512]; /* * This is kinda brutal, but every single 1GB VM memory segment points * to the same first 1GB of physical memory. But it is more than * adequate. */ for (i = 0; i < 512; i++) { /* Each slot of the L4 pages points to the same L3 page. */ PT4[i] = (p4_entry_t)PT3; PT4[i] |= PG_V | PG_RW | PG_U; /* Each slot of the L3 pages points to the same L2 page. */ PT3[i] = (p3_entry_t)PT2; PT3[i] |= PG_V | PG_RW | PG_U; /* The L2 page slots are mapped with 2MB pages for 1GB. */ PT2[i] = i * (2 * 1024 * 1024); PT2[i] |= PG_V | PG_RW | PG_PS | PG_U; } printf("Start @ 0x%lx ...\n", ehdr->e_entry); err = bi_load(fp->f_args, &modulep, &kernend); if (err != 0) return(err); dev_cleanup(); trampoline(trampstack, efi_copy_finish, kernend, modulep, PT4, ehdr->e_entry); panic("exec returned"); }