1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <config.h>
8#include <types.h>
9#include <api/failures.h>
10#include <kernel/vspace.h>
11#include <object/structures.h>
12#include <arch/machine.h>
13#include <arch/model/statedata.h>
14#include <machine/fpu.h>
15#include <arch/object/objecttype.h>
16#include <arch/object/ioport.h>
17#include <plat/machine/devices.h>
18
19#include <arch/object/iospace.h>
20#include <arch/object/vcpu.h>
21#include <plat/machine/intel-vtd.h>
22
23deriveCap_ret_t Arch_deriveCap(cte_t *slot, cap_t cap)
24{
25    deriveCap_ret_t ret;
26
27    switch (cap_get_capType(cap)) {
28    case cap_page_table_cap:
29        if (cap_page_table_cap_get_capPTIsMapped(cap)) {
30            ret.cap = cap;
31            ret.status = EXCEPTION_NONE;
32        } else {
33            userError("Deriving an unmapped PT cap");
34            current_syscall_error.type = seL4_IllegalOperation;
35            ret.cap = cap_null_cap_new();
36            ret.status = EXCEPTION_SYSCALL_ERROR;
37        }
38        return ret;
39
40    case cap_page_directory_cap:
41        if (cap_page_directory_cap_get_capPDIsMapped(cap)) {
42            ret.cap = cap;
43            ret.status = EXCEPTION_NONE;
44        } else {
45            userError("Deriving a PD cap without an assigned ASID");
46            current_syscall_error.type = seL4_IllegalOperation;
47            ret.cap = cap_null_cap_new();
48            ret.status = EXCEPTION_SYSCALL_ERROR;
49        }
50        return ret;
51
52    case cap_asid_control_cap:
53    case cap_asid_pool_cap:
54        ret.cap = cap;
55        ret.status = EXCEPTION_NONE;
56        return ret;
57    case cap_io_port_control_cap:
58        ret.status = EXCEPTION_NONE;
59        ret.cap = cap_null_cap_new();
60        return ret;
61    case cap_io_port_cap:
62        ret.cap = cap;
63        ret.status = EXCEPTION_NONE;
64        return ret;
65
66#ifdef CONFIG_IOMMU
67    case cap_io_space_cap:
68        ret.cap = cap;
69        ret.status = EXCEPTION_NONE;
70        return ret;
71    case cap_io_page_table_cap:
72        if (cap_io_page_table_cap_get_capIOPTIsMapped(cap)) {
73            ret.cap = cap;
74            ret.status = EXCEPTION_NONE;
75        } else {
76            current_syscall_error.type = seL4_IllegalOperation;
77            ret.cap = cap_null_cap_new();
78            ret.status = EXCEPTION_SYSCALL_ERROR;
79        }
80        return ret;
81#endif
82
83#ifdef CONFIG_VTX
84    case cap_vcpu_cap:
85        ret.cap = cap;
86        ret.status = EXCEPTION_NONE;
87        return ret;
88    case cap_ept_pml4_cap:
89        if (cap_ept_pml4_cap_get_capPML4IsMapped(cap)) {
90            ret.cap = cap;
91            ret.status = EXCEPTION_NONE;
92        } else {
93            userError("Deriving a EPT PML4 cap without an assigned ASID.");
94            current_syscall_error.type = seL4_IllegalOperation;
95            ret.cap = cap_null_cap_new();
96            ret.status = EXCEPTION_SYSCALL_ERROR;
97        }
98        return ret;
99    case cap_ept_pdpt_cap:
100        if (cap_ept_pdpt_cap_get_capPDPTIsMapped(cap)) {
101            ret.cap = cap;
102            ret.status = EXCEPTION_NONE;
103        } else {
104            userError("Deriving an unmapped EPT PDPT cap.");
105            current_syscall_error.type = seL4_IllegalOperation;
106            ret.cap = cap_null_cap_new();
107            ret.status = EXCEPTION_SYSCALL_ERROR;
108        }
109        return ret;
110
111    case cap_ept_pd_cap:
112        if (cap_ept_pd_cap_get_capPDIsMapped(cap)) {
113            ret.cap = cap;
114            ret.status = EXCEPTION_NONE;
115        } else {
116            userError("Deriving an unmapped EPT PD cap.");
117            current_syscall_error.type = seL4_IllegalOperation;
118            ret.cap = cap_null_cap_new();
119            ret.status = EXCEPTION_SYSCALL_ERROR;
120        }
121        return ret;
122
123    case cap_ept_pt_cap:
124        if (cap_ept_pt_cap_get_capPTIsMapped(cap)) {
125            ret.cap = cap;
126            ret.status = EXCEPTION_NONE;
127        } else {
128            userError("Deriving an unmapped EPT PT cap.");
129            current_syscall_error.type = seL4_IllegalOperation;
130            ret.cap = cap_null_cap_new();
131            ret.status = EXCEPTION_SYSCALL_ERROR;
132        }
133        return ret;
134#endif
135
136    default:
137        return Mode_deriveCap(slot, cap);
138    }
139}
140
141cap_t CONST Arch_updateCapData(bool_t preserve, word_t data, cap_t cap)
142{
143    /* Avoid a switch statement with just a 'default' case as the C parser does not like this */
144#ifdef CONFIG_IOMMU
145    switch (cap_get_capType(cap)) {
146    case cap_io_space_cap: {
147        io_space_capdata_t w = { { data } };
148        uint16_t PCIDevice = io_space_capdata_get_PCIDevice(w);
149        uint16_t domainID = io_space_capdata_get_domainID(w);
150        if (!preserve && cap_io_space_cap_get_capPCIDevice(cap) == 0 &&
151            domainID >= x86KSFirstValidIODomain &&
152            domainID != 0                        &&
153            domainID <= MASK(x86KSnumIODomainIDBits)) {
154            return cap_io_space_cap_new(domainID, PCIDevice);
155        } else {
156            return cap_null_cap_new();
157        }
158    }
159
160    default:
161        return cap;
162    }
163#endif
164    return cap;
165}
166
167cap_t CONST Arch_maskCapRights(seL4_CapRights_t cap_rights_mask, cap_t cap)
168{
169    if (cap_get_capType(cap) == cap_frame_cap) {
170        vm_rights_t vm_rights;
171
172        vm_rights = vmRightsFromWord(cap_frame_cap_get_capFVMRights(cap));
173        vm_rights = maskVMRights(vm_rights, cap_rights_mask);
174        return cap_frame_cap_set_capFVMRights(cap, wordFromVMRights(vm_rights));
175    } else {
176        return cap;
177    }
178}
179
180finaliseCap_ret_t Arch_finaliseCap(cap_t cap, bool_t final)
181{
182    finaliseCap_ret_t fc_ret;
183
184    switch (cap_get_capType(cap)) {
185    case cap_page_directory_cap:
186        if (final && cap_page_directory_cap_get_capPDIsMapped(cap)) {
187            unmapPageDirectory(
188                cap_page_directory_cap_get_capPDMappedASID(cap),
189                cap_page_directory_cap_get_capPDMappedAddress(cap),
190                PDE_PTR(cap_page_directory_cap_get_capPDBasePtr(cap))
191            );
192        }
193        break;
194
195    case cap_page_table_cap:
196        if (final && cap_page_table_cap_get_capPTIsMapped(cap)) {
197            unmapPageTable(
198                cap_page_table_cap_get_capPTMappedASID(cap),
199                cap_page_table_cap_get_capPTMappedAddress(cap),
200                PT_PTR(cap_page_table_cap_get_capPTBasePtr(cap))
201            );
202        }
203        break;
204
205    case cap_asid_pool_cap:
206        if (final) {
207            deleteASIDPool(
208                cap_asid_pool_cap_get_capASIDBase(cap),
209                ASID_POOL_PTR(cap_asid_pool_cap_get_capASIDPool(cap))
210            );
211        }
212        break;
213    case cap_asid_control_cap:
214    case cap_io_port_control_cap:
215        break;
216    case cap_io_port_cap:
217#ifdef CONFIG_VTX
218        clearVPIDIOPortMappings(cap_io_port_cap_get_capIOPortVPID(cap),
219                                cap_io_port_cap_get_capIOPortFirstPort(cap),
220                                cap_io_port_cap_get_capIOPortLastPort(cap));
221#endif
222        if (final) {
223            fc_ret.remainder = cap_null_cap_new();
224            fc_ret.cleanupInfo = cap;
225            return fc_ret;
226        }
227        break;
228#ifdef CONFIG_IOMMU
229    case cap_io_space_cap:
230        if (final) {
231            unmapVTDContextEntry(cap);
232        }
233        break;
234
235    case cap_io_page_table_cap:
236        if (final && cap_io_page_table_cap_get_capIOPTIsMapped(cap)) {
237            deleteIOPageTable(cap);
238        }
239        break;
240#endif
241
242#ifdef CONFIG_VTX
243    case cap_vcpu_cap:
244        if (final) {
245            vcpu_finalise(VCPU_PTR(cap_vcpu_cap_get_capVCPUPtr(cap)));
246        }
247        break;
248    case cap_ept_pml4_cap:
249        if (final && cap_ept_pml4_cap_get_capPML4IsMapped(cap)) {
250            deleteEPTASID(cap_ept_pml4_cap_get_capPML4MappedASID(cap),
251                          (ept_pml4e_t *)cap_ept_pml4_cap_get_capPML4BasePtr(cap));
252        }
253        break;
254
255    case cap_ept_pdpt_cap:
256        if (final && cap_ept_pdpt_cap_get_capPDPTIsMapped(cap)) {
257            unmapEPTPDPT(
258                cap_ept_pdpt_cap_get_capPDPTMappedASID(cap),
259                cap_ept_pdpt_cap_get_capPDPTMappedAddress(cap),
260                (ept_pdpte_t *)cap_ept_pdpt_cap_get_capPDPTBasePtr(cap));
261        }
262        break;
263
264    case cap_ept_pd_cap:
265        if (final && cap_ept_pd_cap_get_capPDIsMapped(cap)) {
266            unmapEPTPageDirectory(
267                cap_ept_pd_cap_get_capPDMappedASID(cap),
268                cap_ept_pd_cap_get_capPDMappedAddress(cap),
269                (ept_pde_t *)cap_ept_pd_cap_get_capPDBasePtr(cap));
270        }
271        break;
272
273    case cap_ept_pt_cap:
274        if (final && cap_ept_pt_cap_get_capPTIsMapped(cap)) {
275            unmapEPTPageTable(
276                cap_ept_pt_cap_get_capPTMappedASID(cap),
277                cap_ept_pt_cap_get_capPTMappedAddress(cap),
278                (ept_pte_t *)cap_ept_pt_cap_get_capPTBasePtr(cap));
279        }
280        break;
281#endif
282
283    default:
284        return Mode_finaliseCap(cap, final);
285    }
286
287    fc_ret.remainder = cap_null_cap_new();
288    fc_ret.cleanupInfo = cap_null_cap_new();
289    return fc_ret;
290}
291
292bool_t CONST Arch_sameRegionAs(cap_t cap_a, cap_t cap_b)
293{
294    switch (cap_get_capType(cap_a)) {
295    case cap_frame_cap:
296        if (cap_get_capType(cap_b) == cap_frame_cap) {
297            word_t botA, botB, topA, topB;
298            botA = cap_frame_cap_get_capFBasePtr(cap_a);
299            botB = cap_frame_cap_get_capFBasePtr(cap_b);
300            topA = botA + MASK(pageBitsForSize(cap_frame_cap_get_capFSize(cap_a)));
301            topB = botB + MASK(pageBitsForSize(cap_frame_cap_get_capFSize(cap_b)));
302            return ((botA <= botB) && (topA >= topB) && (botB <= topB));
303        }
304        break;
305
306    case cap_page_table_cap:
307        if (cap_get_capType(cap_b) == cap_page_table_cap) {
308            return cap_page_table_cap_get_capPTBasePtr(cap_a) ==
309                   cap_page_table_cap_get_capPTBasePtr(cap_b);
310        }
311        break;
312
313    case cap_page_directory_cap:
314        if (cap_get_capType(cap_b) == cap_page_directory_cap) {
315            return cap_page_directory_cap_get_capPDBasePtr(cap_a) ==
316                   cap_page_directory_cap_get_capPDBasePtr(cap_b);
317        }
318        break;
319
320    case cap_asid_control_cap:
321        if (cap_get_capType(cap_b) == cap_asid_control_cap) {
322            return true;
323        }
324        break;
325
326    case cap_asid_pool_cap:
327        if (cap_get_capType(cap_b) == cap_asid_pool_cap) {
328            return cap_asid_pool_cap_get_capASIDPool(cap_a) ==
329                   cap_asid_pool_cap_get_capASIDPool(cap_b);
330        }
331        break;
332
333    case cap_io_port_control_cap:
334        if (cap_get_capType(cap_b) == cap_io_port_control_cap ||
335            cap_get_capType(cap_b) == cap_io_port_cap) {
336            return true;
337        }
338        break;
339
340    case cap_io_port_cap:
341        if (cap_get_capType(cap_b) == cap_io_port_cap) {
342            return  cap_io_port_cap_get_capIOPortFirstPort(cap_a) ==
343                    cap_io_port_cap_get_capIOPortFirstPort(cap_b) &&
344                    cap_io_port_cap_get_capIOPortLastPort(cap_a) ==
345                    cap_io_port_cap_get_capIOPortLastPort(cap_b);
346        }
347        break;
348
349#ifdef CONFIG_IOMMU
350    case cap_io_space_cap:
351        if (cap_get_capType(cap_b) == cap_io_space_cap) {
352            return cap_io_space_cap_get_capPCIDevice(cap_a) ==
353                   cap_io_space_cap_get_capPCIDevice(cap_b);
354        }
355        break;
356
357    case cap_io_page_table_cap:
358        if (cap_get_capType(cap_b) == cap_io_page_table_cap) {
359            return cap_io_page_table_cap_get_capIOPTBasePtr(cap_a) ==
360                   cap_io_page_table_cap_get_capIOPTBasePtr(cap_b);
361        }
362        break;
363#endif
364
365#ifdef CONFIG_VTX
366    case cap_vcpu_cap:
367        if (cap_get_capType(cap_b) == cap_vcpu_cap) {
368            return cap_vcpu_cap_get_capVCPUPtr(cap_a) ==
369                   cap_vcpu_cap_get_capVCPUPtr(cap_b);
370        }
371        break;
372
373    case cap_ept_pml4_cap:
374        if (cap_get_capType(cap_b) == cap_ept_pml4_cap) {
375            return cap_ept_pml4_cap_get_capPML4BasePtr(cap_a) ==
376                   cap_ept_pml4_cap_get_capPML4BasePtr(cap_b);
377        }
378        break;
379
380    case cap_ept_pdpt_cap:
381        if (cap_get_capType(cap_b) == cap_ept_pdpt_cap) {
382            return cap_ept_pdpt_cap_get_capPDPTBasePtr(cap_a) ==
383                   cap_ept_pdpt_cap_get_capPDPTBasePtr(cap_b);
384        }
385        break;
386
387    case cap_ept_pd_cap:
388        if (cap_get_capType(cap_b) == cap_ept_pd_cap) {
389            return cap_ept_pd_cap_get_capPDBasePtr(cap_a) ==
390                   cap_ept_pd_cap_get_capPDBasePtr(cap_b);
391        }
392        break;
393
394    case cap_ept_pt_cap:
395        if (cap_get_capType(cap_b) == cap_ept_pt_cap) {
396            return cap_ept_pt_cap_get_capPTBasePtr(cap_a) ==
397                   cap_ept_pt_cap_get_capPTBasePtr(cap_b);
398        }
399        break;
400
401#endif
402
403    }
404
405    return Mode_sameRegionAs(cap_a, cap_b);
406}
407
408bool_t CONST Arch_sameObjectAs(cap_t cap_a, cap_t cap_b)
409{
410    if (cap_get_capType(cap_a) == cap_io_port_control_cap &&
411        cap_get_capType(cap_b) == cap_io_port_cap) {
412        return false;
413    }
414    if (cap_get_capType(cap_a) == cap_frame_cap) {
415        if (cap_get_capType(cap_b) == cap_frame_cap) {
416            return ((cap_frame_cap_get_capFBasePtr(cap_a) ==
417                     cap_frame_cap_get_capFBasePtr(cap_b)) &&
418                    (cap_frame_cap_get_capFSize(cap_a) ==
419                     cap_frame_cap_get_capFSize(cap_b)) &&
420                    ((cap_frame_cap_get_capFIsDevice(cap_a) == 0) ==
421                     (cap_frame_cap_get_capFIsDevice(cap_b) == 0)));
422        }
423    }
424    return Arch_sameRegionAs(cap_a, cap_b);
425}
426
427word_t Arch_getObjectSize(word_t t)
428{
429    switch (t) {
430    case seL4_X86_4K:
431        return pageBitsForSize(X86_SmallPage);
432    case seL4_X86_LargePageObject:
433        return pageBitsForSize(X86_LargePage);
434    case seL4_X86_PageTableObject:
435        return seL4_PageTableBits;
436    case seL4_X86_PageDirectoryObject:
437        return seL4_PageDirBits;
438    case seL4_IA32_PDPTObject:
439        return seL4_PDPTBits;
440    case seL4_X86_IOPageTableObject:
441        return seL4_IOPageTableBits;
442#ifdef CONFIG_VTX
443    case seL4_X86_VCPUObject:
444        return seL4_X86_VCPUBits;
445    case seL4_X86_EPTPML4Object:
446        return seL4_X86_EPTPML4Bits;
447    case seL4_X86_EPTPDPTObject:
448        return seL4_X86_EPTPDPTBits;
449    case seL4_X86_EPTPDObject:
450        return seL4_X86_EPTPDBits;
451    case seL4_X86_EPTPTObject:
452        return seL4_X86_EPTPTBits;
453#endif
454    default:
455        return Mode_getObjectSize(t);
456    }
457}
458
459cap_t Arch_createObject(object_t t, void *regionBase, word_t userSize, bool_t deviceMemory)
460{
461#ifdef CONFIG_VTX
462    switch (t) {
463    case seL4_X86_VCPUObject: {
464        vcpu_t *vcpu;
465        vcpu = VCPU_PTR((word_t)regionBase);
466        vcpu_init(vcpu);
467        return cap_vcpu_cap_new(VCPU_REF(vcpu));
468    }
469    case seL4_X86_EPTPML4Object:
470        return cap_ept_pml4_cap_new(
471                   0,                  /* capPML4IsMapped      */
472                   VPID_INVALID,       /* capPML4MappedASID    */
473                   (word_t)regionBase  /* capPML4BasePtr       */
474               );
475    case seL4_X86_EPTPDPTObject:
476        return cap_ept_pdpt_cap_new(
477                   0,                  /* capPDPTMappedAddress */
478                   0,                  /* capPDPTIsMapped      */
479                   VPID_INVALID,       /* capPDPTMappedASID    */
480                   (word_t)regionBase   /* capPDPTBasePtr      */
481               );
482    case seL4_X86_EPTPDObject:
483        return cap_ept_pd_cap_new(
484                   0,                  /* capPDMappedAddress   */
485                   0,                  /* capPDIsMapped        */
486                   VPID_INVALID,       /* capPDMappedASID      */
487                   (word_t)regionBase  /* capPDBasePtr         */
488               );
489    case seL4_X86_EPTPTObject:
490        return cap_ept_pt_cap_new(
491                   0,                  /* capPTMappedAddress   */
492                   0,                  /* capPTIsMapped        */
493                   VPID_INVALID,       /* capPTMappedASID      */
494                   (word_t)regionBase  /* capPTBasePtr         */
495               );
496    default:
497#endif
498        return Mode_createObject(t, regionBase, userSize, deviceMemory);
499#ifdef CONFIG_VTX
500    }
501#endif
502}
503
504exception_t Arch_decodeInvocation(
505    word_t invLabel,
506    word_t length,
507    cptr_t cptr,
508    cte_t *slot,
509    cap_t cap,
510    extra_caps_t excaps,
511    bool_t call,
512    word_t *buffer
513)
514{
515    switch (cap_get_capType(cap)) {
516    case cap_asid_control_cap:
517    case cap_asid_pool_cap:
518        return decodeX86MMUInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
519    case cap_io_port_control_cap:
520        return decodeX86PortControlInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
521    case cap_io_port_cap:
522        return decodeX86PortInvocation(invLabel, length, cptr, slot, cap, excaps, call, buffer);
523#ifdef CONFIG_IOMMU
524    case cap_io_space_cap:
525        return decodeX86IOSpaceInvocation(invLabel, cap);
526    case cap_io_page_table_cap:
527        return decodeX86IOPTInvocation(invLabel, length, slot, cap, excaps, buffer);
528#endif
529#ifdef CONFIG_VTX
530    case cap_vcpu_cap:
531        return decodeX86VCPUInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
532    case cap_ept_pml4_cap:
533    case cap_ept_pdpt_cap:
534    case cap_ept_pd_cap:
535    case cap_ept_pt_cap:
536        return decodeX86EPTInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
537#endif
538    default:
539        return Mode_decodeInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
540    }
541}
542
543void Arch_prepareThreadDelete(tcb_t *thread)
544{
545    /* Notify the lazy FPU module about this thread's deletion. */
546    fpuThreadDelete(thread);
547}
548
549void Arch_postCapDeletion(cap_t cap)
550{
551    if (cap_get_capType(cap) == cap_io_port_cap) {
552        uint16_t first_port = cap_io_port_cap_get_capIOPortFirstPort(cap);
553        uint16_t last_port = cap_io_port_cap_get_capIOPortLastPort(cap);
554
555        freeIOPortRange(first_port, last_port);
556    }
557}
558