1/* 2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6 7/* Guest specific booting to do bootinfo manipulation */ 8 9#include <autoconf.h> 10#include <sel4vm/gen_config.h> 11 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15 16#include <cpio/cpio.h> 17#include <sel4utils/mapping.h> 18#include <vka/capops.h> 19 20#include <sel4vm/guest_vm.h> 21#include <sel4vm/arch/guest_x86_context.h> 22#include <sel4vm/guest_ram.h> 23#include <sel4vm/guest_memory_helpers.h> 24#include <sel4vm/arch/vmcs_fields.h> 25#include <sel4vm/guest_memory.h> 26 27#include <sel4vmmplatsupport/guest_memory_util.h> 28#include <sel4vmmplatsupport/arch/guest_boot_init.h> 29#include <sel4vmmplatsupport/arch/guest_boot_info.h> 30#include <sel4vmmplatsupport/arch/e820.h> 31#include <sel4vmmplatsupport/arch/acpi.h> 32 33#include <sel4/arch/bootinfo_types.h> 34 35static inline uint32_t vmm_plat_vesa_fbuffer_size(seL4_VBEModeInfoBlock_t *block) 36{ 37 assert(block); 38 return ALIGN_UP(block->vbe_common.bytesPerScanLine * block->vbe12_part1.yRes, 65536); 39} 40 41static int make_guest_cmd_line_continued(vm_t *vm, uintptr_t phys, void *vaddr, size_t size, size_t offset, 42 void *cookie) 43{ 44 /* Copy the string to this area. */ 45 const char *cmdline = (const char *)cookie; 46 memcpy(vaddr, cmdline + offset, size); 47 return 0; 48} 49 50static int make_guest_cmd_line(vm_t *vm, const char *cmdline, uintptr_t *guest_cmd_addr, size_t *guest_cmd_len) 51{ 52 /* Allocate command line from guest ram */ 53 int len = strlen(cmdline); 54 uintptr_t cmd_addr = vm_ram_allocate(vm, len + 1); 55 if (cmd_addr == 0) { 56 ZF_LOGE("Failed to allocate guest cmdline (length %d)", len); 57 return -1; 58 } 59 printf("Constructing guest cmdline at 0x%x of size %d\n", (unsigned int)cmd_addr, len); 60 *guest_cmd_addr = cmd_addr; 61 *guest_cmd_len = len; 62 return vm_ram_touch(vm, cmd_addr, len + 1, make_guest_cmd_line_continued, (void *)cmdline); 63} 64 65static void make_guest_screen_info(vm_t *vm, struct screen_info *info) 66{ 67 /* VESA information */ 68 seL4_X86_BootInfo_VBE vbeinfo; 69 ssize_t result; 70 int error; 71 result = simple_get_extended_bootinfo(vm->simple, SEL4_BOOTINFO_HEADER_X86_VBE, &vbeinfo, 72 sizeof(seL4_X86_BootInfo_VBE)); 73 uintptr_t base = 0; 74 size_t fbuffer_size; 75 if (config_set(CONFIG_VMM_PLATSUPPORT_VESA_FRAMEBUFFER) && result != -1) { 76 /* map the protected mode interface at the same location we are told about it at. 77 * this is just to guarantee that it ends up within the segment addressable range */ 78 uintptr_t pm_base = (vbeinfo.vbeInterfaceSeg << 4) + vbeinfo.vbeInterfaceOff; 79 error = 0; 80 if (pm_base > 0xc000) { 81 /* construct a page size and aligned region to map */ 82 uintptr_t aligned_pm = ROUND_DOWN(pm_base, PAGE_SIZE_4K); 83 int size = vbeinfo.vbeInterfaceLen + (pm_base - aligned_pm); 84 size = ROUND_UP(size, PAGE_SIZE_4K); 85 vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, aligned_pm, size, 86 default_error_fault_callback, NULL); 87 if (reservation) { 88 error = map_ut_alloc_reservation(vm, reservation); 89 } else { 90 error = -1; 91 } 92 } 93 if (error) { 94 ZF_LOGE("Failed to map vbe protected mode interface for VESA frame buffer. Disabling"); 95 } else { 96 fbuffer_size = vmm_plat_vesa_fbuffer_size(&vbeinfo.vbeModeInfoBlock); 97 vm_memory_reservation_t *reservation = vm_reserve_anon_memory(vm, fbuffer_size, 98 default_error_fault_callback, NULL, &base); 99 if (!reservation) { 100 ZF_LOGE("Failed to reserve base pointer for VESA frame buffer. Disabling"); 101 } else { 102 error = map_ut_alloc_reservation_with_base_paddr(vm, vbeinfo.vbeModeInfoBlock.vbe20.physBasePtr, reservation); 103 if (error) { 104 ZF_LOGE("Failed to map base pointer for VESA frame buffer. Disabling"); 105 base = 0; 106 } 107 } 108 } 109 } 110 if (base) { 111 info->orig_video_isVGA = 0x23; // Tell Linux it's a VESA mode 112 info->lfb_width = vbeinfo.vbeModeInfoBlock.vbe12_part1.xRes; 113 info->lfb_height = vbeinfo.vbeModeInfoBlock.vbe12_part1.yRes; 114 info->lfb_depth = vbeinfo.vbeModeInfoBlock.vbe12_part1.bitsPerPixel; 115 116 info->lfb_base = base; 117 info->lfb_size = fbuffer_size >> 16; 118 info->lfb_linelength = vbeinfo.vbeModeInfoBlock.vbe_common.bytesPerScanLine; 119 120 info->red_size = vbeinfo.vbeModeInfoBlock.vbe12_part2.redLen; 121 info->red_pos = vbeinfo.vbeModeInfoBlock.vbe12_part2.redOff; 122 info->green_size = vbeinfo.vbeModeInfoBlock.vbe12_part2.greenLen; 123 info->green_pos = vbeinfo.vbeModeInfoBlock.vbe12_part2.greenOff; 124 info->blue_size = vbeinfo.vbeModeInfoBlock.vbe12_part2.blueLen; 125 info->blue_pos = vbeinfo.vbeModeInfoBlock.vbe12_part2.blueOff; 126 info->rsvd_size = vbeinfo.vbeModeInfoBlock.vbe12_part2.rsvdLen; 127 info->rsvd_pos = vbeinfo.vbeModeInfoBlock.vbe12_part2.rsvdOff; 128 info->vesapm_seg = vbeinfo.vbeInterfaceSeg; 129 info->vesapm_off = vbeinfo.vbeInterfaceOff; 130 info->pages = vbeinfo.vbeModeInfoBlock.vbe12_part1.planes; 131 } else { 132 memset(info, 0, sizeof(*info)); 133 } 134} 135 136static int make_guest_e820_map(struct e820entry *e820, vm_mem_t *guest_memory) 137{ 138 int i; 139 int entry = 0; 140 /* Create an initial entry at 0 that is reserved */ 141 e820[entry].addr = 0; 142 e820[entry].size = 0; 143 e820[entry].type = E820_RESERVED; 144 assert(guest_memory->num_ram_regions > 0); 145 for (i = 0; i < guest_memory->num_ram_regions; i++) { 146 /* Check for discontinuity. We need this check since we can have multiple 147 * regions that are contiguous if they have different allocation flags. 148 * However we are reporting ALL of this memory to the guest */ 149 if (e820[entry].addr + e820[entry].size != guest_memory->ram_regions[i].start) { 150 /* Finish region. Unless it was zero sized */ 151 if (e820[entry].size != 0) { 152 entry++; 153 assert(entry < E820MAX); 154 e820[entry].addr = e820[entry - 1].addr + e820[entry - 1].size; 155 e820[entry].type = E820_RESERVED; 156 } 157 /* Pad region */ 158 e820[entry].size = guest_memory->ram_regions[i].start - e820[entry].addr; 159 /* Now start a new RAM region */ 160 entry++; 161 assert(entry < E820MAX); 162 e820[entry].addr = guest_memory->ram_regions[i].start; 163 e820[entry].type = E820_RAM; 164 } 165 /* Increase region to size */ 166 e820[entry].size = guest_memory->ram_regions[i].start - e820[entry].addr + guest_memory->ram_regions[i].size; 167 } 168 /* Create empty region at the end */ 169 entry++; 170 assert(entry < E820MAX); 171 e820[entry].addr = e820[entry - 1].addr + e820[entry - 1].size; 172 e820[entry].size = 0x100000000ull - e820[entry].addr; 173 e820[entry].type = E820_RESERVED; 174 printf("Final e820 map is:\n"); 175 for (i = 0; i <= entry; i++) { 176 printf("\t0x%x - 0x%x type %d\n", (unsigned int)e820[i].addr, (unsigned int)(e820[i].addr + e820[i].size), 177 e820[i].type); 178 assert(e820[i].addr < e820[i].addr + e820[i].size); 179 } 180 return entry + 1; 181} 182 183static int make_guest_boot_info(vm_t *vm, uintptr_t guest_cmd_addr, size_t guest_cmd_len, 184 guest_kernel_image_t guest_kernel_image, guest_image_t guest_ramdisk_image, 185 uintptr_t *guest_boot_info_addr) 186{ 187 /* TODO: Bootinfo struct needs to be allocated in location accessable by real mode? */ 188 uintptr_t addr = vm_ram_allocate(vm, sizeof(struct boot_params)); 189 if (addr == 0) { 190 ZF_LOGE("Failed to allocate %zu bytes for guest boot info struct", sizeof(struct boot_params)); 191 return -1; 192 } 193 printf("Guest boot info allocated at %p. Populating...\n", (void *)addr); 194 /* Map in BIOS boot info structure. */ 195 struct boot_params boot_info; 196 memset(&boot_info, 0, sizeof(struct boot_params)); 197 198 /* Initialise basic bootinfo structure. Src: Linux kernel Documentation/x86/boot.txt */ 199 boot_info.hdr.header = 0x53726448; /* Magic number 'HdrS' */ 200 boot_info.hdr.boot_flag = 0xAA55; /* Magic number for Linux. */ 201 boot_info.hdr.type_of_loader = 0xFF; /* Undefined loeader type. */ 202 boot_info.hdr.code32_start = guest_kernel_image.kernel_image.load_paddr; 203 boot_info.hdr.kernel_alignment = guest_kernel_image.kernel_image.alignment; 204 boot_info.hdr.relocatable_kernel = true; 205 206 /* Set up screen information. */ 207 /* Tell Guest OS about VESA mode. */ 208 make_guest_screen_info(vm, &boot_info.screen_info); 209 210 /* Create e820 memory map */ 211 boot_info.e820_entries = make_guest_e820_map(boot_info.e820_map, &vm->mem); 212 213 /* Pass in the command line string. */ 214 boot_info.hdr.cmd_line_ptr = guest_cmd_addr; 215 boot_info.hdr.cmdline_size = guest_cmd_len; 216 217 /* These are not needed to be precise, because Linux uses these values 218 * only to raise an error when the decompression code cannot find good 219 * space. ref: GRUB2 source code loader/i386/linux.c */ 220 boot_info.alt_mem_k = 0;//((32 * 0x100000) >> 10); 221 222 /* Pass in initramfs. */ 223 if (guest_ramdisk_image.load_paddr) { 224 boot_info.hdr.ramdisk_image = (uint32_t) guest_ramdisk_image.load_paddr; 225 boot_info.hdr.ramdisk_size = guest_ramdisk_image.size; 226 boot_info.hdr.root_dev = 0x0100; 227 boot_info.hdr.version = 0x0204; /* Report version 2.04 in order to report ramdisk_image. */ 228 } else { 229 boot_info.hdr.version = 0x0202; 230 } 231 int err = vm_ram_touch(vm, addr, sizeof(boot_info), vm_guest_ram_write_callback, &boot_info); 232 if (err) { 233 ZF_LOGE("Failed to populalte guest boot info region"); 234 return -1; 235 } 236 *guest_boot_info_addr = addr; 237 return 0; 238} 239 240/* Init the guest page directory, cmd line args and boot info structures. */ 241int vmm_plat_init_guest_boot_structure(vm_t *vm, const char *cmdline, 242 guest_kernel_image_t guest_kernel_image, guest_image_t guest_ramdisk_image, 243 uintptr_t *guest_boot_info_addr) 244{ 245 int UNUSED err; 246 uintptr_t guest_cmd_addr; 247 size_t guest_cmd_size; 248 249 err = make_guest_cmd_line(vm, cmdline, &guest_cmd_addr, &guest_cmd_size); 250 if (err) { 251 return -1; 252 } 253 254 err = make_guest_boot_info(vm, guest_cmd_addr, guest_cmd_size, 255 guest_kernel_image, guest_ramdisk_image, guest_boot_info_addr); 256 if (err) { 257 return -1; 258 } 259 260 err = make_guest_acpi_tables(vm); 261 return err; 262} 263 264int vmm_plat_init_guest_thread_state(vm_vcpu_t *vcpu, uintptr_t guest_entry_addr, 265 uintptr_t guest_boot_info_addr) 266{ 267 int err; 268 seL4_VCPUContext context; 269 err = vm_get_thread_context(vcpu, &context); 270 if (err) { 271 return -1; 272 } 273 context.eax = 0; 274 context.ebx = 0; 275 context.ecx = 0; 276 context.edx = 0; 277 context.esi = (unsigned int)guest_boot_info_addr; 278 err = vm_set_thread_context(vcpu, context); 279 if (err) { 280 ZF_LOGE("Failed to init guest state: Unable to set inital thread context"); 281 return -1; 282 } 283 284 /* Entry point. */ 285 printf("Initializing guest to start running at 0x%x\n", (unsigned int) guest_entry_addr); 286 err = vm_set_vmcs_field(vcpu, VMX_GUEST_RIP, (unsigned int) guest_entry_addr); 287 if (err) { 288 ZF_LOGE("Failed to init guest state: Unable set Guest RIP"); 289 } 290 return err; 291} 292