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