aboutsummaryrefslogtreecommitdiff
path: root/pc-bios/optionrom/pvh_main.c
blob: 1dcc5c9256532424735f867634ef526d58e19e04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
 * PVH Option ROM for fw_cfg DMA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (c) 2019 Red Hat Inc.
 *   Authors:
 *     Stefano Garzarella <sgarzare@redhat.com>
 */

asm (".code32"); /* this code will be executed in protected mode */

#include <stddef.h>
#include <stdint.h>
#include "optrom.h"
#include "optrom_fw_cfg.h"
#include "../../include/hw/xen/start_info.h"

#define RSDP_SIGNATURE          0x2052545020445352LL /* "RSD PTR " */
#define RSDP_AREA_ADDR          0x000E0000
#define RSDP_AREA_SIZE          2048
#define EBDA_BASE_ADDR          0x0000040E
#define EBDA_SIZE               1024

#define E820_MAXENTRIES         128
#define CMDLINE_BUFSIZE         4096

/* e820 table filled in pvh.S using int 0x15 */
struct pvh_e820_table {
    uint32_t entries;
    uint32_t reserved;
    struct hvm_memmap_table_entry table[E820_MAXENTRIES];
};

struct pvh_e820_table pvh_e820 asm("pvh_e820") __attribute__ ((aligned));

static struct hvm_start_info start_info;
static uint8_t cmdline_buffer[CMDLINE_BUFSIZE];


/* Search RSDP signature. */
static uintptr_t search_rsdp(uint32_t start_addr, uint32_t end_addr)
{
    uint64_t *rsdp_p;

    /* RSDP signature is always on a 16 byte boundary */
    for (rsdp_p = (uint64_t *)start_addr; rsdp_p < (uint64_t *)end_addr;
         rsdp_p += 2) {
        if (*rsdp_p == RSDP_SIGNATURE) {
            return (uintptr_t)rsdp_p;
        }
    }

    return 0;
}

/* Force the asm name without leading underscore, even on Win32. */
extern void pvh_load_kernel(void) asm("pvh_load_kernel");

void pvh_load_kernel(void)
{
    void *cmdline_addr = &cmdline_buffer;
    void *kernel_entry;
    uint32_t cmdline_size, fw_cfg_version = bios_cfg_version();

    start_info.magic = XEN_HVM_START_MAGIC_VALUE;
    start_info.version = 1;

    /*
     * pvh_e820 is filled in the pvh.S before to switch in protected mode,
     * because we can use int 0x15 only in real mode.
     */
    start_info.memmap_entries = pvh_e820.entries;
    start_info.memmap_paddr = (uintptr_t)pvh_e820.table;

    /*
     * Search RSDP in the main BIOS area below 1 MB.
     * SeaBIOS store the RSDP in this area, so we try it first.
     */
    start_info.rsdp_paddr = search_rsdp(RSDP_AREA_ADDR,
                                        RSDP_AREA_ADDR + RSDP_AREA_SIZE);

    /* Search RSDP in the EBDA if it is not found */
    if (!start_info.rsdp_paddr) {
        /*
         * Th EBDA address is stored at EBDA_BASE_ADDR. It contains 2 bytes
         * segment pointer to EBDA, so we must convert it to a linear address.
         */
        uint32_t ebda_paddr = ((uint32_t)*((uint16_t *)EBDA_BASE_ADDR)) << 4;
        if (ebda_paddr > 0x400) {
            uint32_t *ebda = (uint32_t *)ebda_paddr;

            start_info.rsdp_paddr = search_rsdp(*ebda, *ebda + EBDA_SIZE);
        }
    }

    bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4, fw_cfg_version);
    bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size,
                        fw_cfg_version);
    start_info.cmdline_paddr = (uintptr_t)cmdline_addr;

    bios_cfg_read_entry(&kernel_entry, FW_CFG_KERNEL_ENTRY, 4, fw_cfg_version);

    asm volatile("jmp *%1" : : "b"(&start_info), "c"(kernel_entry));
}