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