1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/spinlock.h>
8 #include <zephyr/arch/x86/efi.h>
9 #include <zephyr/kernel/mm.h>
10 #include "../zefi/efi.h" /* ZEFI not on include path */
11 #include <zephyr/kernel.h>
12 #include <kernel_arch_func.h>
13 
14 #define EFI_CON_BUFSZ 128
15 
16 /* Big stack for the EFI code to use */
17 static uint64_t __aligned(64) efi_stack[1024];
18 
19 struct efi_boot_arg *efi;
20 
21 #ifdef CONFIG_DYNAMIC_BOOTARGS
22 __pinned_noinit char efi_bootargs[CONFIG_BOOTARGS_ARGS_BUFFER_SIZE];
23 #endif
24 
efi_get_acpi_rsdp(void)25 void *efi_get_acpi_rsdp(void)
26 {
27 	if (efi == NULL) {
28 		return NULL;
29 	}
30 
31 	return efi->acpi_rsdp;
32 }
33 
efi_init(struct efi_boot_arg * efi_arg)34 void efi_init(struct efi_boot_arg *efi_arg)
35 {
36 	if (efi_arg == NULL) {
37 		return;
38 	}
39 
40 	k_mem_map_phys_bare((uint8_t **)&efi, (uintptr_t)efi_arg,
41 			    sizeof(struct efi_boot_arg), 0);
42 }
43 
44 /* EFI thunk.  Not a lot of code, but lots of context:
45  *
46  * We need to swap in the original EFI page tables for this to work,
47  * as Zephyr has only mapped memory it uses and IO it knows about.  In
48  * theory we might need to restore more state too (maybe the EFI code
49  * uses special segment descriptors from its own GDT, maybe it relies
50  * on interrupts in its own IDT, maybe it twiddles custom MSRs or
51  * plays with the IO-MMU... the posibilities are endless).  But
52  * experimentally, only the memory state seems to be required on known
53  * hardware.  This is safe because in the existing architecture Zephyr
54  * has already initialized all its own memory and left the rest of the
55  * system as-is; we already know it doesn't overlap with the EFI
56  * environment (because we've always just assumed that's the case,
57  * heh).
58  *
59  * Similarly we need to swap the stack: EFI firmware was written in an
60  * environment where it would be running on multi-gigabyte systems and
61  * likes to overflow the tiny stacks Zephyr code uses.  (There is also
62  * the problem of the red zone -- SysV reserves 128 bytes of
63  * unpreserved data "under" the stack pointer for the use of the
64  * current function.  Our compiler would be free to write things there
65  * that might be clobbered by the EFI call, which doesn't understand
66  * that rule.  Inspection of generated code shows that we're safe, but
67  * still, best to swap stacks explicitly.)
68  *
69  * And the calling conventions are different: the EFI function uses
70  * Microsoft's ABI, not SysV.  Parameters go in RCX/RDX/R8/R9 (though
71  * we only pass two here), and return value is in RAX (which we
72  * multiplex as an input to hold the function pointer).  R10 and R11
73  * are also caller-save.  Technically X/YMM0-5 are caller-save too,
74  * but as long as this (SysV) function was called per its own ABI they
75  * have already been saved by our own caller.  Also note that there is
76  * a 32 byte region ABOVE the return value that must be allocated by
77  * the caller as spill space for the 4 register-passed arguments (this
78  * ABI is so weird...).  We also need two call-preserved scratch
79  * registers (for preserving the stack pointer and page table), those
80  * are R12/R13.
81  *
82  * Finally: note that the firmware on at least one board (an Up
83  * Squared APL device) will internally ENABLE INTERRUPTS before
84  * returing from its OutputString method.  This is... unfortunate, and
85  * says poor things about reliability using this code as it will
86  * implicitly break the spinlock we're using.  The OS will be able to
87  * take an interrupt just fine, but if the resulting ISR tries to log,
88  * we'll end up in EFI firmware reentrantly!  The best we can do is an
89  * unconditional CLI immediately after returning.
90  */
efi_call(void * fn,uint64_t arg1,uint64_t arg2)91 static uint64_t efi_call(void *fn, uint64_t arg1, uint64_t arg2)
92 {
93 	void *stack_top = &efi_stack[ARRAY_SIZE(efi_stack) - 4];
94 
95 	/* During the efi_call window the interrupt is enabled, that
96 	 * means an interrupt could happen and trigger scheduler at
97 	 * end of the interrupt. Try to prevent swap happening.
98 	 */
99 	k_sched_lock();
100 
101 	__asm__ volatile("movq %%cr3, %%r12;" /* save zephyr page table */
102 			 "movq %%rsp, %%r13;" /* save stack pointer */
103 			 "movq %%rsi, %%rsp;" /* set stack */
104 			 "movq %%rdi, %%cr3;" /* set EFI page table */
105 			 "callq *%%rax;"
106 			 "cli;"
107 			 "movq %%r12, %%cr3;" /* reset paging */
108 			 "movq %%r13, %%rsp;" /* reset stack */
109 			 : "+a"(fn)
110 			 : "c"(arg1), "d"(arg2), "S"(stack_top), "D"(efi->efi_cr3)
111 			 : "r8", "r9", "r10", "r11", "r12", "r13");
112 
113 	k_sched_unlock();
114 	return (uint64_t) fn;
115 }
116 
efi_console_putchar(int c)117 int efi_console_putchar(int c)
118 {
119 	static struct k_spinlock lock;
120 	static uint16_t efibuf[EFI_CON_BUFSZ + 1];
121 	static int n;
122 	static void *conout;
123 	static void *output_string_fn;
124 	struct efi_system_table *efist = efi->efi_systab;
125 
126 	/* Limit the printk call in interrupt context for
127 	 * EFI cosnsole. This is a workaround that prevents
128 	 * the printk call re-entries when an interrupt
129 	 * happens during the EFI call window.
130 	 */
131 	if (arch_is_in_isr()) {
132 		return 0;
133 	}
134 
135 	if (c == '\n') {
136 		efi_console_putchar('\r');
137 	}
138 
139 	k_spinlock_key_t key = k_spin_lock(&lock);
140 
141 	/* These structs live in EFI memory and aren't mapped by
142 	 * Zephyr.  Extract the needed pointers by swapping page
143 	 * tables.  Do it via lazy evaluation because this code is
144 	 * routinely needed much earlier than any feasible init hook.
145 	 */
146 	if (conout == NULL) {
147 		uint64_t cr3;
148 
149 		__asm__ volatile("movq %%cr3, %0" : "=r"(cr3));
150 		__asm__ volatile("movq %0, %%cr3" :: "r"(efi->efi_cr3));
151 		conout = efist->ConOut;
152 		output_string_fn = efist->ConOut->OutputString;
153 		__asm__ volatile("movq %0, %%cr3" :: "r"(cr3));
154 	}
155 
156 	/* Buffer, to reduce trips through the thunking layer.
157 	 * Flushes when full and at newlines.
158 	 */
159 	efibuf[n++] = c;
160 	if (c == '\n' || n == EFI_CON_BUFSZ) {
161 		efibuf[n] = 0U;
162 		(void)efi_call(output_string_fn, (uint64_t)conout, (uint64_t)efibuf);
163 		n = 0;
164 	}
165 
166 	k_spin_unlock(&lock, key);
167 	return 0;
168 }
169 
170 #ifdef CONFIG_X86_EFI_CONSOLE
arch_printk_char_out(int c)171 int arch_printk_char_out(int c)
172 {
173 	return efi_console_putchar(c);
174 }
175 #endif
176 
177 #ifdef CONFIG_DYNAMIC_BOOTARGS
get_bootargs(void)178 const char *get_bootargs(void)
179 {
180 	return efi_bootargs;
181 }
182 #endif /* CONFIG_DYNAMIC_BOOTARGS */
183