/* * Copyright (c) 2011, Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "efilinux.h" #include "stdlib.h" #include "boot.h" #include "multiboot.h" #define ERROR_STRING_LENGTH 32 #define EFI_LOADER_SIGNATURE "EL64" #define LEAGCY_BIOS #define ACPI_XSDT_ENTRY_SIZE (sizeof (UINT64)) #define ACPI_NAME_SIZE 4 #define ACPI_OEM_ID_SIZE 6 #define ACPI_OEM_TABLE_ID_SIZE 8 EFI_SYSTEM_TABLE *sys_table; EFI_BOOT_SERVICES *boot; EFI_RUNTIME_SERVICES *runtime; /** * memory_map - Allocate and fill out an array of memory descriptors * @map_buf: buffer containing the memory map * @map_size: size of the buffer containing the memory map * @map_key: key for the current memory map * @desc_size: size of the desc * @desc_version: memory descriptor version * * On success, @map_size contains the size of the memory map pointed * to by @map_buf and @map_key, @desc_size and @desc_version are * updated. */ EFI_STATUS memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size, UINTN *map_key, UINTN *desc_size, UINT32 *desc_version) { EFI_STATUS err; *map_size = sizeof(**map_buf) * 31; get_map: /* * Because we're about to allocate memory, we may * potentially create a new memory descriptor, thereby * increasing the size of the memory map. So increase * the buffer size by the size of one memory * descriptor, just in case. */ *map_size += sizeof(**map_buf); err = allocate_pool(EfiLoaderData, *map_size, (void **)map_buf); if (err != EFI_SUCCESS) { Print(L"Failed to allocate pool for memory map"); goto failed; } err = get_memory_map(map_size, *map_buf, map_key, desc_size, desc_version); if (err != EFI_SUCCESS) { if (err == EFI_BUFFER_TOO_SMALL) { /* * 'map_size' has been updated to reflect the * required size of a map buffer. */ free_pool((void *)*map_buf); goto get_map; } Print(L"Failed to get memory map"); goto failed; } failed: return err; } static inline BOOLEAN isspace(CHAR8 ch) { return ((unsigned char)ch <= ' '); } #if 0 static void print_ch(char *str) { int j; CHAR16 *buf; int len = strlen(str); buf = malloc((strlen(str) + 1)* 2); for (j=0; jFilePath); for (i = 0; i < StrLen(pathstr); i++) { if (pathstr[i] == '/') pathstr[i] = '\\'; } pathlen = StrLen(pathstr); if (name[0] == '\\') { *path = FileDevicePath(info->DeviceHandle, name); goto out; } for (i=pathlen - 1; i > 0; i--) { if (pathstr[i] == '\\') break; } pathstr[i] = '\0'; pathlen = StrLen(pathstr); pathlen++; pathname = AllocatePool((pathlen + 1 + StrLen(name))*sizeof(CHAR16)); if (!pathname) { Print(L"Failed to allocate memory for pathname\n"); efi_status = EFI_OUT_OF_RESOURCES; goto out; } StrCpy(pathname, pathstr); StrCat(pathname, L"\\"); StrCat(pathname, name); *path = FileDevicePath(info->DeviceHandle, pathname); out: FreePool(pathstr); return efi_status; } /** * load_kernel - Load a kernel image into memory from the boot device */ EFI_STATUS load_sos_image(EFI_HANDLE image, CHAR16 *name, CHAR16 *cmdline) { UINTN map_size, _map_size, map_key; UINT32 desc_version; UINTN desc_size; EFI_MEMORY_DESCRIPTOR *map_buf; EFI_PHYSICAL_ADDRESS addr; EFI_LOADED_IMAGE *info = NULL; EFI_STATUS err; struct multiboot_mmap *mmap; struct multiboot_info *mbi; struct acpi_table_rsdp *rsdp; int i, j; err = handle_protocol(image, &LoadedImageProtocol, (void **)&info); if (err != EFI_SUCCESS) goto out; EFI_HANDLE bz_hd; EFI_DEVICE_PATH *path; EFI_LOADED_IMAGE *bz_info = NULL; EFI_IMAGE_ENTRY_POINT pe_entry; struct efi_ctx* pe; err = get_path(name, info, &path); if (err != EFI_SUCCESS) { Print(L"fail to get bzImage.efi path"); goto out; } err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image, path, NULL, 0, &bz_hd); if (err != EFI_SUCCESS) { Print(L"failed to load bzImage %lx\n", err); goto out; } err = handle_protocol(bz_hd, &LoadedImageProtocol, (void **)&bz_info); if (err != EFI_SUCCESS) goto out; if (cmdline) { bz_info->LoadOptions = cmdline; bz_info->LoadOptionsSize = (StrLen(cmdline) + 1) * sizeof(CHAR16); } pe_entry = get_pe_entry(bz_info->ImageBase); if (pe_entry == NULL) { Print(L"fail to get pe entry of bzImage\n"); goto out; } err = emalloc(sizeof(struct efi_ctx), 8, &addr); if (err != EFI_SUCCESS) goto out; pe = (struct efi_ctx*)(UINTN)addr; pe->entry = pe_entry; pe->handle = bz_hd; pe->table = sys_table; /* multiboot info */ err = emalloc(16384, 8, &addr); if (err != EFI_SUCCESS) goto out; mbi = (struct multiboot_info *)(UINTN)addr; memset((void *)mbi, 0x0, sizeof(*mbi)); /* allocate mmap[] */ err = emalloc(sizeof(struct multiboot_mmap)*128, 8, &addr); if (err != EFI_SUCCESS) goto out; mmap = (struct multiboot_mmap *)(UINTN)addr; memset((void *)mmap, 0x0, sizeof(*mmap)*128); EFI_CONFIGURATION_TABLE *config_table = sys_table->ConfigurationTable; for (i = 0; i < sys_table->NumberOfTableEntries;i++) { EFI_GUID acpi_20_table_guid = ACPI_20_TABLE_GUID; EFI_GUID acpi_table_guid = ACPI_TABLE_GUID; if (CompareGuid(&acpi_20_table_guid, &config_table->VendorGuid) == 0) { rsdp = config_table->VendorTable; break; } if (CompareGuid(&acpi_table_guid, &config_table->VendorGuid) == 0) rsdp = config_table->VendorTable; config_table++; } if (!rsdp) { Print(L"unable to find RSDP\n"); goto out; } /* We're just interested in the map's size for now */ map_size = 0; err = get_memory_map(&map_size, NULL, NULL, NULL, NULL); if (err != EFI_SUCCESS && err != EFI_BUFFER_TOO_SMALL) goto out; again: _map_size = map_size; err = emalloc(map_size, 1, &addr); if (err != EFI_SUCCESS) goto out; map_buf = (EFI_MEMORY_DESCRIPTOR *)(UINTN)addr; /* * Remember! We've already allocated map_buf with emalloc (and * 'map_size' contains its size) which means that it should be * positioned below our allocation for the kernel. Use that * space for the memory map. */ err = get_memory_map(&map_size, map_buf, &map_key, &desc_size, &desc_version); if (err != EFI_SUCCESS) { if (err == EFI_BUFFER_TOO_SMALL) { /* * Argh! The buffer that we allocated further * up wasn't large enough which means we need * to allocate them again, but this time * larger. 'map_size' has been updated by the * call to memory_map(). */ efree((UINTN)map_buf, _map_size); goto again; } goto out; } /* * Convert the EFI memory map to E820. */ for (i = 0, j = 0; i < map_size / desc_size; i++) { EFI_MEMORY_DESCRIPTOR *d; unsigned int e820_type = 0; d = (EFI_MEMORY_DESCRIPTOR *)((unsigned long)map_buf + (i * desc_size)); switch(d->Type) { case EfiReservedMemoryType: case EfiRuntimeServicesCode: case EfiRuntimeServicesData: case EfiMemoryMappedIO: case EfiMemoryMappedIOPortSpace: case EfiPalCode: e820_type = E820_RESERVED; break; case EfiUnusableMemory: e820_type = E820_UNUSABLE; break; case EfiACPIReclaimMemory: e820_type = E820_ACPI; break; case EfiLoaderCode: case EfiLoaderData: case EfiBootServicesCode: case EfiBootServicesData: case EfiConventionalMemory: e820_type = E820_RAM; break; case EfiACPIMemoryNVS: e820_type = E820_NVS; break; default: continue; } if (e820_type == E820_RAM) { UINT64 start = d->PhysicalStart; UINT64 end = d->PhysicalStart + (d->NumberOfPages< (ACRN_HV_ADDR + ACRN_HV_SIZE)) Print(L"e820[%d] start=%lx len=%lx\n", i, d->PhysicalStart, d->NumberOfPages << EFI_PAGE_SHIFT); } if (j && mmap[j-1].mm_type == e820_type && (mmap[j-1].mm_base_addr + mmap[j-1].mm_length) == d->PhysicalStart) { mmap[j-1].mm_length += d->NumberOfPages << EFI_PAGE_SHIFT; } else { mmap[j].mm_base_addr = d->PhysicalStart; mmap[j].mm_length = d->NumberOfPages << EFI_PAGE_SHIFT; mmap[j].mm_type = e820_type; j++; } } /* switch hv memory region(0x20000000 ~ 0x22000000) to availiable RAM in e820 table */ mmap[j].mm_base_addr = ACRN_HV_ADDR; mmap[j].mm_length = ACRN_HV_SIZE; mmap[j].mm_type = E820_RAM; j++; /* reserve secondary memory region(0x1000 ~ 0x10000) for hv */ err = __emalloc(ACRN_SECONDARY_SIZE, ACRN_SECONDARY_ADDR, &addr, EfiReservedMemoryType); if (err != EFI_SUCCESS) goto out; mbi->mi_flags |= MULTIBOOT_INFO_HAS_MMAP | MULTIBOOT_INFO_HAS_CMDLINE; mbi->mi_mmap_length = j*sizeof(struct multiboot_mmap); //mbi->mi_cmdline = (UINTN)"uart=mmio@0x92230000"; //mbi->mi_cmdline = (UINTN)"uart=port@0x3F8"; mbi->mi_cmdline = (UINTN)"uart=disabled"; mbi->mi_mmap_addr = (UINTN)mmap; #ifdef LEAGCY_BIOS /* copy rsdt in low memory space(0~0x1000) for hypervisor parsing */ memcpy((void *)0x500, (void*)rsdp, sizeof(struct acpi_table_rsdp)); *(UINT16*)(0x40E) = 0x50; #endif //Print(L"start 9!\n"); asm volatile ("mov %%cr0, %0":"=r"(pe->cr0)); asm volatile ("mov %%cr3, %0":"=r"(pe->cr3)); asm volatile ("mov %%cr4, %0":"=r"(pe->cr4)); asm volatile ("sidt %0" :: "m" (pe->idt)); asm volatile ("sgdt %0" :: "m" (pe->gdt)); asm volatile ("str %0" :: "m" (pe->tr_sel)); asm volatile ("sldt %0" :: "m" (pe->ldt_sel)); asm volatile ("mov %%cs, %%ax": "=a"(pe->cs_sel)); asm volatile ("lar %%eax, %%eax" :"=a"(pe->cs_ar) :"a"(pe->cs_sel) ); pe->cs_ar = (pe->cs_ar >> 8) & 0xf0ff; /* clear bits 11:8 */ asm volatile ("mov %%es, %%ax": "=a"(pe->es_sel)); asm volatile ("mov %%ss, %%ax": "=a"(pe->ss_sel)); asm volatile ("mov %%ds, %%ax": "=a"(pe->ds_sel)); asm volatile ("mov %%fs, %%ax": "=a"(pe->fs_sel)); asm volatile ("mov %%gs, %%ax": "=a"(pe->gs_sel)); uint32_t idx = 0xC0000080; /* MSR_IA32_EFER */ uint32_t msrl, msrh; asm volatile ("rdmsr":"=a"(msrl), "=d"(msrh): "c"(idx)); pe->efer = ((uint64_t)msrh<<32) | msrl; asm volatile ("pushf\n\t" "pop %0\n\t" :"=r"(pe->rflags):); asm volatile ("movq %%rsp, %0":"=r"(pe->rsp)); hv_jump(ACRN_HV_ADDR, mbi, pe); out: return err; } static EFI_STATUS parse_args(CHAR16 *options, UINT32 size, CHAR16 **name, CHAR16 **hcmdline, CHAR16 **scmdline) { CHAR16 *n, *p, *cmdline, *search; UINTN i = 0; *hcmdline = NULL; *scmdline = NULL; *name = NULL; cmdline = StrDuplicate(options); search = PoolPrint(L"sos="); n = strstr_16(cmdline, search); if (!n) { Print(L"Failed to get sos\n"); return EFI_OUT_OF_RESOURCES; } FreePool(search); n += 4; p = n; i = 0; while (*n && !isspace((CHAR8)*n)) { n++; i++; } *n++ = '\0'; *name = p; *scmdline = n; return EFI_SUCCESS; } /** * efi_main - The entry point for the OS loader image. * @image: firmware-allocated handle that identifies the image * @sys_table: EFI system table */ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) { WCHAR *error_buf; EFI_STATUS err; EFI_LOADED_IMAGE *info; EFI_PHYSICAL_ADDRESS addr; CHAR16 *options = NULL, *name; UINT32 options_size = 0; CHAR16 *hcmdline, *scmdline; UINTN sec_addr; UINTN sec_size; char *section; InitializeLib(image, _table); sys_table = _table; boot = sys_table->BootServices; runtime = sys_table->RuntimeServices; if (CheckCrc(sys_table->Hdr.HeaderSize, &sys_table->Hdr) != TRUE) return EFI_LOAD_ERROR; err = handle_protocol(image, &LoadedImageProtocol, (void **)&info); if (err != EFI_SUCCESS) goto failed; options = info->LoadOptions; options_size = info->LoadOptionsSize; err = parse_args(options, options_size, &name, &hcmdline, &scmdline); if (err != EFI_SUCCESS) return err; section = ".hv"; err = get_pe_section(info->ImageBase, section, &sec_addr, &sec_size); if (EFI_ERROR(err)) { Print(L"Unable to locate section of ACRNHV %r ", err); goto failed; } err = __emalloc(ACRN_HV_SIZE, ACRN_HV_ADDR, &addr, EfiReservedMemoryType); if (err != EFI_SUCCESS) goto failed; /* Copy ACRNHV binary to fixed phys addr. LoadImage and StartImage ?? */ memcpy((char*)addr, info->ImageBase + sec_addr, sec_size); /* load sos and run hypervisor */ err = load_sos_image(image, name, scmdline); if (err != EFI_SUCCESS) goto free_args; return EFI_SUCCESS; free_args: free(name); failed: /* * We need to be careful not to trash 'err' here. If we fail * to allocate enough memory to hold the error string fallback * to returning 'err'. */ if (allocate_pool(EfiLoaderData, ERROR_STRING_LENGTH, (void **)&error_buf) != EFI_SUCCESS) { Print(L"Couldn't allocate pages for error string\n"); return err; } StatusToString(error_buf, err); Print(L": %s\n", error_buf); return exit(image, err, ERROR_STRING_LENGTH, error_buf); }