]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/amd64/efirt_machdep.c
MFV r329552: less v530.
[FreeBSD/FreeBSD.git] / sys / amd64 / amd64 / efirt_machdep.c
1 /*-
2  * Copyright (c) 2004 Marcel Moolenaar
3  * Copyright (c) 2001 Doug Rabson
4  * Copyright (c) 2016 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * Portions of this software were developed by Konstantin Belousov
8  * under sponsorship from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/efi.h>
37 #include <sys/kernel.h>
38 #include <sys/linker.h>
39 #include <sys/lock.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/clock.h>
43 #include <sys/proc.h>
44 #include <sys/rwlock.h>
45 #include <sys/sched.h>
46 #include <sys/sysctl.h>
47 #include <sys/systm.h>
48 #include <sys/vmmeter.h>
49 #include <isa/rtc.h>
50 #include <machine/fpu.h>
51 #include <machine/efi.h>
52 #include <machine/metadata.h>
53 #include <machine/md_var.h>
54 #include <machine/smp.h>
55 #include <machine/vmparam.h>
56 #include <vm/vm.h>
57 #include <vm/pmap.h>
58 #include <vm/vm_map.h>
59 #include <vm/vm_object.h>
60 #include <vm/vm_page.h>
61 #include <vm/vm_pager.h>
62
63 static pml4_entry_t *efi_pml4;
64 static vm_object_t obj_1t1_pt;
65 static vm_page_t efi_pml4_page;
66 static vm_pindex_t efi_1t1_idx;
67
68 void
69 efi_destroy_1t1_map(void)
70 {
71         vm_page_t m;
72
73         if (obj_1t1_pt != NULL) {
74                 VM_OBJECT_RLOCK(obj_1t1_pt);
75                 TAILQ_FOREACH(m, &obj_1t1_pt->memq, listq)
76                         m->wire_count = 0;
77                 vm_wire_sub(obj_1t1_pt->resident_page_count);
78                 VM_OBJECT_RUNLOCK(obj_1t1_pt);
79                 vm_object_deallocate(obj_1t1_pt);
80         }
81
82         obj_1t1_pt = NULL;
83         efi_pml4 = NULL;
84         efi_pml4_page = NULL;
85 }
86
87 static vm_page_t
88 efi_1t1_page(void)
89 {
90
91         return (vm_page_grab(obj_1t1_pt, efi_1t1_idx++, VM_ALLOC_NOBUSY |
92             VM_ALLOC_WIRED | VM_ALLOC_ZERO));
93 }
94
95 static pt_entry_t *
96 efi_1t1_pte(vm_offset_t va)
97 {
98         pml4_entry_t *pml4e;
99         pdp_entry_t *pdpe;
100         pd_entry_t *pde;
101         pt_entry_t *pte;
102         vm_page_t m;
103         vm_pindex_t pml4_idx, pdp_idx, pd_idx;
104         vm_paddr_t mphys;
105
106         pml4_idx = pmap_pml4e_index(va);
107         pml4e = &efi_pml4[pml4_idx];
108         if (*pml4e == 0) {
109                 m = efi_1t1_page();
110                 mphys =  VM_PAGE_TO_PHYS(m);
111                 *pml4e = mphys | X86_PG_RW | X86_PG_V;
112         } else {
113                 mphys = *pml4e & ~PAGE_MASK;
114         }
115
116         pdpe = (pdp_entry_t *)PHYS_TO_DMAP(mphys);
117         pdp_idx = pmap_pdpe_index(va);
118         pdpe += pdp_idx;
119         if (*pdpe == 0) {
120                 m = efi_1t1_page();
121                 mphys =  VM_PAGE_TO_PHYS(m);
122                 *pdpe = mphys | X86_PG_RW | X86_PG_V;
123         } else {
124                 mphys = *pdpe & ~PAGE_MASK;
125         }
126
127         pde = (pd_entry_t *)PHYS_TO_DMAP(mphys);
128         pd_idx = pmap_pde_index(va);
129         pde += pd_idx;
130         if (*pde == 0) {
131                 m = efi_1t1_page();
132                 mphys = VM_PAGE_TO_PHYS(m);
133                 *pde = mphys | X86_PG_RW | X86_PG_V;
134         } else {
135                 mphys = *pde & ~PAGE_MASK;
136         }
137
138         pte = (pt_entry_t *)PHYS_TO_DMAP(mphys);
139         pte += pmap_pte_index(va);
140         KASSERT(*pte == 0, ("va %#jx *pt %#jx", va, *pte));
141
142         return (pte);
143 }
144
145 bool
146 efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
147 {
148         struct efi_md *p;
149         pt_entry_t *pte;
150         vm_offset_t va;
151         uint64_t idx;
152         int bits, i, mode;
153
154         obj_1t1_pt = vm_pager_allocate(OBJT_PHYS, NULL, ptoa(1 +
155             NPML4EPG + NPML4EPG * NPDPEPG + NPML4EPG * NPDPEPG * NPDEPG),
156             VM_PROT_ALL, 0, NULL);
157         efi_1t1_idx = 0;
158         VM_OBJECT_WLOCK(obj_1t1_pt);
159         efi_pml4_page = efi_1t1_page();
160         VM_OBJECT_WUNLOCK(obj_1t1_pt);
161         efi_pml4 = (pml4_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(efi_pml4_page));
162         pmap_pinit_pml4(efi_pml4_page);
163
164         for (i = 0, p = map; i < ndesc; i++, p = efi_next_descriptor(p,
165             descsz)) {
166                 if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
167                         continue;
168                 if (p->md_virt != NULL) {
169                         if (bootverbose)
170                                 printf("EFI Runtime entry %d is mapped\n", i);
171                         goto fail;
172                 }
173                 if ((p->md_phys & EFI_PAGE_MASK) != 0) {
174                         if (bootverbose)
175                                 printf("EFI Runtime entry %d is not aligned\n",
176                                     i);
177                         goto fail;
178                 }
179                 if (p->md_phys + p->md_pages * EFI_PAGE_SIZE < p->md_phys ||
180                     p->md_phys + p->md_pages * EFI_PAGE_SIZE >=
181                     VM_MAXUSER_ADDRESS) {
182                         printf("EFI Runtime entry %d is not in mappable for RT:"
183                             "base %#016jx %#jx pages\n",
184                             i, (uintmax_t)p->md_phys,
185                             (uintmax_t)p->md_pages);
186                         goto fail;
187                 }
188                 if ((p->md_attr & EFI_MD_ATTR_WB) != 0)
189                         mode = VM_MEMATTR_WRITE_BACK;
190                 else if ((p->md_attr & EFI_MD_ATTR_WT) != 0)
191                         mode = VM_MEMATTR_WRITE_THROUGH;
192                 else if ((p->md_attr & EFI_MD_ATTR_WC) != 0)
193                         mode = VM_MEMATTR_WRITE_COMBINING;
194                 else if ((p->md_attr & EFI_MD_ATTR_WP) != 0)
195                         mode = VM_MEMATTR_WRITE_PROTECTED;
196                 else if ((p->md_attr & EFI_MD_ATTR_UC) != 0)
197                         mode = VM_MEMATTR_UNCACHEABLE;
198                 else {
199                         if (bootverbose)
200                                 printf("EFI Runtime entry %d mapping "
201                                     "attributes unsupported\n", i);
202                         mode = VM_MEMATTR_UNCACHEABLE;
203                 }
204                 bits = pmap_cache_bits(kernel_pmap, mode, FALSE) | X86_PG_RW |
205                     X86_PG_V;
206                 VM_OBJECT_WLOCK(obj_1t1_pt);
207                 for (va = p->md_phys, idx = 0; idx < p->md_pages; idx++,
208                     va += PAGE_SIZE) {
209                         pte = efi_1t1_pte(va);
210                         pte_store(pte, va | bits);
211                 }
212                 VM_OBJECT_WUNLOCK(obj_1t1_pt);
213         }
214
215         return (true);
216
217 fail:
218         efi_destroy_1t1_map();
219         return (false);
220 }
221
222 /*
223  * Create an environment for the EFI runtime code call.  The most
224  * important part is creating the required 1:1 physical->virtual
225  * mappings for the runtime segments.  To do that, we manually create
226  * page table which unmap userspace but gives correct kernel mapping.
227  * The 1:1 mappings for runtime segments usually occupy low 4G of the
228  * physical address map.
229  *
230  * The 1:1 mappings were chosen over the SetVirtualAddressMap() EFI RT
231  * service, because there are some BIOSes which fail to correctly
232  * relocate itself on the call, requiring both 1:1 and virtual
233  * mapping.  As result, we must provide 1:1 mapping anyway, so no
234  * reason to bother with the virtual map, and no need to add a
235  * complexity into loader.
236  *
237  * The fpu_kern_enter() call allows firmware to use FPU, as mandated
238  * by the specification.  In particular, CR0.TS bit is cleared.  Also
239  * it enters critical section, giving us neccessary protection against
240  * context switch.
241  *
242  * There is no need to disable interrupts around the change of %cr3,
243  * the kernel mappings are correct, while we only grabbed the
244  * userspace portion of VA.  Interrupts handlers must not access
245  * userspace.  Having interrupts enabled fixes the issue with
246  * firmware/SMM long operation, which would negatively affect IPIs,
247  * esp. TLB shootdown requests.
248  */
249 int
250 efi_arch_enter(void)
251 {
252         pmap_t curpmap;
253
254         curpmap = PCPU_GET(curpmap);
255         PMAP_LOCK_ASSERT(curpmap, MA_OWNED);
256
257         /*
258          * IPI TLB shootdown handler invltlb_pcid_handler() reloads
259          * %cr3 from the curpmap->pm_cr3, which would disable runtime
260          * segments mappings.  Block the handler's action by setting
261          * curpmap to impossible value.  See also comment in
262          * pmap.c:pmap_activate_sw().
263          */
264         if (pmap_pcid_enabled && !invpcid_works)
265                 PCPU_SET(curpmap, NULL);
266
267         load_cr3(VM_PAGE_TO_PHYS(efi_pml4_page) | (pmap_pcid_enabled ?
268             curpmap->pm_pcids[PCPU_GET(cpuid)].pm_pcid : 0));
269         /*
270          * If PCID is enabled, the clear CR3_PCID_SAVE bit in the loaded %cr3
271          * causes TLB invalidation.
272          */
273         if (!pmap_pcid_enabled)
274                 invltlb();
275         return (0);
276 }
277
278 void
279 efi_arch_leave(void)
280 {
281         pmap_t curpmap;
282
283         curpmap = &curproc->p_vmspace->vm_pmap;
284         if (pmap_pcid_enabled && !invpcid_works)
285                 PCPU_SET(curpmap, curpmap);
286         load_cr3(curpmap->pm_cr3 | (pmap_pcid_enabled ?
287             curpmap->pm_pcids[PCPU_GET(cpuid)].pm_pcid : 0));
288         if (!pmap_pcid_enabled)
289                 invltlb();
290 }
291
292 /* XXX debug stuff */
293 static int
294 efi_time_sysctl_handler(SYSCTL_HANDLER_ARGS)
295 {
296         struct efi_tm tm;
297         int error, val;
298
299         val = 0;
300         error = sysctl_handle_int(oidp, &val, 0, req);
301         if (error != 0 || req->newptr == NULL)
302                 return (error);
303         error = efi_get_time(&tm);
304         if (error == 0) {
305                 uprintf("EFI reports: Year %d Month %d Day %d Hour %d Min %d "
306                     "Sec %d\n", tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
307                     tm.tm_min, tm.tm_sec);
308         }
309         return (error);
310 }
311
312 SYSCTL_PROC(_debug, OID_AUTO, efi_time, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
313     efi_time_sysctl_handler, "I", "");