1 /*
2  * Copyright 2016, General Dynamics C4 Systems
3  *
4  * SPDX-License-Identifier: GPL-2.0-only
5  */
6 
7 #include <config.h>
8 
9 #ifdef CONFIG_TK1_SMMU
10 
11 #include <api/syscall.h>
12 #include <machine/io.h>
13 #include <kernel/thread.h>
14 #include <arch/api/invocation.h>
15 #include <arch/object/iospace.h>
16 #include <arch/model/statedata.h>
17 #include <object/structures.h>
18 #include <linker.h>
19 #include <plat/machine/smmu.h>
20 
21 
22 typedef struct lookupIOPDSlot_ret {
23     exception_t status;
24     iopde_t     *iopdSlot;
25 } lookupIOPDSlot_ret_t;
26 
27 typedef struct lookupIOPTSlot_ret {
28     exception_t status;
29     iopte_t     *ioptSlot;
30 } lookupIOPTSlot_ret_t;
31 
32 
33 #define IOPDE_VALID_MASK    0xe0000000
34 #define IOPTE_EMPTY_MASK    0xe0000000
35 
isIOPDEValid(iopde_t * iopde)36 static bool_t isIOPDEValid(iopde_t *iopde)
37 {
38     assert(iopde != 0);
39     return (iopde->words[0] & IOPDE_VALID_MASK) != 0;
40 }
41 
isIOPTEEmpty(iopte_t * iopte)42 static bool_t isIOPTEEmpty(iopte_t *iopte)
43 {
44     assert(iopte != 0);
45     return (iopte->words[0] & IOPTE_EMPTY_MASK) == 0;
46 }
47 
48 
lookupIOPDSlot(iopde_t * iopd,word_t io_address)49 static lookupIOPDSlot_ret_t lookupIOPDSlot(iopde_t *iopd, word_t io_address)
50 {
51     lookupIOPDSlot_ret_t ret;
52     uint32_t index = plat_smmu_iopd_index(io_address);
53     ret.status = EXCEPTION_NONE;
54     ret.iopdSlot = iopd + index;
55     return ret;
56 }
57 
lookupIOPTSlot(iopde_t * iopd,word_t io_address)58 static lookupIOPTSlot_ret_t lookupIOPTSlot(iopde_t *iopd, word_t io_address)
59 {
60     lookupIOPTSlot_ret_t pt_ret;
61     uint32_t index;
62     iopte_t *pt;
63 
64     lookupIOPDSlot_ret_t pd_ret = lookupIOPDSlot(iopd, io_address);
65     if (pd_ret.status != EXCEPTION_NONE) {
66         pt_ret.status = EXCEPTION_LOOKUP_FAULT;
67         pt_ret.ioptSlot = 0;
68         return pt_ret;
69     }
70 
71     if (!isIOPDEValid(pd_ret.iopdSlot) ||
72         iopde_ptr_get_page_size(pd_ret.iopdSlot) != iopde_iopde_pt) {
73         pt_ret.status = EXCEPTION_LOOKUP_FAULT;
74         pt_ret.ioptSlot = 0;
75         return pt_ret;
76     }
77 
78     index = plat_smmu_iopt_index(io_address);
79     pt = (iopte_t *)paddr_to_pptr(iopde_iopde_pt_ptr_get_address(pd_ret.iopdSlot));
80 
81     if (pt == 0) {
82         pt_ret.status = EXCEPTION_LOOKUP_FAULT;
83         pt_ret.ioptSlot = 0;
84         return pt_ret;
85     }
86 
87     pt_ret.status = EXCEPTION_NONE;
88     pt_ret.ioptSlot = pt + index;
89     return pt_ret;
90 }
91 
create_iospace_caps(cap_t root_cnode_cap)92 BOOT_CODE seL4_SlotRegion create_iospace_caps(cap_t root_cnode_cap)
93 {
94     seL4_SlotPos start = ndks_boot.slot_pos_cur;
95     seL4_SlotPos end = 0;
96     cap_t        io_space_cap;
97     int i = 0;
98     int num_smmu = plat_smmu_init();
99 
100     if (num_smmu == 0) {
101         printf("SMMU init failuer\n");
102         return S_REG_EMPTY;
103     }
104 
105     /* the 0 is reserved as an invalidASID,
106      * assuming each module is assigned an unique ASID
107      * and the ASIDs are contiguous
108      * */
109     for (i = 1; i <= num_smmu; i++) {
110         io_space_cap = cap_io_space_cap_new(i, i);
111         if (!provide_cap(root_cnode_cap, io_space_cap)) {
112             return S_REG_EMPTY;
113         }
114     }
115     end = ndks_boot.slot_pos_cur;
116     printf("Region [%x to %x) for SMMU caps\n", (unsigned int)start, (unsigned int)end);
117     return (seL4_SlotRegion) {
118         start, end
119     };
120 }
121 
performARMIOPTInvocationMap(cap_t cap,cte_t * slot,iopde_t * iopdSlot,iopde_t iopde)122 static exception_t performARMIOPTInvocationMap(cap_t cap, cte_t *slot, iopde_t *iopdSlot,
123                                                iopde_t iopde)
124 {
125 
126 
127     *iopdSlot = iopde;
128     cleanCacheRange_RAM((word_t)iopdSlot,
129                         ((word_t)iopdSlot) + sizeof(iopde_t),
130                         addrFromPPtr(iopdSlot));
131 
132     plat_smmu_tlb_flush_all();
133     plat_smmu_ptc_flush_all();
134 
135     slot->cap = cap;
136     setThreadState(ksCurThread, ThreadState_Restart);
137     return EXCEPTION_NONE;
138 }
139 
140 
decodeARMIOPTInvocation(word_t invLabel,uint32_t length,cte_t * slot,cap_t cap,word_t * buffer)141 exception_t decodeARMIOPTInvocation(
142     word_t       invLabel,
143     uint32_t     length,
144     cte_t       *slot,
145     cap_t        cap,
146     word_t      *buffer
147 )
148 {
149     cap_t      io_space;
150     word_t     io_address;
151     word_t     paddr;
152     uint16_t   module_id;
153     uint32_t   asid;
154     iopde_t    *pd;
155     iopde_t    iopde;
156     lookupIOPDSlot_ret_t    lu_ret;
157 
158     if (invLabel == ARMIOPageTableUnmap) {
159         deleteIOPageTable(slot->cap);
160         slot->cap = cap_io_page_table_cap_set_capIOPTIsMapped(slot->cap, 0);
161 
162         setThreadState(ksCurThread, ThreadState_Restart);
163         return EXCEPTION_NONE;
164     }
165 
166     if (current_extra_caps.excaprefs[0] == NULL || length < 1) {
167         userError("IOPTInvocation: Truncated message.");
168         current_syscall_error.type = seL4_TruncatedMessage;
169         return EXCEPTION_SYSCALL_ERROR;
170     }
171 
172     if (invLabel != ARMIOPageTableMap) {
173         userError("IOPTInvocation: Invalid operation.");
174         current_syscall_error.type = seL4_IllegalOperation;
175         return EXCEPTION_SYSCALL_ERROR;
176     }
177 
178     io_space     = current_extra_caps.excaprefs[0]->cap;
179     io_address   = getSyscallArg(0, buffer) & ~MASK(SMMU_IOPD_INDEX_SHIFT);
180 
181     if (cap_io_page_table_cap_get_capIOPTIsMapped(cap)) {
182         userError("IOPTMap: Cap already mapped.");
183         current_syscall_error.type = seL4_InvalidCapability;
184         current_syscall_error.invalidCapNumber = 0;
185         return EXCEPTION_SYSCALL_ERROR;
186     }
187 
188     if (cap_get_capType(io_space) != cap_io_space_cap) {
189         userError("IOPTMap: Invalid IOSpace cap.");
190         current_syscall_error.type = seL4_InvalidCapability;
191         current_syscall_error.invalidCapNumber = 1;
192         return EXCEPTION_SYSCALL_ERROR;
193     }
194 
195     module_id = cap_io_space_cap_get_capModuleID(io_space);
196     asid = plat_smmu_get_asid_by_module_id(module_id);
197     assert(asid != asidInvalid);
198 
199     paddr = pptr_to_paddr((void *)cap_io_page_table_cap_get_capIOPTBasePtr(cap));
200 
201     pd = plat_smmu_lookup_iopd_by_asid(asid);
202 
203     lu_ret = lookupIOPDSlot(pd, io_address);
204 
205     if (isIOPDEValid(lu_ret.iopdSlot)) {
206         userError("IOPTMap: Delete first.");
207         current_syscall_error.type = seL4_DeleteFirst;
208         return EXCEPTION_SYSCALL_ERROR;
209     }
210 
211     iopde = iopde_iopde_pt_new(
212                 1,      /* read         */
213                 1,      /* write        */
214                 1,      /* nonsecure    */
215                 paddr
216             );
217 
218     cap = cap_io_page_table_cap_set_capIOPTIsMapped(cap, 1);
219     cap = cap_io_page_table_cap_set_capIOPTASID(cap, asid);
220     cap = cap_io_page_table_cap_set_capIOPTMappedAddress(cap, io_address);
221 
222     return performARMIOPTInvocationMap(cap, slot, lu_ret.iopdSlot, iopde);
223 }
224 
performARMIOMapInvocation(cap_t cap,cte_t * slot,iopte_t * ioptSlot,iopte_t iopte)225 static exception_t performARMIOMapInvocation(cap_t cap, cte_t *slot, iopte_t *ioptSlot,
226                                              iopte_t iopte)
227 {
228     *ioptSlot = iopte;
229     cleanCacheRange_RAM((word_t)ioptSlot,
230                         ((word_t)ioptSlot) + sizeof(iopte_t),
231                         addrFromPPtr(ioptSlot));
232 
233     plat_smmu_tlb_flush_all();
234     plat_smmu_ptc_flush_all();
235 
236     slot->cap = cap;
237 
238     setThreadState(ksCurThread, ThreadState_Restart);
239     return EXCEPTION_NONE;
240 }
241 
decodeARMIOMapInvocation(word_t invLabel,uint32_t length,cte_t * slot,cap_t cap,word_t * buffer)242 exception_t decodeARMIOMapInvocation(
243     word_t       invLabel,
244     uint32_t     length,
245     cte_t       *slot,
246     cap_t        cap,
247     word_t      *buffer
248 )
249 {
250     cap_t      io_space;
251     paddr_t    io_address;
252     paddr_t    paddr;
253     uint32_t   module_id;
254     uint32_t   asid;
255     iopde_t    *pd;
256     iopte_t    iopte;
257     vm_rights_t     frame_cap_rights;
258     seL4_CapRights_t    dma_cap_rights_mask;
259     lookupIOPTSlot_ret_t lu_ret;
260 
261     if (current_extra_caps.excaprefs[0] == NULL || length < 2) {
262         userError("IOMap: Truncated message.");
263         current_syscall_error.type = seL4_TruncatedMessage;
264         return EXCEPTION_SYSCALL_ERROR;
265     }
266 
267     if (generic_frame_cap_get_capFSize(cap) != ARMSmallPage) {
268         userError("IOMap: Invalid cap type.");
269         current_syscall_error.type = seL4_InvalidCapability;
270         current_syscall_error.invalidCapNumber = 0;
271         return EXCEPTION_SYSCALL_ERROR;
272     }
273 
274     if (cap_small_frame_cap_get_capFMappedASID(cap) != asidInvalid) {
275         userError("IOMap: Frame all ready mapped.");
276         current_syscall_error.type = seL4_InvalidCapability;
277         current_syscall_error.invalidCapNumber = 0;
278         return EXCEPTION_SYSCALL_ERROR;
279     }
280 
281     io_space    = current_extra_caps.excaprefs[0]->cap;
282     io_address  = getSyscallArg(1, buffer) & ~MASK(PAGE_BITS);
283     paddr       = pptr_to_paddr((void *)cap_small_frame_cap_get_capFBasePtr(cap));
284 
285     if (cap_get_capType(io_space) != cap_io_space_cap) {
286         userError("IOMap: Invalid IOSpace cap.");
287         current_syscall_error.type = seL4_InvalidCapability;
288         current_syscall_error.invalidCapNumber = 1;
289         return EXCEPTION_SYSCALL_ERROR;
290     }
291 
292     module_id = cap_io_space_cap_get_capModuleID(io_space);
293     asid = plat_smmu_get_asid_by_module_id(module_id);
294     assert(asid != asidInvalid);
295 
296     pd = plat_smmu_lookup_iopd_by_asid(asid);
297 
298     lu_ret = lookupIOPTSlot(pd, io_address);
299     if (lu_ret.status != EXCEPTION_NONE) {
300         current_syscall_error.type = seL4_FailedLookup;
301         current_syscall_error.failedLookupWasSource = false;
302         return EXCEPTION_SYSCALL_ERROR;
303     }
304 
305     if (!isIOPTEEmpty(lu_ret.ioptSlot)) {
306         userError("IOMap: Delete first.");
307         current_syscall_error.type = seL4_DeleteFirst;
308         return EXCEPTION_SYSCALL_ERROR;
309     }
310     frame_cap_rights = cap_small_frame_cap_get_capFVMRights(cap);
311     dma_cap_rights_mask = rightsFromWord(getSyscallArg(0, buffer));
312 
313     if ((frame_cap_rights == VMReadOnly) && seL4_CapRights_get_capAllowRead(dma_cap_rights_mask)) {
314         /* read only */
315         iopte = iopte_new(
316                     1,      /* read         */
317                     0,      /* write        */
318                     1,      /* nonsecure    */
319                     paddr
320                 );
321     } else if (frame_cap_rights == VMReadWrite) {
322         if (seL4_CapRights_get_capAllowRead(dma_cap_rights_mask) &&
323             !seL4_CapRights_get_capAllowWrite(dma_cap_rights_mask)) {
324             /* read only */
325             iopte = iopte_new(
326                         1,      /* read         */
327                         0,      /* write        */
328                         1,      /* nonsecure    */
329                         paddr
330                     );
331         } else if (!seL4_CapRights_get_capAllowRead(dma_cap_rights_mask) &&
332                    seL4_CapRights_get_capAllowWrite(dma_cap_rights_mask)) {
333             /* write only */
334             iopte = iopte_new(
335                         0,      /* read         */
336                         1,      /* write        */
337                         1,      /* nonsecure    */
338                         paddr
339                     );
340         } else if (seL4_CapRights_get_capAllowRead(dma_cap_rights_mask) &&
341                    seL4_CapRights_get_capAllowWrite(dma_cap_rights_mask)) {
342             /* read write */
343             iopte = iopte_new(
344                         1,      /* read         */
345                         1,      /* write        */
346                         1,      /* nonsecure    */
347                         paddr
348                     );
349         } else {
350             userError("IOMap: Invalid argument.");
351             current_syscall_error.type = seL4_InvalidArgument;
352             current_syscall_error.invalidArgumentNumber = 0;
353             return EXCEPTION_SYSCALL_ERROR;
354         }
355 
356     } else {
357         /* VMKernelOnly */
358         userError("IOMap: Invalid argument.");
359         current_syscall_error.type = seL4_InvalidArgument;
360         current_syscall_error.invalidArgumentNumber = 0;
361         return EXCEPTION_SYSCALL_ERROR;
362     }
363 
364     cap = cap_small_frame_cap_set_capFIsIOSpace(cap, 1);
365     cap = cap_small_frame_cap_set_capFMappedASID(cap, asid);
366     cap = cap_small_frame_cap_set_capFMappedAddress(cap, io_address);
367 
368     return performARMIOMapInvocation(cap, slot, lu_ret.ioptSlot, iopte);
369 }
370 
371 
deleteIOPageTable(cap_t io_pt_cap)372 void deleteIOPageTable(cap_t io_pt_cap)
373 {
374 
375     uint32_t asid;
376     iopde_t *pd;
377     lookupIOPDSlot_ret_t lu_ret;
378     word_t io_address;
379     if (cap_io_page_table_cap_get_capIOPTIsMapped(io_pt_cap)) {
380         io_pt_cap = cap_io_page_table_cap_set_capIOPTIsMapped(io_pt_cap, 0);
381         asid = cap_io_page_table_cap_get_capIOPTASID(io_pt_cap);
382         assert(asid != asidInvalid);
383         pd = plat_smmu_lookup_iopd_by_asid(asid);
384         io_address = cap_io_page_table_cap_get_capIOPTMappedAddress(io_pt_cap);
385 
386         lu_ret = lookupIOPDSlot(pd, io_address);
387         if (lu_ret.status != EXCEPTION_NONE) {
388             return;
389         }
390 
391         if (isIOPDEValid(lu_ret.iopdSlot) &&
392             iopde_ptr_get_page_size(lu_ret.iopdSlot) == iopde_iopde_pt &&
393             iopde_iopde_pt_ptr_get_address(lu_ret.iopdSlot) != (pptr_to_paddr((void *)cap_io_page_table_cap_get_capIOPTBasePtr(
394                                                                                   io_pt_cap)))) {
395             return;
396         }
397 
398         *lu_ret.iopdSlot = iopde_iopde_pt_new(0, 0, 0, 0);
399         cleanCacheRange_RAM((word_t)lu_ret.iopdSlot,
400                             ((word_t)lu_ret.iopdSlot) + sizeof(iopde_t),
401                             addrFromPPtr(lu_ret.iopdSlot));
402 
403 
404         /* nice to have: flush by address and asid */
405         plat_smmu_tlb_flush_all();
406         plat_smmu_ptc_flush_all();
407     }
408 }
409 
unmapIOPage(cap_t cap)410 void unmapIOPage(cap_t cap)
411 {
412     lookupIOPTSlot_ret_t lu_ret;
413     iopde_t *pd;
414     word_t  io_address;
415     uint32_t asid;
416 
417     io_address = cap_small_frame_cap_get_capFMappedAddress(cap);
418     asid = cap_small_frame_cap_get_capFMappedASID(cap);
419     assert(asid != asidInvalid);
420     pd = plat_smmu_lookup_iopd_by_asid(asid);
421 
422     lu_ret = lookupIOPTSlot(pd, io_address);
423 
424     if (lu_ret.status != EXCEPTION_NONE) {
425         return;
426     }
427     if (iopte_ptr_get_address(lu_ret.ioptSlot) != pptr_to_paddr((void *)cap_small_frame_cap_get_capFBasePtr(cap))) {
428         return;
429     }
430 
431     *lu_ret.ioptSlot = iopte_new(0, 0, 0, 0);
432     cleanCacheRange_RAM((word_t)lu_ret.ioptSlot,
433                         ((word_t)lu_ret.ioptSlot) + sizeof(iopte_t),
434                         addrFromPPtr(lu_ret.ioptSlot));
435 
436     plat_smmu_tlb_flush_all();
437     plat_smmu_ptc_flush_all();
438     return;
439 }
440 
clearIOPageDirectory(cap_t cap)441 void clearIOPageDirectory(cap_t cap)
442 {
443     iopde_t  *pd;
444     uint32_t asid = cap_io_space_cap_get_capModuleID(cap);
445     word_t   size = BIT((SMMU_PD_INDEX_BITS));
446     assert(asid != asidInvalid);
447     pd = plat_smmu_lookup_iopd_by_asid(asid);
448 
449     memset((void *)pd, 0, size);
450     cleanCacheRange_RAM((word_t)pd, (word_t)pd + size, addrFromPPtr(pd));
451 
452     plat_smmu_tlb_flush_all();
453     plat_smmu_ptc_flush_all();
454     return;
455 }
456 
performPageInvocationUnmapIO(cap_t cap,cte_t * slot)457 exception_t performPageInvocationUnmapIO(
458     cap_t        cap,
459     cte_t       *slot
460 )
461 {
462     unmapIOPage(slot->cap);
463     slot->cap = cap_small_frame_cap_set_capFMappedAddress(slot->cap, 0);
464     slot->cap = cap_small_frame_cap_set_capFIsIOSpace(slot->cap, 0);
465     slot->cap = cap_small_frame_cap_set_capFMappedASID(slot->cap, asidInvalid);
466 
467     return EXCEPTION_NONE;
468 }
469 
decodeARMIOSpaceInvocation(word_t invLabel,cap_t cap)470 exception_t decodeARMIOSpaceInvocation(word_t invLabel, cap_t cap)
471 {
472     userError("IOSpace capability has no invocations");
473     current_syscall_error.type = seL4_IllegalOperation;
474     return EXCEPTION_SYSCALL_ERROR;
475 }
476 #endif /* end of CONFIG_TK1_SMMU */
477