]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/efirt_machdep.c
arm64/cmn600: Grammar fix
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / efirt_machdep.c
1 /*-
2  * Copyright (c) 2004 Marcel Moolenaar
3  * Copyright (c) 2001 Doug Rabson
4  * Copyright (c) 2016 The FreeBSD Foundation
5  * Copyright (c) 2017 Andrew Turner
6  * All rights reserved.
7  *
8  * Portions of this software were developed by Konstantin Belousov
9  * under sponsorship from the FreeBSD Foundation.
10  *
11  * This software was developed by SRI International and the University of
12  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
13  * ("CTSRD"), as part of the DARPA CRASH research programme.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include <sys/param.h>
38 #include <sys/efi.h>
39 #include <sys/kernel.h>
40 #include <sys/linker.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43 #include <sys/proc.h>
44 #include <sys/rwlock.h>
45 #include <sys/systm.h>
46 #include <sys/vmmeter.h>
47
48 #include <machine/pte.h>
49 #include <machine/vmparam.h>
50
51 #include <vm/vm.h>
52 #include <vm/pmap.h>
53 #include <vm/vm_map.h>
54 #include <vm/vm_object.h>
55 #include <vm/vm_page.h>
56 #include <vm/vm_pager.h>
57
58 static vm_object_t obj_1t1_pt;
59 static vm_pindex_t efi_1t1_idx;
60 static pd_entry_t *efi_l0;
61 static uint64_t efi_ttbr0;
62
63 void
64 efi_destroy_1t1_map(void)
65 {
66         vm_page_t m;
67
68         if (obj_1t1_pt != NULL) {
69                 VM_OBJECT_RLOCK(obj_1t1_pt);
70                 TAILQ_FOREACH(m, &obj_1t1_pt->memq, listq)
71                         m->ref_count = VPRC_OBJREF;
72                 vm_wire_sub(obj_1t1_pt->resident_page_count);
73                 VM_OBJECT_RUNLOCK(obj_1t1_pt);
74                 vm_object_deallocate(obj_1t1_pt);
75         }
76
77         obj_1t1_pt = NULL;
78         efi_1t1_idx = 0;
79         efi_l0 = NULL;
80         efi_ttbr0 = 0;
81 }
82
83 static vm_page_t
84 efi_1t1_page(void)
85 {
86
87         return (vm_page_grab(obj_1t1_pt, efi_1t1_idx++, VM_ALLOC_NOBUSY |
88             VM_ALLOC_WIRED | VM_ALLOC_ZERO));
89 }
90
91 static pt_entry_t *
92 efi_1t1_l3(vm_offset_t va)
93 {
94         pd_entry_t *l0, *l1, *l2;
95         pt_entry_t *l3;
96         vm_pindex_t l0_idx, l1_idx, l2_idx;
97         vm_page_t m;
98         vm_paddr_t mphys;
99
100         l0_idx = pmap_l0_index(va);
101         l0 = &efi_l0[l0_idx];
102         if (*l0 == 0) {
103                 m = efi_1t1_page();
104                 mphys = VM_PAGE_TO_PHYS(m);
105                 *l0 = PHYS_TO_PTE(mphys) | L0_TABLE;
106         } else {
107                 mphys = PTE_TO_PHYS(*l0);
108         }
109
110         l1 = (pd_entry_t *)PHYS_TO_DMAP(mphys);
111         l1_idx = pmap_l1_index(va);
112         l1 += l1_idx;
113         if (*l1 == 0) {
114                 m = efi_1t1_page();
115                 mphys = VM_PAGE_TO_PHYS(m);
116                 *l1 = PHYS_TO_PTE(mphys) | L1_TABLE;
117         } else {
118                 mphys = PTE_TO_PHYS(*l1);
119         }
120
121         l2 = (pd_entry_t *)PHYS_TO_DMAP(mphys);
122         l2_idx = pmap_l2_index(va);
123         l2 += l2_idx;
124         if (*l2 == 0) {
125                 m = efi_1t1_page();
126                 mphys = VM_PAGE_TO_PHYS(m);
127                 *l2 = PHYS_TO_PTE(mphys) | L2_TABLE;
128         } else {
129                 mphys = PTE_TO_PHYS(*l2);
130         }
131
132         l3 = (pt_entry_t *)PHYS_TO_DMAP(mphys);
133         l3 += pmap_l3_index(va);
134         KASSERT(*l3 == 0, ("%s: Already mapped: va %#jx *pt %#jx", __func__,
135             va, *l3));
136
137         return (l3);
138 }
139
140 /*
141  * Map a physical address from EFI runtime space into KVA space.  Returns 0 to
142  * indicate a failed mapping so that the caller may handle error.
143  */
144 vm_offset_t
145 efi_phys_to_kva(vm_paddr_t paddr)
146 {
147         vm_offset_t vaddr;
148
149         if (PHYS_IN_DMAP(paddr)) {
150                 vaddr = PHYS_TO_DMAP(paddr);
151                 if (pmap_klookup(vaddr, NULL))
152                         return (vaddr);
153         }
154
155         /* TODO: Map memory not in the DMAP */
156
157         return (0);
158 }
159
160 /*
161  * Create the 1:1 virtual to physical map for EFI
162  */
163 bool
164 efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
165 {
166         struct efi_md *p;
167         pt_entry_t *l3, l3_attr;
168         vm_offset_t va;
169         vm_page_t efi_l0_page;
170         uint64_t idx;
171         int i, mode;
172
173         obj_1t1_pt = vm_pager_allocate(OBJT_PHYS, NULL, L0_ENTRIES +
174             L0_ENTRIES * Ln_ENTRIES + L0_ENTRIES * Ln_ENTRIES * Ln_ENTRIES +
175             L0_ENTRIES * Ln_ENTRIES * Ln_ENTRIES * Ln_ENTRIES,
176             VM_PROT_ALL, 0, NULL);
177         VM_OBJECT_WLOCK(obj_1t1_pt);
178         efi_l0_page = efi_1t1_page();
179         VM_OBJECT_WUNLOCK(obj_1t1_pt);
180         efi_l0 = (pd_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(efi_l0_page));
181         efi_ttbr0 = ASID_TO_OPERAND(ASID_RESERVED_FOR_EFI) |
182             VM_PAGE_TO_PHYS(efi_l0_page);
183
184         for (i = 0, p = map; i < ndesc; i++, p = efi_next_descriptor(p,
185             descsz)) {
186                 if ((p->md_attr & EFI_MD_ATTR_RT) == 0)
187                         continue;
188                 if (p->md_virt != 0 && p->md_virt != p->md_phys) {
189                         if (bootverbose)
190                                 printf("EFI Runtime entry %d is mapped\n", i);
191                         goto fail;
192                 }
193                 if ((p->md_phys & EFI_PAGE_MASK) != 0) {
194                         if (bootverbose)
195                                 printf("EFI Runtime entry %d is not aligned\n",
196                                     i);
197                         goto fail;
198                 }
199                 if (p->md_phys + p->md_pages * EFI_PAGE_SIZE < p->md_phys ||
200                     p->md_phys + p->md_pages * EFI_PAGE_SIZE >=
201                     VM_MAXUSER_ADDRESS) {
202                         printf("EFI Runtime entry %d is not in mappable for RT:"
203                             "base %#016jx %#jx pages\n",
204                             i, (uintmax_t)p->md_phys,
205                             (uintmax_t)p->md_pages);
206                         goto fail;
207                 }
208                 if ((p->md_attr & EFI_MD_ATTR_WB) != 0)
209                         mode = VM_MEMATTR_WRITE_BACK;
210                 else if ((p->md_attr & EFI_MD_ATTR_WT) != 0)
211                         mode = VM_MEMATTR_WRITE_THROUGH;
212                 else if ((p->md_attr & EFI_MD_ATTR_WC) != 0)
213                         mode = VM_MEMATTR_WRITE_COMBINING;
214                 else
215                         mode = VM_MEMATTR_DEVICE;
216
217                 if (bootverbose) {
218                         printf("MAP %lx mode %x pages %lu\n",
219                             p->md_phys, mode, p->md_pages);
220                 }
221
222                 l3_attr = ATTR_DEFAULT | ATTR_S1_IDX(mode) |
223                     ATTR_S1_AP(ATTR_S1_AP_RW) | ATTR_S1_nG | L3_PAGE;
224                 if (mode == VM_MEMATTR_DEVICE || p->md_attr & EFI_MD_ATTR_XP)
225                         l3_attr |= ATTR_S1_XN;
226
227                 VM_OBJECT_WLOCK(obj_1t1_pt);
228                 for (va = p->md_phys, idx = 0; idx < p->md_pages;
229                     idx += (PAGE_SIZE / EFI_PAGE_SIZE), va += PAGE_SIZE) {
230                         l3 = efi_1t1_l3(va);
231                         *l3 = va | l3_attr;
232                 }
233                 VM_OBJECT_WUNLOCK(obj_1t1_pt);
234         }
235
236         return (true);
237 fail:
238         efi_destroy_1t1_map();
239         return (false);
240 }
241
242 int
243 efi_arch_enter(void)
244 {
245
246         CRITICAL_ASSERT(curthread);
247
248         /*
249          * Temporarily switch to EFI's page table.  However, we leave curpmap
250          * unchanged in order to prevent its ASID from being reclaimed before
251          * we switch back to its page table in efi_arch_leave().
252          */
253         set_ttbr0(efi_ttbr0);
254         if (PCPU_GET(bcast_tlbi_workaround) != 0)
255                 invalidate_local_icache();
256
257         return (0);
258 }
259
260 void
261 efi_arch_leave(void)
262 {
263
264         /*
265          * Restore the pcpu pointer. Some UEFI implementations trash it and
266          * we don't store it before calling into them. To fix this we need
267          * to restore it after returning to the kernel context. As reading
268          * curpmap will access x18 we need to restore it before loading
269          * the pmap pointer.
270          */
271         __asm __volatile(
272             "mrs x18, tpidr_el1 \n"
273         );
274         set_ttbr0(pmap_to_ttbr0(PCPU_GET(curpmap)));
275         if (PCPU_GET(bcast_tlbi_workaround) != 0)
276                 invalidate_local_icache();
277 }
278
279 int
280 efi_rt_arch_call(struct efirt_callinfo *ec)
281 {
282
283         panic("not implemented");
284 }