1 /*
2 * Copyright (c) 2020 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stdint.h>
7 #include <stddef.h>
8 #include <string.h>
9 #include "efi.h"
10 #include "printf.h"
11 #include <zefi-segments.h>
12 #include <zephyr/arch/x86/efi.h>
13
14 #define PUTCHAR_BUFSZ 128
15
16 /* EFI GUID for RSDP
17 * See "Finding the RSDP on UEFI Enabled Systems" in ACPI specs.
18 */
19 #define ACPI_1_0_RSDP_EFI_GUID \
20 { \
21 .Data1 = 0xeb9d2d30, \
22 .Data2 = 0x2d88, \
23 .Data3 = 0x11d3, \
24 .Data4 = { 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d }, \
25 }
26
27 #define ACPI_2_0_RSDP_EFI_GUID \
28 { \
29 .Data1 = 0x8868e871, \
30 .Data2 = 0xe4f1, \
31 .Data3 = 0x11d3, \
32 .Data4 = { 0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81 }, \
33 }
34
35 /* The linker places this dummy last in the data memory. We can't use
36 * traditional linker address symbols because we're relocatable; the
37 * linker doesn't know what the runtime address will be. The compiler
38 * has to emit code to find this thing's address at runtime via an
39 * offset from RIP. It's a qword so we can guarantee alignment of the
40 * stuff after.
41 */
42 static __attribute__((section(".runtime_data_end")))
43 uint64_t runtime_data_end[1] = { 0x1111aa8888aa1111L };
44
45 #define EXT_DATA_START ((void *) &runtime_data_end[1])
46
47 static struct efi_system_table *efi;
48 static struct efi_boot_arg efi_arg;
49
efi_putchar(int c)50 static void efi_putchar(int c)
51 {
52 static uint16_t efibuf[PUTCHAR_BUFSZ + 1];
53 static int n;
54
55 if (c == '\n') {
56 efi_putchar('\r');
57 }
58
59 efibuf[n] = c;
60 ++n;
61
62 if (c == '\n' || n == PUTCHAR_BUFSZ) {
63 efibuf[n] = 0U;
64 efi->ConOut->OutputString(efi->ConOut, efibuf);
65 n = 0;
66 }
67 }
68
efi_guid_compare(efi_guid_t * s1,efi_guid_t * s2)69 static inline bool efi_guid_compare(efi_guid_t *s1, efi_guid_t *s2)
70 {
71 return ((s1->Part1 == s2->Part1) && (s1->Part2 == s2->Part2));
72 }
73
efi_config_get_vendor_table_by_guid(efi_guid_t * guid)74 static void *efi_config_get_vendor_table_by_guid(efi_guid_t *guid)
75 {
76 struct efi_configuration_table *ect_tmp;
77 int n_ct;
78
79 if (efi == NULL) {
80 return NULL;
81 }
82
83 ect_tmp = efi->ConfigurationTable;
84
85 for (n_ct = 0; n_ct < efi->NumberOfTableEntries; n_ct++) {
86 if (efi_guid_compare(&ect_tmp->VendorGuid, guid)) {
87 return ect_tmp->VendorTable;
88 }
89
90 ect_tmp++;
91 }
92
93 return NULL;
94 }
95
efi_prepare_boot_arg(void)96 static void efi_prepare_boot_arg(void)
97 {
98 efi_guid_t rsdp_guid_1 = ACPI_1_0_RSDP_EFI_GUID;
99 efi_guid_t rsdp_guid_2 = ACPI_2_0_RSDP_EFI_GUID;
100
101 /* Let's lookup for most recent ACPI table first */
102 efi_arg.acpi_rsdp = efi_config_get_vendor_table_by_guid(&rsdp_guid_2);
103 if (efi_arg.acpi_rsdp == NULL) {
104 efi_arg.acpi_rsdp =
105 efi_config_get_vendor_table_by_guid(&rsdp_guid_1);
106 }
107
108 if (efi_arg.acpi_rsdp != NULL) {
109 printf("RSDP found at %p\n", efi_arg.acpi_rsdp);
110 }
111 }
112
113 /* Existing x86_64 EFI environments have a bad habit of leaving the
114 * HPET timer running. This then fires later on, once the OS has
115 * started. If the timing isn't right, it can happen before the OS
116 * HPET driver gets a chance to disable it. And because we do the
117 * handoff (necessarily) with interrupts disabled, it's not actually
118 * possible for the OS to reliably disable it in time anyway.
119 *
120 * Basically: it's our job as the bootloader to ensure that no
121 * interrupt sources are live before entering the OS. Clear the
122 * interrupt enable bit of HPET timer zero.
123 */
disable_hpet(void)124 static void disable_hpet(void)
125 {
126 uint64_t *hpet = (uint64_t *)0xfed00000L;
127
128 hpet[32] &= ~4;
129 }
130
131 /* FIXME: if you check the generated code, "ms_abi" calls like this
132 * have to SPILL HALF OF THE SSE REGISTER SET TO THE STACK on entry
133 * because of the way the conventions collide. Is there a way to
134 * prevent/suppress that?
135 */
efi_entry(void * img_handle,struct efi_system_table * sys_tab)136 uintptr_t __abi efi_entry(void *img_handle, struct efi_system_table *sys_tab)
137 {
138 (void)img_handle;
139
140 efi = sys_tab;
141 z_putchar = efi_putchar;
142 printf("*** Zephyr EFI Loader ***\n");
143
144 efi_prepare_boot_arg();
145
146 for (int i = 0; i < sizeof(zefi_zsegs)/sizeof(zefi_zsegs[0]); i++) {
147 int bytes = zefi_zsegs[i].sz;
148 uint8_t *dst = (uint8_t *)zefi_zsegs[i].addr;
149
150 printf("Zeroing %d bytes of memory at %p\n", bytes, dst);
151 for (int j = 0; j < bytes; j++) {
152 dst[j] = 0U;
153 }
154 }
155
156 for (int i = 0; i < sizeof(zefi_dsegs)/sizeof(zefi_dsegs[0]); i++) {
157 int bytes = zefi_dsegs[i].sz;
158 int off = zefi_dsegs[i].off;
159 uint8_t *dst = (uint8_t *)zefi_dsegs[i].addr;
160 uint8_t *src = &((uint8_t *)EXT_DATA_START)[off];
161
162 printf("Copying %d data bytes to %p from image offset %d\n",
163 bytes, dst, zefi_dsegs[i].off);
164 for (int j = 0; j < bytes; j++) {
165 dst[j] = src[j];
166 }
167
168 /* Page-aligned blocks below 1M are the .locore
169 * section, which has a jump in its first bytes for
170 * the benefit of 32 bit entry. Those have to be
171 * written over with NOP instructions. (See comment
172 * about OUTRAGEOUS HACK in locore.S) before Zephyr
173 * starts, because the very first thing it does is
174 * install its own page table that disallows writes.
175 */
176 if (((long)dst & 0xfff) == 0 && dst < (uint8_t *)0x100000L) {
177 for (int i = 0; i < 8; i++) {
178 dst[i] = 0x90; /* 0x90 == 1-byte NOP */
179 }
180 }
181 }
182
183 unsigned char *code = (void *)zefi_entry;
184
185 efi_arg.efi_systab = efi;
186 __asm__ volatile("movq %%cr3, %0" : "=r"(efi_arg.efi_cr3));
187
188 printf("Jumping to Entry Point: %p (%x %x %x %x %x %x %x)\n",
189 code, code[0], code[1], code[2], code[3],
190 code[4], code[5], code[6]);
191
192 disable_hpet();
193
194 /* The EFI console seems to be buffered, give it a little time
195 * to drain before we start banging on the same UART from the
196 * OS.
197 */
198 for (volatile int i = 0; i < 50000000; i++) {
199 }
200
201 __asm__ volatile("cli; movq %0, %%rbx; jmp *%1"
202 :: "r"(&efi_arg), "r"(code) : "rbx");
203
204 return 0;
205 }
206
207 /* Trick cribbed shamelessly from gnu-efi. We need to emit a ".reloc"
208 * section into the image with a single dummy entry for the EFI loader
209 * to think we're a valid PE file, gcc won't because it thinks we're
210 * ELF.
211 */
212 uint32_t relocation_dummy;
213 __asm__(".section .reloc\n"
214 "base_relocation_block:\n"
215 ".long relocation_dummy - base_relocation_block\n"
216 ".long 0x0a\n"
217 ".word 0\n");
218