2 * PVH Option ROM for fw_cfg DMA
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 * Copyright (c) 2019 Red Hat Inc.
19 * Stefano Garzarella <sgarzare@redhat.com>
22 asm (".code32"); /* this code will be executed in protected mode */
27 #include "optrom_fw_cfg.h"
28 #include "../../include/hw/xen/start_info.h"
30 #define RSDP_SIGNATURE 0x2052545020445352LL /* "RSD PTR " */
31 #define RSDP_AREA_ADDR 0x000E0000
32 #define RSDP_AREA_SIZE 0x00020000
33 #define EBDA_BASE_ADDR 0x0000040E
34 #define EBDA_SIZE 1024
36 #define E820_MAXENTRIES 128
37 #define CMDLINE_BUFSIZE 4096
39 /* e820 table filled in pvh.S using int 0x15 */
40 struct pvh_e820_table
{
43 struct hvm_memmap_table_entry table
[E820_MAXENTRIES
];
46 struct pvh_e820_table pvh_e820
asm("pvh_e820") __attribute__ ((aligned
));
48 static struct hvm_start_info start_info
;
49 static struct hvm_modlist_entry ramdisk_mod
;
50 static uint8_t cmdline_buffer
[CMDLINE_BUFSIZE
];
53 /* Search RSDP signature. */
54 static uintptr_t search_rsdp(uint32_t start_addr
, uint32_t end_addr
)
58 /* RSDP signature is always on a 16 byte boundary */
59 for (rsdp_p
= (uint64_t *)start_addr
; rsdp_p
< (uint64_t *)end_addr
;
61 if (*rsdp_p
== RSDP_SIGNATURE
) {
62 return (uintptr_t)rsdp_p
;
69 /* Force the asm name without leading underscore, even on Win32. */
70 extern void pvh_load_kernel(void) asm("pvh_load_kernel");
72 void pvh_load_kernel(void)
74 void *cmdline_addr
= &cmdline_buffer
;
75 void *kernel_entry
, *initrd_addr
;
76 uint32_t cmdline_size
, initrd_size
, fw_cfg_version
= bios_cfg_version();
78 start_info
.magic
= XEN_HVM_START_MAGIC_VALUE
;
79 start_info
.version
= 1;
82 * pvh_e820 is filled in the pvh.S before to switch in protected mode,
83 * because we can use int 0x15 only in real mode.
85 start_info
.memmap_entries
= pvh_e820
.entries
;
86 start_info
.memmap_paddr
= (uintptr_t)pvh_e820
.table
;
89 * Search RSDP in the main BIOS area below 1 MB.
90 * SeaBIOS store the RSDP in this area, so we try it first.
92 start_info
.rsdp_paddr
= search_rsdp(RSDP_AREA_ADDR
,
93 RSDP_AREA_ADDR
+ RSDP_AREA_SIZE
);
95 /* Search RSDP in the EBDA if it is not found */
96 if (!start_info
.rsdp_paddr
) {
98 * Th EBDA address is stored at EBDA_BASE_ADDR. It contains 2 bytes
99 * segment pointer to EBDA, so we must convert it to a linear address.
101 uint32_t ebda_paddr
= ((uint32_t)*((uint16_t *)EBDA_BASE_ADDR
)) << 4;
102 if (ebda_paddr
> 0x400) {
103 uint32_t *ebda
= (uint32_t *)ebda_paddr
;
105 start_info
.rsdp_paddr
= search_rsdp(*ebda
, *ebda
+ EBDA_SIZE
);
109 bios_cfg_read_entry(&cmdline_size
, FW_CFG_CMDLINE_SIZE
, 4, fw_cfg_version
);
110 bios_cfg_read_entry(cmdline_addr
, FW_CFG_CMDLINE_DATA
, cmdline_size
,
112 start_info
.cmdline_paddr
= (uintptr_t)cmdline_addr
;
114 /* Check if we have the initrd to load */
115 bios_cfg_read_entry(&initrd_size
, FW_CFG_INITRD_SIZE
, 4, fw_cfg_version
);
117 bios_cfg_read_entry(&initrd_addr
, FW_CFG_INITRD_ADDR
, 4,
119 bios_cfg_read_entry(initrd_addr
, FW_CFG_INITRD_DATA
, initrd_size
,
122 ramdisk_mod
.paddr
= (uintptr_t)initrd_addr
;
123 ramdisk_mod
.size
= initrd_size
;
125 /* The first module is always ramdisk. */
126 start_info
.modlist_paddr
= (uintptr_t)&ramdisk_mod
;
127 start_info
.nr_modules
= 1;
130 bios_cfg_read_entry(&kernel_entry
, FW_CFG_KERNEL_ENTRY
, 4, fw_cfg_version
);
132 asm volatile("jmp *%1" : : "b"(&start_info
), "c"(kernel_entry
));