xref: /openbmc/qemu/pc-bios/optionrom/pvh_main.c (revision d649689a8ecb2e276cc20d3af6d416e3c299cb17)
12785dc7bSStefano Garzarella /*
22785dc7bSStefano Garzarella  * PVH Option ROM for fw_cfg DMA
32785dc7bSStefano Garzarella  *
42785dc7bSStefano Garzarella  * This program is free software; you can redistribute it and/or modify
52785dc7bSStefano Garzarella  * it under the terms of the GNU General Public License as published by
62785dc7bSStefano Garzarella  * the Free Software Foundation; either version 2 of the License, or
72785dc7bSStefano Garzarella  * (at your option) any later version.
82785dc7bSStefano Garzarella  *
92785dc7bSStefano Garzarella  * This program is distributed in the hope that it will be useful,
102785dc7bSStefano Garzarella  * but WITHOUT ANY WARRANTY; without even the implied warranty of
112785dc7bSStefano Garzarella  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
122785dc7bSStefano Garzarella  * GNU General Public License for more details.
132785dc7bSStefano Garzarella  *
142785dc7bSStefano Garzarella  * You should have received a copy of the GNU General Public License
152785dc7bSStefano Garzarella  * along with this program; if not, see <http://www.gnu.org/licenses/>.
162785dc7bSStefano Garzarella  *
172785dc7bSStefano Garzarella  * Copyright (c) 2019 Red Hat Inc.
182785dc7bSStefano Garzarella  *   Authors:
192785dc7bSStefano Garzarella  *     Stefano Garzarella <sgarzare@redhat.com>
202785dc7bSStefano Garzarella  */
212785dc7bSStefano Garzarella 
222785dc7bSStefano Garzarella asm (".code32"); /* this code will be executed in protected mode */
232785dc7bSStefano Garzarella 
242785dc7bSStefano Garzarella #include <stddef.h>
252785dc7bSStefano Garzarella #include <stdint.h>
262785dc7bSStefano Garzarella #include "optrom.h"
272785dc7bSStefano Garzarella #include "optrom_fw_cfg.h"
282785dc7bSStefano Garzarella #include "../../include/hw/xen/start_info.h"
292785dc7bSStefano Garzarella 
302785dc7bSStefano Garzarella #define RSDP_SIGNATURE          0x2052545020445352LL /* "RSD PTR " */
312785dc7bSStefano Garzarella #define RSDP_AREA_ADDR          0x000E0000
32*770275edSJoe Richey #define RSDP_AREA_SIZE          0x00020000
332785dc7bSStefano Garzarella #define EBDA_BASE_ADDR          0x0000040E
342785dc7bSStefano Garzarella #define EBDA_SIZE               1024
352785dc7bSStefano Garzarella 
362785dc7bSStefano Garzarella #define E820_MAXENTRIES         128
372785dc7bSStefano Garzarella #define CMDLINE_BUFSIZE         4096
382785dc7bSStefano Garzarella 
392785dc7bSStefano Garzarella /* e820 table filled in pvh.S using int 0x15 */
402785dc7bSStefano Garzarella struct pvh_e820_table {
412785dc7bSStefano Garzarella     uint32_t entries;
422785dc7bSStefano Garzarella     uint32_t reserved;
432785dc7bSStefano Garzarella     struct hvm_memmap_table_entry table[E820_MAXENTRIES];
442785dc7bSStefano Garzarella };
452785dc7bSStefano Garzarella 
462785dc7bSStefano Garzarella struct pvh_e820_table pvh_e820 asm("pvh_e820") __attribute__ ((aligned));
472785dc7bSStefano Garzarella 
482785dc7bSStefano Garzarella static struct hvm_start_info start_info;
49b1b876caSStefano Garzarella static struct hvm_modlist_entry ramdisk_mod;
502785dc7bSStefano Garzarella static uint8_t cmdline_buffer[CMDLINE_BUFSIZE];
512785dc7bSStefano Garzarella 
522785dc7bSStefano Garzarella 
532785dc7bSStefano Garzarella /* Search RSDP signature. */
search_rsdp(uint32_t start_addr,uint32_t end_addr)542785dc7bSStefano Garzarella static uintptr_t search_rsdp(uint32_t start_addr, uint32_t end_addr)
552785dc7bSStefano Garzarella {
562785dc7bSStefano Garzarella     uint64_t *rsdp_p;
572785dc7bSStefano Garzarella 
582785dc7bSStefano Garzarella     /* RSDP signature is always on a 16 byte boundary */
592785dc7bSStefano Garzarella     for (rsdp_p = (uint64_t *)start_addr; rsdp_p < (uint64_t *)end_addr;
602785dc7bSStefano Garzarella          rsdp_p += 2) {
612785dc7bSStefano Garzarella         if (*rsdp_p == RSDP_SIGNATURE) {
622785dc7bSStefano Garzarella             return (uintptr_t)rsdp_p;
632785dc7bSStefano Garzarella         }
642785dc7bSStefano Garzarella     }
652785dc7bSStefano Garzarella 
662785dc7bSStefano Garzarella     return 0;
672785dc7bSStefano Garzarella }
682785dc7bSStefano Garzarella 
692785dc7bSStefano Garzarella /* Force the asm name without leading underscore, even on Win32. */
702785dc7bSStefano Garzarella extern void pvh_load_kernel(void) asm("pvh_load_kernel");
712785dc7bSStefano Garzarella 
pvh_load_kernel(void)722785dc7bSStefano Garzarella void pvh_load_kernel(void)
732785dc7bSStefano Garzarella {
742785dc7bSStefano Garzarella     void *cmdline_addr = &cmdline_buffer;
75b1b876caSStefano Garzarella     void *kernel_entry, *initrd_addr;
76b1b876caSStefano Garzarella     uint32_t cmdline_size, initrd_size, fw_cfg_version = bios_cfg_version();
772785dc7bSStefano Garzarella 
782785dc7bSStefano Garzarella     start_info.magic = XEN_HVM_START_MAGIC_VALUE;
792785dc7bSStefano Garzarella     start_info.version = 1;
802785dc7bSStefano Garzarella 
812785dc7bSStefano Garzarella     /*
822785dc7bSStefano Garzarella      * pvh_e820 is filled in the pvh.S before to switch in protected mode,
832785dc7bSStefano Garzarella      * because we can use int 0x15 only in real mode.
842785dc7bSStefano Garzarella      */
852785dc7bSStefano Garzarella     start_info.memmap_entries = pvh_e820.entries;
862785dc7bSStefano Garzarella     start_info.memmap_paddr = (uintptr_t)pvh_e820.table;
872785dc7bSStefano Garzarella 
882785dc7bSStefano Garzarella     /*
892785dc7bSStefano Garzarella      * Search RSDP in the main BIOS area below 1 MB.
902785dc7bSStefano Garzarella      * SeaBIOS store the RSDP in this area, so we try it first.
912785dc7bSStefano Garzarella      */
922785dc7bSStefano Garzarella     start_info.rsdp_paddr = search_rsdp(RSDP_AREA_ADDR,
932785dc7bSStefano Garzarella                                         RSDP_AREA_ADDR + RSDP_AREA_SIZE);
942785dc7bSStefano Garzarella 
952785dc7bSStefano Garzarella     /* Search RSDP in the EBDA if it is not found */
962785dc7bSStefano Garzarella     if (!start_info.rsdp_paddr) {
972785dc7bSStefano Garzarella         /*
982785dc7bSStefano Garzarella          * Th EBDA address is stored at EBDA_BASE_ADDR. It contains 2 bytes
992785dc7bSStefano Garzarella          * segment pointer to EBDA, so we must convert it to a linear address.
1002785dc7bSStefano Garzarella          */
1012785dc7bSStefano Garzarella         uint32_t ebda_paddr = ((uint32_t)*((uint16_t *)EBDA_BASE_ADDR)) << 4;
1022785dc7bSStefano Garzarella         if (ebda_paddr > 0x400) {
1032785dc7bSStefano Garzarella             uint32_t *ebda = (uint32_t *)ebda_paddr;
1042785dc7bSStefano Garzarella 
1052785dc7bSStefano Garzarella             start_info.rsdp_paddr = search_rsdp(*ebda, *ebda + EBDA_SIZE);
1062785dc7bSStefano Garzarella         }
1072785dc7bSStefano Garzarella     }
1082785dc7bSStefano Garzarella 
1092785dc7bSStefano Garzarella     bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4, fw_cfg_version);
1102785dc7bSStefano Garzarella     bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size,
1112785dc7bSStefano Garzarella                         fw_cfg_version);
1122785dc7bSStefano Garzarella     start_info.cmdline_paddr = (uintptr_t)cmdline_addr;
1132785dc7bSStefano Garzarella 
114b1b876caSStefano Garzarella     /* Check if we have the initrd to load */
115b1b876caSStefano Garzarella     bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4, fw_cfg_version);
116b1b876caSStefano Garzarella     if (initrd_size) {
117b1b876caSStefano Garzarella         bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4,
118b1b876caSStefano Garzarella                             fw_cfg_version);
119b1b876caSStefano Garzarella         bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size,
120b1b876caSStefano Garzarella                             fw_cfg_version);
121b1b876caSStefano Garzarella 
122b1b876caSStefano Garzarella         ramdisk_mod.paddr = (uintptr_t)initrd_addr;
123b1b876caSStefano Garzarella         ramdisk_mod.size = initrd_size;
124b1b876caSStefano Garzarella 
125b1b876caSStefano Garzarella         /* The first module is always ramdisk. */
126b1b876caSStefano Garzarella         start_info.modlist_paddr = (uintptr_t)&ramdisk_mod;
127b1b876caSStefano Garzarella         start_info.nr_modules = 1;
128b1b876caSStefano Garzarella     }
129b1b876caSStefano Garzarella 
1302785dc7bSStefano Garzarella     bios_cfg_read_entry(&kernel_entry, FW_CFG_KERNEL_ENTRY, 4, fw_cfg_version);
1312785dc7bSStefano Garzarella 
1322785dc7bSStefano Garzarella     asm volatile("jmp *%1" : : "b"(&start_info), "c"(kernel_entry));
1332785dc7bSStefano Garzarella }
134