193dd2aa3SAndrew Jones /* 293dd2aa3SAndrew Jones * Initialize machine setup information 393dd2aa3SAndrew Jones * 493dd2aa3SAndrew Jones * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com> 5dbd38004SZixuan Wang * Copyright (C) 2021, Google Inc, Zixuan Wang <zixuanwang@google.com> 693dd2aa3SAndrew Jones * 793dd2aa3SAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 893dd2aa3SAndrew Jones */ 993dd2aa3SAndrew Jones #include "libcflat.h" 10716cea8aSPaolo Bonzini #include "fwcfg.h" 11716cea8aSPaolo Bonzini #include "alloc_phys.h" 1203b1e457SNadav Amit #include "argv.h" 13dbd38004SZixuan Wang #include "desc.h" 14dbd38004SZixuan Wang #include "apic.h" 15dbd38004SZixuan Wang #include "apic-defs.h" 16dbd38004SZixuan Wang #include "asm/setup.h" 1793dd2aa3SAndrew Jones 18716cea8aSPaolo Bonzini extern char edata; 19716cea8aSPaolo Bonzini 20716cea8aSPaolo Bonzini struct mbi_bootinfo { 21716cea8aSPaolo Bonzini u32 flags; 22716cea8aSPaolo Bonzini u32 mem_lower; 23716cea8aSPaolo Bonzini u32 mem_upper; 24716cea8aSPaolo Bonzini u32 boot_device; 25716cea8aSPaolo Bonzini u32 cmdline; 26716cea8aSPaolo Bonzini u32 mods_count; 27716cea8aSPaolo Bonzini u32 mods_addr; 2848a0145fSPaolo Bonzini u32 reserved[4]; /* 28-43 */ 2948a0145fSPaolo Bonzini u32 mmap_length; 30716cea8aSPaolo Bonzini u32 mmap_addr; 31716cea8aSPaolo Bonzini u32 reserved0[3]; /* 52-63 */ 32716cea8aSPaolo Bonzini u32 bootloader; 33716cea8aSPaolo Bonzini u32 reserved1[5]; /* 68-87 */ 34716cea8aSPaolo Bonzini u32 size; 35716cea8aSPaolo Bonzini }; 36716cea8aSPaolo Bonzini 37716cea8aSPaolo Bonzini struct mbi_module { 38716cea8aSPaolo Bonzini u32 start, end; 39716cea8aSPaolo Bonzini u32 cmdline; 40716cea8aSPaolo Bonzini u32 unused; 41716cea8aSPaolo Bonzini }; 4293dd2aa3SAndrew Jones 4348a0145fSPaolo Bonzini struct mbi_mem { 4448a0145fSPaolo Bonzini u32 size; 4548a0145fSPaolo Bonzini u64 base_addr; 4648a0145fSPaolo Bonzini u64 length; 4748a0145fSPaolo Bonzini u32 type; 4848a0145fSPaolo Bonzini } __attribute__((packed)); 4948a0145fSPaolo Bonzini 503c7d322eSAndrew Jones #define ENV_SIZE 16384 513c7d322eSAndrew Jones 5206846df5SThomas Huth void setup_env(char *env, int size); 5306846df5SThomas Huth void setup_multiboot(struct mbi_bootinfo *bootinfo); 5406846df5SThomas Huth void setup_libcflat(void); 553c7d322eSAndrew Jones 5693dd2aa3SAndrew Jones char *initrd; 5793dd2aa3SAndrew Jones u32 initrd_size; 5893dd2aa3SAndrew Jones 593c7d322eSAndrew Jones static char env[ENV_SIZE]; 6048a0145fSPaolo Bonzini static struct mbi_bootinfo *bootinfo; 613c7d322eSAndrew Jones 6248a0145fSPaolo Bonzini #define HUGEPAGE_SIZE (1 << 21) 6348a0145fSPaolo Bonzini 6448a0145fSPaolo Bonzini #ifdef __x86_64__ 6548a0145fSPaolo Bonzini void find_highmem(void) 6648a0145fSPaolo Bonzini { 6748a0145fSPaolo Bonzini /* Memory above 4 GB is only supported on 64-bit systems. */ 6848a0145fSPaolo Bonzini if (!(bootinfo->flags & 64)) 6948a0145fSPaolo Bonzini return; 7048a0145fSPaolo Bonzini 7148a0145fSPaolo Bonzini u64 upper_end = bootinfo->mem_upper * 1024ull; 7248a0145fSPaolo Bonzini u64 best_start = (uintptr_t) &edata; 7348a0145fSPaolo Bonzini u64 best_end = upper_end; 74eb2db85dSNadav Amit u64 max_end = fwcfg_get_u64(FW_CFG_MAX_RAM); 75eb2db85dSNadav Amit if (max_end == 0) 76eb2db85dSNadav Amit max_end = -1ull; 7748a0145fSPaolo Bonzini bool found = false; 7848a0145fSPaolo Bonzini 7948a0145fSPaolo Bonzini uintptr_t mmap = bootinfo->mmap_addr; 8048a0145fSPaolo Bonzini while (mmap < bootinfo->mmap_addr + bootinfo->mmap_length) { 8148a0145fSPaolo Bonzini struct mbi_mem *mem = (void *)mmap; 8248a0145fSPaolo Bonzini mmap += mem->size + 4; 8348a0145fSPaolo Bonzini if (mem->type != 1) 8448a0145fSPaolo Bonzini continue; 8548a0145fSPaolo Bonzini if (mem->base_addr <= (uintptr_t) &edata || 8648a0145fSPaolo Bonzini (mem->base_addr <= upper_end && mem->base_addr + mem->length <= upper_end)) 8748a0145fSPaolo Bonzini continue; 8848a0145fSPaolo Bonzini if (mem->length < best_end - best_start) 8948a0145fSPaolo Bonzini continue; 90eb2db85dSNadav Amit if (mem->base_addr >= max_end) 91eb2db85dSNadav Amit continue; 9248a0145fSPaolo Bonzini best_start = mem->base_addr; 9348a0145fSPaolo Bonzini best_end = mem->base_addr + mem->length; 94eb2db85dSNadav Amit if (best_end > max_end) 95eb2db85dSNadav Amit best_end = max_end; 9648a0145fSPaolo Bonzini found = true; 9748a0145fSPaolo Bonzini } 9848a0145fSPaolo Bonzini 9948a0145fSPaolo Bonzini if (found) { 10048a0145fSPaolo Bonzini best_start = (best_start + HUGEPAGE_SIZE - 1) & -HUGEPAGE_SIZE; 10148a0145fSPaolo Bonzini best_end = best_end & -HUGEPAGE_SIZE; 10248a0145fSPaolo Bonzini phys_alloc_init(best_start, best_end - best_start); 10348a0145fSPaolo Bonzini } 10448a0145fSPaolo Bonzini } 105dbd38004SZixuan Wang 106dbd38004SZixuan Wang /* Setup TSS for the current processor, and return TSS offset within GDT */ 1077e33895dSPaolo Bonzini unsigned long setup_tss(u8 *stacktop) 108dbd38004SZixuan Wang { 109dbd38004SZixuan Wang u32 id; 110dbd38004SZixuan Wang tss64_t *tss_entry; 111dbd38004SZixuan Wang 112dbd38004SZixuan Wang id = apic_id(); 113dbd38004SZixuan Wang 114dbd38004SZixuan Wang /* Runtime address of current TSS */ 115dbd38004SZixuan Wang tss_entry = &tss[id]; 116dbd38004SZixuan Wang 117dbd38004SZixuan Wang /* Update TSS */ 118dbd38004SZixuan Wang memset((void *)tss_entry, 0, sizeof(tss64_t)); 119dbd38004SZixuan Wang 120dbd38004SZixuan Wang /* Update TSS descriptors; each descriptor takes up 2 entries */ 121dbd38004SZixuan Wang set_gdt_entry(TSS_MAIN + id * 16, (unsigned long)tss_entry, 0xffff, 0x89, 0); 122dbd38004SZixuan Wang 123dbd38004SZixuan Wang return TSS_MAIN + id * 16; 124dbd38004SZixuan Wang } 1257e33895dSPaolo Bonzini #else 1267e33895dSPaolo Bonzini /* Setup TSS for the current processor, and return TSS offset within GDT */ 1277e33895dSPaolo Bonzini unsigned long setup_tss(u8 *stacktop) 1287e33895dSPaolo Bonzini { 1297e33895dSPaolo Bonzini u32 id; 1307e33895dSPaolo Bonzini tss32_t *tss_entry; 1317e33895dSPaolo Bonzini 1327e33895dSPaolo Bonzini id = apic_id(); 1337e33895dSPaolo Bonzini 1347e33895dSPaolo Bonzini /* Runtime address of current TSS */ 1357e33895dSPaolo Bonzini tss_entry = &tss[id]; 1367e33895dSPaolo Bonzini 1377e33895dSPaolo Bonzini /* Update TSS */ 1387e33895dSPaolo Bonzini memset((void *)tss_entry, 0, sizeof(tss32_t)); 1397e33895dSPaolo Bonzini tss_entry->ss0 = KERNEL_DS; 1407e33895dSPaolo Bonzini 1417e33895dSPaolo Bonzini /* Update descriptors for TSS and percpu data segment. */ 1427e33895dSPaolo Bonzini set_gdt_entry(TSS_MAIN + id * 8, 1437e33895dSPaolo Bonzini (unsigned long)tss_entry, 0xffff, 0x89, 0); 1447e33895dSPaolo Bonzini set_gdt_entry(TSS_MAIN + MAX_TEST_CPUS * 8 + id * 8, 1457e33895dSPaolo Bonzini (unsigned long)stacktop - 4096, 0xfffff, 0x93, 0xc0); 1467e33895dSPaolo Bonzini 1477e33895dSPaolo Bonzini return TSS_MAIN + id * 8; 1487e33895dSPaolo Bonzini } 14948a0145fSPaolo Bonzini #endif 15048a0145fSPaolo Bonzini 15148a0145fSPaolo Bonzini void setup_multiboot(struct mbi_bootinfo *bi) 15293dd2aa3SAndrew Jones { 153716cea8aSPaolo Bonzini struct mbi_module *mods; 15493dd2aa3SAndrew Jones 15548a0145fSPaolo Bonzini bootinfo = bi; 15648a0145fSPaolo Bonzini 15748a0145fSPaolo Bonzini u64 best_start = (uintptr_t) &edata; 15848a0145fSPaolo Bonzini u64 best_end = bootinfo->mem_upper * 1024ull; 15948a0145fSPaolo Bonzini phys_alloc_init(best_start, best_end - best_start); 160cb67196aSPaolo Bonzini 161716cea8aSPaolo Bonzini if (bootinfo->mods_count != 1) 16293dd2aa3SAndrew Jones return; 16393dd2aa3SAndrew Jones 164716cea8aSPaolo Bonzini mods = (struct mbi_module *)(uintptr_t) bootinfo->mods_addr; 16593dd2aa3SAndrew Jones 166716cea8aSPaolo Bonzini initrd = (char *)(uintptr_t) mods->start; 167716cea8aSPaolo Bonzini initrd_size = mods->end - mods->start; 16893dd2aa3SAndrew Jones } 1693c7d322eSAndrew Jones 170ad5fb883SZixuan Wang #ifdef TARGET_EFI 171ad5fb883SZixuan Wang 1724143d8a7SZixuan Wang /* From x86/efi/efistart64.S */ 1734143d8a7SZixuan Wang extern void load_idt(void); 1743298643cSZixuan Wang extern void load_gdt_tss(size_t tss_offset); 1753298643cSZixuan Wang 1761ae9072eSZixuan Wang void setup_efi_bootinfo(efi_bootinfo_t *efi_bootinfo) 1771ae9072eSZixuan Wang { 1781ae9072eSZixuan Wang efi_bootinfo->free_mem_size = 0; 1791ae9072eSZixuan Wang efi_bootinfo->free_mem_start = 0; 180*f20589d6SZixuan Wang efi_bootinfo->rsdp = NULL; 1811ae9072eSZixuan Wang } 1821ae9072eSZixuan Wang 1831ae9072eSZixuan Wang static efi_status_t setup_pre_boot_memory(unsigned long *mapkey, efi_bootinfo_t *efi_bootinfo) 1841ae9072eSZixuan Wang { 1851ae9072eSZixuan Wang int i; 1861ae9072eSZixuan Wang unsigned long free_mem_total_pages; 1871ae9072eSZixuan Wang efi_status_t status; 1881ae9072eSZixuan Wang struct efi_boot_memmap map; 1891ae9072eSZixuan Wang efi_memory_desc_t *buffer, *d; 1901ae9072eSZixuan Wang unsigned long map_size, desc_size, buff_size; 1911ae9072eSZixuan Wang u32 desc_ver; 1921ae9072eSZixuan Wang 1931ae9072eSZixuan Wang map.map = &buffer; 1941ae9072eSZixuan Wang map.map_size = &map_size; 1951ae9072eSZixuan Wang map.desc_size = &desc_size; 1961ae9072eSZixuan Wang map.desc_ver = &desc_ver; 1971ae9072eSZixuan Wang map.buff_size = &buff_size; 1981ae9072eSZixuan Wang map.key_ptr = mapkey; 1991ae9072eSZixuan Wang 2001ae9072eSZixuan Wang status = efi_get_memory_map(&map); 2011ae9072eSZixuan Wang if (status != EFI_SUCCESS) { 2021ae9072eSZixuan Wang return status; 2031ae9072eSZixuan Wang } 2041ae9072eSZixuan Wang 2051ae9072eSZixuan Wang /* 2061ae9072eSZixuan Wang * The 'buffer' contains multiple descriptors that describe memory 2071ae9072eSZixuan Wang * regions maintained by UEFI. This code records the largest free 2081ae9072eSZixuan Wang * EFI_CONVENTIONAL_MEMORY region which will be used to set up the 2091ae9072eSZixuan Wang * memory allocator, so that the memory allocator can work in the 2101ae9072eSZixuan Wang * largest free continuous memory region. 2111ae9072eSZixuan Wang */ 2121ae9072eSZixuan Wang free_mem_total_pages = 0; 2131ae9072eSZixuan Wang for (i = 0; i < map_size; i += desc_size) { 2141ae9072eSZixuan Wang d = (efi_memory_desc_t *)(&((u8 *)buffer)[i]); 2151ae9072eSZixuan Wang if (d->type == EFI_CONVENTIONAL_MEMORY) { 2161ae9072eSZixuan Wang if (free_mem_total_pages < d->num_pages) { 2171ae9072eSZixuan Wang free_mem_total_pages = d->num_pages; 2181ae9072eSZixuan Wang efi_bootinfo->free_mem_size = free_mem_total_pages << EFI_PAGE_SHIFT; 2191ae9072eSZixuan Wang efi_bootinfo->free_mem_start = d->phys_addr; 2201ae9072eSZixuan Wang } 2211ae9072eSZixuan Wang } 2221ae9072eSZixuan Wang } 2231ae9072eSZixuan Wang 2241ae9072eSZixuan Wang if (efi_bootinfo->free_mem_size == 0) { 2251ae9072eSZixuan Wang return EFI_OUT_OF_RESOURCES; 2261ae9072eSZixuan Wang } 2271ae9072eSZixuan Wang 2281ae9072eSZixuan Wang return EFI_SUCCESS; 2291ae9072eSZixuan Wang } 2301ae9072eSZixuan Wang 231*f20589d6SZixuan Wang static efi_status_t setup_pre_boot_rsdp(efi_bootinfo_t *efi_bootinfo) 232*f20589d6SZixuan Wang { 233*f20589d6SZixuan Wang return efi_get_system_config_table(ACPI_TABLE_GUID, (void **)&efi_bootinfo->rsdp); 234*f20589d6SZixuan Wang } 235*f20589d6SZixuan Wang 2361ae9072eSZixuan Wang efi_status_t setup_efi_pre_boot(unsigned long *mapkey, efi_bootinfo_t *efi_bootinfo) 2371ae9072eSZixuan Wang { 2381ae9072eSZixuan Wang efi_status_t status; 2391ae9072eSZixuan Wang 2401ae9072eSZixuan Wang status = setup_pre_boot_memory(mapkey, efi_bootinfo); 2411ae9072eSZixuan Wang if (status != EFI_SUCCESS) { 2421ae9072eSZixuan Wang printf("setup_pre_boot_memory() failed: "); 2431ae9072eSZixuan Wang switch (status) { 2441ae9072eSZixuan Wang case EFI_OUT_OF_RESOURCES: 2451ae9072eSZixuan Wang printf("No free memory region\n"); 2461ae9072eSZixuan Wang break; 2471ae9072eSZixuan Wang default: 2481ae9072eSZixuan Wang printf("Unknown error\n"); 2491ae9072eSZixuan Wang break; 2501ae9072eSZixuan Wang } 2511ae9072eSZixuan Wang return status; 2521ae9072eSZixuan Wang } 2531ae9072eSZixuan Wang 254*f20589d6SZixuan Wang status = setup_pre_boot_rsdp(efi_bootinfo); 255*f20589d6SZixuan Wang if (status != EFI_SUCCESS) { 256*f20589d6SZixuan Wang printf("Cannot find RSDP in EFI system table\n"); 257*f20589d6SZixuan Wang return status; 258*f20589d6SZixuan Wang } 259*f20589d6SZixuan Wang 2601ae9072eSZixuan Wang return EFI_SUCCESS; 2611ae9072eSZixuan Wang } 2621ae9072eSZixuan Wang 2633298643cSZixuan Wang static void setup_gdt_tss(void) 2643298643cSZixuan Wang { 2653298643cSZixuan Wang size_t tss_offset; 2663298643cSZixuan Wang 2673298643cSZixuan Wang /* 64-bit setup_tss does not use the stacktop argument. */ 2683298643cSZixuan Wang tss_offset = setup_tss(NULL); 2693298643cSZixuan Wang load_gdt_tss(tss_offset); 2703298643cSZixuan Wang } 2714143d8a7SZixuan Wang 2721ae9072eSZixuan Wang void setup_efi(efi_bootinfo_t *efi_bootinfo) 273ad5fb883SZixuan Wang { 274ad5fb883SZixuan Wang reset_apic(); 2753298643cSZixuan Wang setup_gdt_tss(); 2764143d8a7SZixuan Wang setup_idt(); 2774143d8a7SZixuan Wang load_idt(); 278ad5fb883SZixuan Wang mask_pic_interrupts(); 279ad5fb883SZixuan Wang enable_apic(); 280ad5fb883SZixuan Wang enable_x2apic(); 281ad5fb883SZixuan Wang smp_init(); 2821ae9072eSZixuan Wang phys_alloc_init(efi_bootinfo->free_mem_start, efi_bootinfo->free_mem_size); 283*f20589d6SZixuan Wang setup_efi_rsdp(efi_bootinfo->rsdp); 284ad5fb883SZixuan Wang } 285ad5fb883SZixuan Wang 286ad5fb883SZixuan Wang #endif /* TARGET_EFI */ 287ad5fb883SZixuan Wang 288716cea8aSPaolo Bonzini void setup_libcflat(void) 2893c7d322eSAndrew Jones { 2903c7d322eSAndrew Jones if (initrd) { 2913c7d322eSAndrew Jones /* environ is currently the only file in the initrd */ 2923c7d322eSAndrew Jones u32 size = MIN(initrd_size, ENV_SIZE); 29303b1e457SNadav Amit const char *str; 29403b1e457SNadav Amit 2953c7d322eSAndrew Jones memcpy(env, initrd, size); 2963c7d322eSAndrew Jones setup_env(env, size); 29703b1e457SNadav Amit if ((str = getenv("BOOTLOADER")) && atol(str) != 0) 29803b1e457SNadav Amit add_setup_arg("bootloader"); 2993c7d322eSAndrew Jones } 3003c7d322eSAndrew Jones } 301