1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
8 * See "LICENSE_GPLv2.txt" for details.
9 *
10 * @TAG(DATA61_GPL)
11 */
12
13#include <config.h>
14
15#ifdef CONFIG_VTX
16
17#include <model/statedata.h>
18#include <arch/kernel/ept.h>
19#include <arch/api/invocation.h>
20
21struct lookupEPTPDPTSlot_ret {
22    exception_t status;
23    ept_pdpte_t* pdptSlot;
24};
25typedef struct lookupEPTPDPTSlot_ret lookupEPTPDPTSlot_ret_t;
26
27struct lookupEPTPDSlot_ret {
28    exception_t status;
29    ept_pde_t*  pdSlot;
30};
31typedef struct lookupEPTPDSlot_ret lookupEPTPDSlot_ret_t;
32
33struct lookupEPTPTSlot_ret {
34    exception_t status;
35    ept_pte_t*  ptSlot;
36};
37typedef struct lookupEPTPTSlot_ret lookupEPTPTSlot_ret_t;
38
39enum ept_cache_options {
40    EPTUncacheable = 0,
41    EPTWriteCombining = 1,
42    EPTWriteThrough = 4,
43    EPTWriteProtected = 5,
44    EPTWriteBack = 6
45};
46typedef enum ept_cache_options ept_cache_options_t;
47
48void
49deleteEPTASID(asid_t asid, ept_pml4e_t *ept)
50{
51    asid_pool_t* poolPtr;
52
53    poolPtr = x86KSASIDTable[asid >> asidLowBits];
54    if (poolPtr != NULL) {
55        asid_map_t asid_map = poolPtr->array[asid & MASK(asidLowBits)];
56        if (asid_map_get_type(asid_map) == asid_map_asid_map_ept &&
57                (ept_pml4e_t*)asid_map_asid_map_ept_get_ept_root(asid_map) == ept) {
58            poolPtr->array[asid & MASK(asidLowBits)] = asid_map_asid_map_none_new();
59        }
60    }
61}
62
63exception_t
64performX86EPTPageInvocationUnmap(cap_t cap, cte_t *ctSlot)
65{
66    unmapEPTPage(
67        cap_frame_cap_get_capFSize(cap),
68        cap_frame_cap_get_capFMappedASID(cap),
69        cap_frame_cap_get_capFMappedAddress(cap),
70        (void *)cap_frame_cap_get_capFBasePtr(cap)
71    );
72
73    cap_frame_cap_ptr_set_capFMappedAddress(&ctSlot->cap, 0);
74    cap_frame_cap_ptr_set_capFMappedASID(&ctSlot->cap, asidInvalid);
75    cap_frame_cap_ptr_set_capFMapType(&ctSlot->cap, X86_MappingNone);
76
77    return EXCEPTION_NONE;
78}
79
80findEPTForASID_ret_t
81findEPTForASID(asid_t asid)
82{
83    findEPTForASID_ret_t ret;
84    asid_map_t asid_map;
85
86    asid_map = findMapForASID(asid);
87    if (asid_map_get_type(asid_map) != asid_map_asid_map_ept) {
88        current_lookup_fault = lookup_fault_invalid_root_new();
89
90        ret.ept = NULL;
91        ret.status = EXCEPTION_LOOKUP_FAULT;
92        return ret;
93    }
94
95    ret.ept = (ept_pml4e_t*)asid_map_asid_map_ept_get_ept_root(asid_map);
96    ret.status = EXCEPTION_NONE;
97    return ret;
98}
99
100static ept_pml4e_t* CONST
101lookupEPTPML4Slot(ept_pml4e_t *pml4, vptr_t vptr)
102{
103    return pml4 + GET_EPT_PML4_INDEX(vptr);
104}
105
106static lookupEPTPDPTSlot_ret_t CONST
107lookupEPTPDPTSlot(ept_pml4e_t *pml4, vptr_t vptr)
108{
109    lookupEPTPDPTSlot_ret_t ret;
110    ept_pml4e_t *pml4Slot;
111
112    pml4Slot = lookupEPTPML4Slot(pml4, vptr);
113
114    if (!ept_pml4e_ptr_get_read(pml4Slot)) {
115        current_lookup_fault = lookup_fault_missing_capability_new(22);
116
117        ret.pdptSlot = NULL;
118        ret.status = EXCEPTION_LOOKUP_FAULT;
119        return ret;
120    }
121
122    ept_pdpte_t *pdpt = paddr_to_pptr(ept_pml4e_ptr_get_pdpt_base_address(pml4Slot));
123    uint32_t index = GET_EPT_PDPT_INDEX(vptr);
124    ret.pdptSlot = pdpt + index;
125    ret.status = EXCEPTION_NONE;
126    return ret;
127}
128
129static lookupEPTPDSlot_ret_t
130lookupEPTPDSlot(ept_pml4e_t* pml4, vptr_t vptr)
131{
132    lookupEPTPDSlot_ret_t ret;
133    lookupEPTPDPTSlot_ret_t lu_ret;
134
135    lu_ret = lookupEPTPDPTSlot(pml4, vptr);
136    if (lu_ret.status != EXCEPTION_NONE) {
137        current_syscall_error.type = seL4_FailedLookup;
138        current_syscall_error.failedLookupWasSource = false;
139        /* current_lookup_fault will have been set by lookupEPTPDPTSlot */
140        ret.pdSlot = NULL;
141        ret.status = EXCEPTION_LOOKUP_FAULT;
142        return ret;
143    }
144
145    if (!ept_pdpte_ptr_get_read(lu_ret.pdptSlot)) {
146        current_lookup_fault = lookup_fault_missing_capability_new(22);
147
148        ret.pdSlot = NULL;
149        ret.status = EXCEPTION_LOOKUP_FAULT;
150        return ret;
151    }
152
153    ept_pde_t *pd = paddr_to_pptr(ept_pdpte_ptr_get_pd_base_address(lu_ret.pdptSlot));
154    uint32_t index = GET_EPT_PD_INDEX(vptr);
155    ret.pdSlot = pd + index;
156    ret.status = EXCEPTION_NONE;
157    return ret;
158}
159
160static lookupEPTPTSlot_ret_t
161lookupEPTPTSlot(ept_pml4e_t* pml4, vptr_t vptr)
162{
163    lookupEPTPTSlot_ret_t ret;
164    lookupEPTPDSlot_ret_t lu_ret;
165
166    lu_ret = lookupEPTPDSlot(pml4, vptr);
167    if (lu_ret.status != EXCEPTION_NONE) {
168        current_syscall_error.type = seL4_FailedLookup;
169        current_syscall_error.failedLookupWasSource = false;
170        /* current_lookup_fault will have been set by lookupEPTPDSlot */
171        ret.ptSlot = NULL;
172        ret.status = EXCEPTION_LOOKUP_FAULT;
173        return ret;
174    }
175
176    if ((ept_pde_ptr_get_page_size(lu_ret.pdSlot) != ept_pde_ept_pde_pt) ||
177            !ept_pde_ept_pde_pt_ptr_get_read(lu_ret.pdSlot)) {
178        current_lookup_fault = lookup_fault_missing_capability_new(22);
179
180        ret.ptSlot = NULL;
181        ret.status = EXCEPTION_LOOKUP_FAULT;
182        return ret;
183    }
184
185    ept_pte_t *pt = paddr_to_pptr(ept_pde_ept_pde_pt_ptr_get_pt_base_address(lu_ret.pdSlot));
186    uint32_t index = GET_EPT_PT_INDEX(vptr);
187
188    ret.ptSlot = pt + index;
189    ret.status = EXCEPTION_NONE;
190    return ret;
191}
192
193static ept_cache_options_t
194eptCacheFromVmAttr(vm_attributes_t vmAttr)
195{
196    /* PAT cache options are 1-1 with ept_cache_options. But need to
197       verify user has specified a sensible option */
198    ept_cache_options_t option = vmAttr.words[0];
199    if (option != EPTUncacheable ||
200            option != EPTWriteCombining ||
201            option != EPTWriteThrough ||
202            option != EPTWriteBack) {
203        /* No failure mode is supported here, vmAttr settings should be verified earlier */
204        option = EPTWriteBack;
205    }
206    return option;
207}
208
209EPTPDPTMapped_ret_t
210EPTPDPTMapped(asid_t asid, vptr_t vptr, ept_pdpte_t *pdpt)
211{
212    EPTPDPTMapped_ret_t ret;
213    findEPTForASID_ret_t asid_ret;
214    ept_pml4e_t *pml4Slot;
215
216    asid_ret = findEPTForASID(asid);
217    if (asid_ret.status != EXCEPTION_NONE) {
218        ret.pml4 = NULL;
219        ret.pml4Slot = NULL;
220        ret.status = asid_ret.status;
221        return ret;
222    }
223
224    pml4Slot = lookupEPTPML4Slot(asid_ret.ept, vptr);
225
226    if (ept_pml4e_ptr_get_read(pml4Slot)
227            && ptrFromPAddr(ept_pml4e_ptr_get_pdpt_base_address(pml4Slot)) == pdpt) {
228        ret.pml4 = asid_ret.ept;
229        ret.pml4Slot = pml4Slot;
230        ret.status = EXCEPTION_NONE;
231        return ret;
232    } else {
233        ret.pml4 = NULL;
234        ret.pml4Slot = NULL;
235        ret.status = EXCEPTION_LOOKUP_FAULT;
236        return ret;
237    }
238}
239
240void
241unmapEPTPDPT(asid_t asid, vptr_t vaddr, ept_pdpte_t *pdpt)
242{
243    EPTPDPTMapped_ret_t lu_ret;
244
245    lu_ret = EPTPDPTMapped(asid, vaddr, pdpt);
246
247    if (lu_ret.status == EXCEPTION_NONE) {
248        *lu_ret.pml4Slot = ept_pml4e_new(0, 0, 0, 0);
249        invept(lu_ret.pml4);
250    }
251}
252
253static exception_t
254performEPTPDPTInvocationUnmap(cap_t cap, cte_t *cte)
255{
256    if (cap_ept_pdpt_cap_get_capPDPTIsMapped(cap)) {
257        ept_pdpte_t *pdpt = (ept_pdpte_t*)cap_ept_pdpt_cap_get_capPDPTBasePtr(cap);
258        unmapEPTPDPT(
259            cap_ept_pdpt_cap_get_capPDPTMappedASID(cap),
260            cap_ept_pdpt_cap_get_capPDPTMappedAddress(cap),
261            pdpt);
262        clearMemory((void *)pdpt, cap_get_capSizeBits(cap));
263    }
264    cap_ept_pdpt_cap_ptr_set_capPDPTIsMapped(&(cte->cap), 0);
265
266    return EXCEPTION_NONE;
267}
268
269static exception_t
270performEPTPDPTInvocationMap(cap_t cap, cte_t *cte, ept_pml4e_t pml4e, ept_pml4e_t *pml4Slot, ept_pml4e_t *pml4)
271{
272    cte->cap = cap;
273    *pml4Slot = pml4e;
274    invept(pml4);
275
276    return EXCEPTION_NONE;
277}
278
279static exception_t
280decodeX86EPTPDPTInvocation(
281    word_t invLabel,
282    word_t length,
283    cte_t *cte,
284    cap_t cap,
285    extra_caps_t excaps,
286    word_t *buffer
287)
288{
289    word_t          vaddr;
290    cap_t           pml4Cap;
291    ept_pml4e_t*    pml4;
292    ept_pml4e_t     pml4e;
293    paddr_t         paddr;
294    asid_t          asid;
295    findEPTForASID_ret_t find_ret;
296    ept_pml4e_t*    pml4Slot;
297
298    if (invLabel == X86EPTPDPTUnmap) {
299        if (!isFinalCapability(cte)) {
300            current_syscall_error.type = seL4_RevokeFirst;
301            return EXCEPTION_SYSCALL_ERROR;
302        }
303        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
304        return performEPTPDPTInvocationUnmap(cap, cte);
305    }
306
307    if (invLabel != X86EPTPDPTMap) {
308        userError("X86EPTPDPT Illegal operation.");
309        current_syscall_error.type = seL4_IllegalOperation;
310        return EXCEPTION_SYSCALL_ERROR;
311    }
312
313    if (length < 2 || excaps.excaprefs[0] == NULL) {
314        userError("X86EPTPDPTMap: Truncated message.");
315        current_syscall_error.type = seL4_TruncatedMessage;
316        return EXCEPTION_SYSCALL_ERROR;
317    }
318
319    if (cap_ept_pdpt_cap_get_capPDPTIsMapped(cap)) {
320        userError("X86EPTPDPTMap: EPT PDPT is already mapped to a PML4.");
321        current_syscall_error.type = seL4_InvalidCapability;
322        current_syscall_error.invalidCapNumber = 0;
323
324        return EXCEPTION_SYSCALL_ERROR;
325    }
326
327    vaddr = getSyscallArg(0, buffer);
328    /* cannot use ~MASK(EPT_PML4_INDEX_OFFSET) because on 32-bit compilations
329     * this results in an error shifting by greater than 31 bits, so we manually
330     * force a 64-bit variable to do the shifting with */
331    vaddr = vaddr & ~(((uint64_t)1 << EPT_PML4_INDEX_OFFSET) - 1);
332    pml4Cap = excaps.excaprefs[0]->cap;
333
334    if (cap_get_capType(pml4Cap) != cap_ept_pml4_cap) {
335        userError("X86EPTPDPTMap: Not a valid EPT PML4.");
336        current_syscall_error.type = seL4_InvalidCapability;
337        current_syscall_error.invalidCapNumber = 1;
338
339        return EXCEPTION_SYSCALL_ERROR;
340    }
341
342    pml4 = (ept_pml4e_t*)cap_ept_pml4_cap_get_capPML4BasePtr(pml4Cap);
343    asid = cap_ept_pml4_cap_get_capPML4MappedASID(pml4Cap);
344
345    find_ret = findEPTForASID(asid);
346    if (find_ret.status != EXCEPTION_NONE) {
347        current_syscall_error.type = seL4_FailedLookup;
348        current_syscall_error.failedLookupWasSource = false;
349
350        return EXCEPTION_SYSCALL_ERROR;
351    }
352
353    if (find_ret.ept != pml4) {
354        current_syscall_error.type = seL4_InvalidCapability;
355        current_syscall_error.invalidCapNumber = 1;
356
357        return EXCEPTION_SYSCALL_ERROR;
358    }
359
360    pml4Slot = lookupEPTPML4Slot(pml4, vaddr);
361
362    if (ept_pml4e_ptr_get_read(pml4Slot)) {
363        userError("X86EPTPDPTMap: PDPT already mapped here.");
364        current_syscall_error.type = seL4_DeleteFirst;
365        return EXCEPTION_SYSCALL_ERROR;
366    }
367
368    paddr = pptr_to_paddr((void*)cap_ept_pdpt_cap_get_capPDPTBasePtr(cap));
369    pml4e = ept_pml4e_new(
370                paddr,
371                1,
372                1,
373                1
374            );
375
376    cap = cap_ept_pdpt_cap_set_capPDPTIsMapped(cap, 1);
377    cap = cap_ept_pdpt_cap_set_capPDPTMappedASID(cap, asid);
378    cap = cap_ept_pdpt_cap_set_capPDPTMappedAddress(cap, vaddr);
379
380    setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
381    return performEPTPDPTInvocationMap(cap, cte, pml4e, pml4Slot, pml4);
382}
383
384exception_t
385decodeX86EPTInvocation(
386    word_t invLabel,
387    word_t length,
388    cptr_t cptr,
389    cte_t* cte,
390    cap_t cap,
391    extra_caps_t excaps,
392    word_t* buffer
393)
394{
395    switch (cap_get_capType(cap)) {
396    case cap_ept_pdpt_cap:
397        return decodeX86EPTPDPTInvocation(invLabel, length, cte, cap, excaps, buffer);
398    case cap_ept_pd_cap:
399        return decodeX86EPTPDInvocation(invLabel, length, cte, cap, excaps, buffer);
400    case cap_ept_pt_cap:
401        return decodeX86EPTPTInvocation(invLabel, length, cte, cap, excaps, buffer);
402    default:
403        fail("Invalid cap type");
404    }
405}
406
407EPTPageDirectoryMapped_ret_t
408EPTPageDirectoryMapped(asid_t asid, vptr_t vaddr, ept_pde_t *pd)
409{
410    EPTPageDirectoryMapped_ret_t ret;
411    lookupEPTPDPTSlot_ret_t find_ret;
412    findEPTForASID_ret_t asid_ret;
413
414    asid_ret = findEPTForASID(asid);
415    if (asid_ret.status != EXCEPTION_NONE) {
416        ret.pml4 = NULL;
417        ret.pdptSlot = NULL;
418        ret.status = asid_ret.status;
419        return ret;
420    }
421
422    find_ret = lookupEPTPDPTSlot(asid_ret.ept, vaddr);
423    if (find_ret.status != EXCEPTION_NONE) {
424        ret.pml4 = NULL;
425        ret.pdptSlot = NULL;
426        ret.status = find_ret.status;
427        return ret;
428    }
429
430    if (ept_pdpte_ptr_get_read(find_ret.pdptSlot)
431            && ptrFromPAddr(ept_pdpte_ptr_get_pd_base_address(find_ret.pdptSlot)) == pd) {
432        ret.pml4 = asid_ret.ept;
433        ret.pdptSlot = find_ret.pdptSlot;
434        ret.status = EXCEPTION_NONE;
435        return ret;
436    } else {
437        ret.pml4 = NULL;
438        ret.pdptSlot = NULL;
439        ret.status = EXCEPTION_LOOKUP_FAULT;
440        return ret;
441    }
442}
443
444void
445unmapEPTPageDirectory(asid_t asid, vptr_t vaddr, ept_pde_t *pd)
446{
447    EPTPageDirectoryMapped_ret_t lu_ret;
448
449    lu_ret = EPTPageDirectoryMapped(asid, vaddr, pd);
450
451    if (lu_ret.status == EXCEPTION_NONE) {
452        *lu_ret.pdptSlot = ept_pdpte_new(
453                               0,  /* pd_base_address  */
454                               0,  /* avl_cte_depth    */
455                               0,  /* execute          */
456                               0,  /* write            */
457                               0   /* read             */
458                           );
459        invept(lu_ret.pml4);
460    }
461}
462
463static exception_t
464performEPTPDInvocationUnmap(cap_t cap, cte_t *cte)
465{
466    if (cap_ept_pd_cap_get_capPDIsMapped(cap)) {
467        ept_pde_t *pd = (ept_pde_t*)cap_ept_pd_cap_get_capPDBasePtr(cap);
468        unmapEPTPageDirectory(
469            cap_ept_pd_cap_get_capPDMappedASID(cap),
470            cap_ept_pd_cap_get_capPDMappedAddress(cap),
471            pd);
472        clearMemory((void*)pd, cap_get_capSizeBits(cap));
473    }
474    cap_ept_pd_cap_ptr_set_capPDIsMapped(&(cte->cap), 0);
475
476    return EXCEPTION_NONE;
477}
478
479static exception_t
480performEPTPDInvocationMap(cap_t cap, cte_t *cte, ept_pdpte_t pdpte, ept_pdpte_t *pdptSlot, ept_pml4e_t *pml4)
481{
482    cte->cap = cap;
483    *pdptSlot = pdpte;
484    invept(pml4);
485
486    return EXCEPTION_NONE;
487}
488
489exception_t
490decodeX86EPTPDInvocation(
491    word_t invLabel,
492    word_t length,
493    cte_t* cte,
494    cap_t cap,
495    extra_caps_t excaps,
496    word_t* buffer
497)
498{
499    word_t          vaddr;
500    cap_t           pml4Cap;
501    ept_pml4e_t*    pml4;
502    ept_pdpte_t     pdpte;
503    paddr_t         paddr;
504    asid_t          asid;
505    findEPTForASID_ret_t find_ret;
506    lookupEPTPDPTSlot_ret_t lu_ret;
507
508    if (invLabel == X86EPTPDUnmap) {
509        if (!isFinalCapability(cte)) {
510            current_syscall_error.type = seL4_RevokeFirst;
511            return EXCEPTION_SYSCALL_ERROR;
512        }
513        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
514        return performEPTPDInvocationUnmap(cap, cte);
515    }
516
517    if (invLabel != X86EPTPDMap) {
518        userError("X86EPTPD Illegal operation.");
519        current_syscall_error.type = seL4_IllegalOperation;
520        return EXCEPTION_SYSCALL_ERROR;
521    }
522
523    if (length < 2 || excaps.excaprefs[0] == NULL) {
524        userError("X86EPTPDMap: Truncated message.");
525        current_syscall_error.type = seL4_TruncatedMessage;
526        return EXCEPTION_SYSCALL_ERROR;
527    }
528
529    if (cap_ept_pd_cap_get_capPDIsMapped(cap)) {
530        userError("X86EPTPDMap: EPT Page directory is already mapped to a PDPT.");
531        current_syscall_error.type =
532            seL4_InvalidCapability;
533        current_syscall_error.invalidCapNumber = 0;
534
535        return EXCEPTION_SYSCALL_ERROR;
536    }
537
538    vaddr = getSyscallArg(0, buffer);
539    vaddr = vaddr & ~MASK(EPT_PDPT_INDEX_OFFSET);
540    pml4Cap = excaps.excaprefs[0]->cap;
541
542    if (cap_get_capType(pml4Cap) != cap_ept_pml4_cap) {
543        userError("X86EPTPDMap: Not a valid EPT pml4.");
544        current_syscall_error.type = seL4_InvalidCapability;
545        current_syscall_error.invalidCapNumber = 1;
546
547        return EXCEPTION_SYSCALL_ERROR;
548    }
549
550    pml4 = (ept_pml4e_t*)cap_ept_pml4_cap_get_capPML4BasePtr(pml4Cap);
551    asid = cap_ept_pml4_cap_get_capPML4MappedASID(pml4Cap);
552
553    find_ret = findEPTForASID(asid);
554    if (find_ret.status != EXCEPTION_NONE) {
555        userError("X86EPTPDMap: EPT PML4 is not mapped.");
556        current_syscall_error.type = seL4_FailedLookup;
557        current_syscall_error.failedLookupWasSource = false;
558
559        return EXCEPTION_SYSCALL_ERROR;
560    }
561
562    if (find_ret.ept != pml4) {
563        userError("X86EPTPDMap: EPT PML4 asid is invalid.");
564        current_syscall_error.type = seL4_InvalidCapability;
565        current_syscall_error.invalidCapNumber = 1;
566
567        return EXCEPTION_SYSCALL_ERROR;
568    }
569
570    lu_ret = lookupEPTPDPTSlot(pml4, vaddr);
571    if (lu_ret.status != EXCEPTION_NONE) {
572        current_syscall_error.type = seL4_FailedLookup;
573        current_syscall_error.failedLookupWasSource = false;
574        return EXCEPTION_SYSCALL_ERROR;
575    }
576
577    if (ept_pdpte_ptr_get_read(lu_ret.pdptSlot)) {
578        userError("X86EPTPDMap: Page directory already mapped here.");
579        current_syscall_error.type = seL4_DeleteFirst;
580        return EXCEPTION_SYSCALL_ERROR;
581    }
582
583    paddr = pptr_to_paddr((void*)(cap_ept_pd_cap_get_capPDBasePtr(cap)));
584    pdpte = ept_pdpte_new(
585                paddr,  /* pd_base_address  */
586                0,      /* avl_cte_depth    */
587                1,      /* execute          */
588                1,      /* write            */
589                1       /* read             */
590            );
591
592    cap = cap_ept_pd_cap_set_capPDIsMapped(cap, 1);
593    cap = cap_ept_pd_cap_set_capPDMappedASID(cap, asid);
594    cap = cap_ept_pd_cap_set_capPDMappedAddress(cap, vaddr);
595
596    setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
597    return performEPTPDInvocationMap(cap, cte, pdpte, lu_ret.pdptSlot, pml4);
598}
599
600EPTPageTableMapped_ret_t
601EPTPageTableMapped(asid_t asid, vptr_t vaddr, ept_pte_t *pt)
602{
603    EPTPageTableMapped_ret_t ret;
604    lookupEPTPDSlot_ret_t find_ret;
605    findEPTForASID_ret_t asid_ret;
606
607    asid_ret = findEPTForASID(asid);
608    if (asid_ret.status != EXCEPTION_NONE) {
609        ret.pml4 = NULL;
610        ret.pdSlot = NULL;
611        ret.status = asid_ret.status;
612        return ret;
613    }
614
615    find_ret = lookupEPTPDSlot(asid_ret.ept, vaddr);
616    if (find_ret.status != EXCEPTION_NONE) {
617        ret.pml4 = NULL;
618        ret.pdSlot = NULL;
619        ret.status = find_ret.status;
620        return ret;
621    }
622
623    if (ept_pde_ptr_get_page_size(find_ret.pdSlot) == ept_pde_ept_pde_pt
624            && ptrFromPAddr(ept_pde_ept_pde_pt_ptr_get_pt_base_address(find_ret.pdSlot)) == pt) {
625        ret.pml4 = asid_ret.ept;
626        ret.pdSlot = find_ret.pdSlot;
627        ret.status = EXCEPTION_NONE;
628        return ret;
629    } else {
630        ret.pml4 = NULL;
631        ret.pdSlot = NULL;
632        ret.status = EXCEPTION_LOOKUP_FAULT;
633        return ret;
634    }
635}
636
637void
638unmapEPTPageTable(asid_t asid, vptr_t vaddr, ept_pte_t *pt)
639{
640    EPTPageTableMapped_ret_t lu_ret;
641
642    lu_ret = EPTPageTableMapped(asid, vaddr, pt);
643
644    if (lu_ret.status == EXCEPTION_NONE) {
645        *lu_ret.pdSlot = ept_pde_ept_pde_pt_new(
646                             0,  /* pt_base_address  */
647                             0,  /* avl_cte_depth    */
648                             0,  /* execute          */
649                             0,  /* write            */
650                             0   /* read             */
651                         );
652        invept(lu_ret.pml4);
653    }
654}
655
656static exception_t
657performEPTPTInvocationUnmap(cap_t cap, cte_t *cte)
658{
659    if (cap_ept_pt_cap_get_capPTIsMapped(cap)) {
660        ept_pte_t *pt = (ept_pte_t*)cap_ept_pt_cap_get_capPTBasePtr(cap);
661        unmapEPTPageTable(
662            cap_ept_pt_cap_get_capPTMappedASID(cap),
663            cap_ept_pt_cap_get_capPTMappedAddress(cap),
664            pt);
665        clearMemory((void *)pt, cap_get_capSizeBits(cap));
666    }
667    cap_ept_pt_cap_ptr_set_capPTIsMapped(&(cte->cap), 0);
668
669    return EXCEPTION_NONE;
670}
671
672static exception_t
673performEPTPTInvocationMap(cap_t cap, cte_t *cte, ept_pde_t pde, ept_pde_t *pdSlot, ept_pml4e_t *pml4)
674{
675    cte->cap = cap;
676    *pdSlot = pde;
677    invept(pml4);
678
679    return EXCEPTION_NONE;
680}
681
682exception_t
683decodeX86EPTPTInvocation(
684    word_t invLabel,
685    word_t length,
686    cte_t* cte,
687    cap_t cap,
688    extra_caps_t excaps,
689    word_t* buffer
690)
691{
692    word_t          vaddr;
693    cap_t           pml4Cap;
694    ept_pml4e_t*    pml4;
695    ept_pde_t       pde;
696    paddr_t         paddr;
697    asid_t          asid;
698    findEPTForASID_ret_t find_ret;
699    lookupEPTPDSlot_ret_t lu_ret;
700
701    if (invLabel == X86EPTPTUnmap) {
702        if (!isFinalCapability(cte)) {
703            current_syscall_error.type = seL4_RevokeFirst;
704            return EXCEPTION_SYSCALL_ERROR;
705        }
706        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
707        return performEPTPTInvocationUnmap(cap, cte);
708    }
709
710    if (invLabel != X86EPTPTMap) {
711        userError("X86EPTPT Illegal operation.");
712        current_syscall_error.type = seL4_IllegalOperation;
713        return EXCEPTION_SYSCALL_ERROR;
714    }
715
716    if (length < 2 || excaps.excaprefs[0] == NULL) {
717        userError("X86EPTPT: Truncated message.");
718        current_syscall_error.type = seL4_TruncatedMessage;
719        return EXCEPTION_SYSCALL_ERROR;
720    }
721
722    if (cap_ept_pt_cap_get_capPTIsMapped(cap)) {
723        userError("X86EPTPT EPT Page table is already mapped to an EPT page directory.");
724        current_syscall_error.type =
725            seL4_InvalidCapability;
726        current_syscall_error.invalidCapNumber = 0;
727
728        return EXCEPTION_SYSCALL_ERROR;
729    }
730
731    vaddr = getSyscallArg(0, buffer);
732    vaddr = vaddr & ~MASK(EPT_PD_INDEX_OFFSET);
733    pml4Cap = excaps.excaprefs[0]->cap;
734
735    if (cap_get_capType(pml4Cap) != cap_ept_pml4_cap ||
736            !cap_ept_pml4_cap_get_capPML4IsMapped(pml4Cap)) {
737        userError("X86EPTPTMap: Not a valid EPT pml4.");
738        current_syscall_error.type = seL4_InvalidCapability;
739        current_syscall_error.invalidCapNumber = 1;
740
741        return EXCEPTION_SYSCALL_ERROR;
742    }
743
744    pml4 = (ept_pml4e_t*)(cap_ept_pml4_cap_get_capPML4BasePtr(pml4Cap));
745    asid = cap_ept_pml4_cap_get_capPML4MappedASID(pml4Cap);
746
747    find_ret = findEPTForASID(asid);
748    if (find_ret.status != EXCEPTION_NONE) {
749        current_syscall_error.type = seL4_FailedLookup;
750        current_syscall_error.failedLookupWasSource = false;
751
752        return EXCEPTION_SYSCALL_ERROR;
753    }
754
755    if (find_ret.ept != pml4) {
756        current_syscall_error.type = seL4_InvalidCapability;
757        current_syscall_error.invalidCapNumber = 1;
758
759        return EXCEPTION_SYSCALL_ERROR;
760    }
761
762    lu_ret = lookupEPTPDSlot(pml4, vaddr);
763    if (lu_ret.status != EXCEPTION_NONE) {
764        current_syscall_error.type = seL4_FailedLookup;
765        current_syscall_error.failedLookupWasSource = false;
766        /* current_lookup_fault will have been set by lookupPTSlot */
767        return EXCEPTION_SYSCALL_ERROR;
768    }
769
770    if (((ept_pde_ptr_get_page_size(lu_ret.pdSlot) == ept_pde_ept_pde_pt) &&
771            ept_pde_ept_pde_pt_ptr_get_read(lu_ret.pdSlot)) ||
772            ((ept_pde_ptr_get_page_size(lu_ret.pdSlot) == ept_pde_ept_pde_2m) &&
773             ept_pde_ept_pde_2m_ptr_get_read(lu_ret.pdSlot))) {
774        userError("X86EPTPTMap: Page table already mapped here");
775        current_syscall_error.type = seL4_DeleteFirst;
776        return EXCEPTION_SYSCALL_ERROR;
777    }
778
779    paddr = pptr_to_paddr((void*)(cap_ept_pt_cap_get_capPTBasePtr(cap)));
780    pde = ept_pde_ept_pde_pt_new(
781              paddr,/* pt_base_address  */
782              0,    /* avl_cte_depth    */
783              1,    /* execute          */
784              1,    /* write            */
785              1     /* read             */
786          );
787
788    cap = cap_ept_pt_cap_set_capPTIsMapped(cap, 1);
789    cap = cap_ept_pt_cap_set_capPTMappedASID(cap, asid);
790    cap = cap_ept_pt_cap_set_capPTMappedAddress(cap, vaddr);
791
792    setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
793    return performEPTPTInvocationMap(cap, cte, pde, lu_ret.pdSlot, pml4);
794}
795
796static exception_t
797performEPTPageMapPTE(cap_t cap, cte_t *cte, ept_pte_t *ptSlot, ept_pte_t pte, ept_pml4e_t *pml4)
798{
799    *ptSlot = pte;
800    cte->cap = cap;
801    invept(pml4);
802
803    return EXCEPTION_NONE;
804}
805
806static exception_t
807performEPTPageMapPDE(cap_t cap, cte_t *cte, ept_pde_t *pdSlot, ept_pde_t pde1, ept_pde_t pde2, ept_pml4e_t *pml4)
808{
809    pdSlot[0] = pde1;
810    if (LARGE_PAGE_BITS == 22) {
811        pdSlot[1] = pde2;
812    }
813    cte->cap = cap;
814    invept(pml4);
815
816    return EXCEPTION_NONE;
817}
818
819exception_t
820decodeX86EPTPageMap(
821    word_t invLabel,
822    word_t length,
823    cte_t* cte,
824    cap_t cap,
825    extra_caps_t excaps,
826    word_t* buffer)
827{
828    word_t          vaddr;
829    word_t          w_rightsMask;
830    paddr_t         paddr;
831    cap_t           pml4Cap;
832    ept_pml4e_t*    pml4;
833    vm_rights_t     capVMRights;
834    vm_rights_t     vmRights;
835    vm_attributes_t vmAttr;
836    vm_page_size_t  frameSize;
837    asid_t          asid;
838
839    frameSize = cap_frame_cap_get_capFSize(cap);
840    vaddr = getSyscallArg(0, buffer);
841    vaddr = vaddr & ~MASK(EPT_PT_INDEX_OFFSET);
842    w_rightsMask = getSyscallArg(1, buffer);
843    vmAttr = vmAttributesFromWord(getSyscallArg(2, buffer));
844    pml4Cap = excaps.excaprefs[0]->cap;
845
846    capVMRights = cap_frame_cap_get_capFVMRights(cap);
847
848    if (cap_frame_cap_get_capFMappedASID(cap) != asidInvalid) {
849        userError("X86EPTPageMap: Frame already mapped.");
850        current_syscall_error.type = seL4_InvalidCapability;
851        current_syscall_error.invalidCapNumber = 0;
852
853        return EXCEPTION_SYSCALL_ERROR;
854    }
855
856    assert(cap_frame_cap_get_capFMapType(cap) == X86_MappingNone);
857
858    if (cap_get_capType(pml4Cap) != cap_ept_pml4_cap ||
859            !cap_ept_pml4_cap_get_capPML4IsMapped(pml4Cap)) {
860        userError("X86EPTPageMap: Attempting to map frame into invalid ept pml4.");
861        current_syscall_error.type = seL4_InvalidCapability;
862        current_syscall_error.invalidCapNumber = 1;
863
864        return EXCEPTION_SYSCALL_ERROR;
865    }
866
867    pml4 = (ept_pml4e_t*)(cap_ept_pml4_cap_get_capPML4BasePtr(pml4Cap));
868    asid = cap_ept_pml4_cap_get_capPML4MappedASID(pml4Cap);
869
870    findEPTForASID_ret_t find_ret = findEPTForASID(asid);
871    if (find_ret.status != EXCEPTION_NONE) {
872        current_syscall_error.type = seL4_FailedLookup;
873        current_syscall_error.failedLookupWasSource = false;
874
875        return EXCEPTION_SYSCALL_ERROR;
876    }
877
878    if (find_ret.ept != pml4) {
879        current_syscall_error.type = seL4_InvalidCapability;
880        current_syscall_error.invalidCapNumber = 1;
881
882        return EXCEPTION_SYSCALL_ERROR;
883    }
884
885
886    vmRights = maskVMRights(capVMRights, rightsFromWord(w_rightsMask));
887
888    if (!checkVPAlignment(frameSize, vaddr)) {
889        current_syscall_error.type = seL4_AlignmentError;
890
891        return EXCEPTION_SYSCALL_ERROR;
892    }
893
894    paddr = pptr_to_paddr((void*)cap_frame_cap_get_capFBasePtr(cap));
895
896    cap = cap_frame_cap_set_capFMappedASID(cap, asid);
897    cap = cap_frame_cap_set_capFMappedAddress(cap, vaddr);
898    cap = cap_frame_cap_set_capFMapType(cap, X86_MappingEPT);
899
900    switch (frameSize) {
901    /* PTE mappings */
902    case X86_SmallPage: {
903        lookupEPTPTSlot_ret_t lu_ret;
904        ept_pte_t pte;
905
906        lu_ret = lookupEPTPTSlot(pml4, vaddr);
907        if (lu_ret.status != EXCEPTION_NONE) {
908            current_syscall_error.type = seL4_FailedLookup;
909            current_syscall_error.failedLookupWasSource = false;
910            /* current_lookup_fault will have been set by lookupEPTPTSlot */
911            return EXCEPTION_SYSCALL_ERROR;
912        }
913
914        if (ept_pte_ptr_get_read(lu_ret.ptSlot)) {
915            userError("X86EPTPageMap: Mapping already present.");
916            current_syscall_error.type = seL4_DeleteFirst;
917            return EXCEPTION_SYSCALL_ERROR;
918        }
919
920        pte = ept_pte_new(
921                  paddr,
922                  0,
923                  0,
924                  eptCacheFromVmAttr(vmAttr),
925                  1,
926                  WritableFromVMRights(vmRights),
927                  1);
928
929        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
930        return performEPTPageMapPTE(cap, cte, lu_ret.ptSlot, pte, pml4);
931    }
932
933    /* PDE mappings */
934    case X86_LargePage: {
935        lookupEPTPDSlot_ret_t lu_ret;
936
937        lu_ret = lookupEPTPDSlot(pml4, vaddr);
938        if (lu_ret.status != EXCEPTION_NONE) {
939            userError("X86EPTPageMap: Need a page directory first.");
940            current_syscall_error.type = seL4_FailedLookup;
941            current_syscall_error.failedLookupWasSource = false;
942            /* current_lookup_fault will have been set by lookupEPTPDSlot */
943            return EXCEPTION_SYSCALL_ERROR;
944        }
945
946
947        if ((ept_pde_ptr_get_page_size(lu_ret.pdSlot) == ept_pde_ept_pde_pt) &&
948                ept_pde_ept_pde_pt_ptr_get_read(lu_ret.pdSlot)) {
949            userError("X86EPTPageMap: Page table already present.");
950            current_syscall_error.type = seL4_DeleteFirst;
951            return EXCEPTION_SYSCALL_ERROR;
952        }
953        if (LARGE_PAGE_BITS != EPT_PD_INDEX_OFFSET &&
954                (ept_pde_ptr_get_page_size(lu_ret.pdSlot + 1) == ept_pde_ept_pde_pt) &&
955                ept_pde_ept_pde_pt_ptr_get_read(lu_ret.pdSlot + 1)) {
956            userError("X86EPTPageMap: Page table already present.");
957            current_syscall_error.type = seL4_DeleteFirst;
958            return EXCEPTION_SYSCALL_ERROR;
959        }
960        if ((ept_pde_ptr_get_page_size(lu_ret.pdSlot) == ept_pde_ept_pde_2m) &&
961                ept_pde_ept_pde_2m_ptr_get_read(lu_ret.pdSlot)) {
962            userError("X86EPTPageMap: Mapping already present.");
963            current_syscall_error.type = seL4_DeleteFirst;
964            return EXCEPTION_SYSCALL_ERROR;
965        }
966
967        ept_pde_t pde1 = ept_pde_ept_pde_2m_new(
968                             paddr,
969                             0,
970                             0,
971                             eptCacheFromVmAttr(vmAttr),
972                             1,
973                             WritableFromVMRights(vmRights),
974                             1);
975
976        ept_pde_t pde2 = ept_pde_ept_pde_2m_new(
977                             paddr + BIT(EPT_PD_INDEX_OFFSET),
978                             0,
979                             0,
980                             eptCacheFromVmAttr(vmAttr),
981                             1,
982                             WritableFromVMRights(vmRights),
983                             1);
984
985        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
986        return performEPTPageMapPDE(cap, cte, lu_ret.pdSlot, pde1, pde2, pml4);
987    }
988
989    default:
990        /* When initializing EPT we only checked for support for 4K and 2M
991         * pages, so we must disallow attempting to use any other */
992        userError("X86EPTPageMap: Attempted to map unsupported page size.");
993        current_syscall_error.type = seL4_InvalidCapability;
994        current_syscall_error.invalidCapNumber = 0;
995        return EXCEPTION_SYSCALL_ERROR;
996    }
997}
998
999void
1000unmapEPTPage(vm_page_size_t page_size, asid_t asid, vptr_t vptr, void *pptr)
1001{
1002    findEPTForASID_ret_t find_ret;
1003    paddr_t addr = addrFromPPtr(pptr);
1004
1005    find_ret = findEPTForASID(asid);
1006    if (find_ret.status != EXCEPTION_NONE) {
1007        return;
1008    }
1009
1010    switch (page_size) {
1011    case X86_SmallPage: {
1012        lookupEPTPTSlot_ret_t lu_ret;
1013
1014        lu_ret = lookupEPTPTSlot(find_ret.ept, vptr);
1015        if (lu_ret.status != EXCEPTION_NONE) {
1016            return;
1017        }
1018        if (!ept_pte_ptr_get_read(lu_ret.ptSlot)) {
1019            return;
1020        }
1021        if (ept_pte_ptr_get_page_base_address(lu_ret.ptSlot) != addr) {
1022            return;
1023        }
1024
1025        *lu_ret.ptSlot = ept_pte_new(0, 0, 0, 0, 0, 0, 0);
1026        break;
1027    }
1028    case X86_LargePage: {
1029        lookupEPTPDSlot_ret_t lu_ret;
1030
1031        lu_ret = lookupEPTPDSlot(find_ret.ept, vptr);
1032        if (lu_ret.status != EXCEPTION_NONE) {
1033            return;
1034        }
1035        if (ept_pde_ptr_get_page_size(lu_ret.pdSlot) != ept_pde_ept_pde_2m) {
1036            return;
1037        }
1038        if (!ept_pde_ept_pde_2m_ptr_get_read(lu_ret.pdSlot)) {
1039            return;
1040        }
1041        if (ept_pde_ept_pde_2m_ptr_get_page_base_address(lu_ret.pdSlot) != addr) {
1042            return;
1043        }
1044
1045        lu_ret.pdSlot[0] = ept_pde_ept_pde_2m_new(0, 0, 0, 0, 0, 0, 0);
1046
1047        if (LARGE_PAGE_BITS != EPT_PD_INDEX_OFFSET) {
1048            assert(ept_pde_ptr_get_page_size(lu_ret.pdSlot + 1) == ept_pde_ept_pde_2m);
1049            assert(ept_pde_ept_pde_2m_ptr_get_read(lu_ret.pdSlot + 1));
1050            assert(ept_pde_ept_pde_2m_ptr_get_page_base_address(lu_ret.pdSlot + 1) == addr + BIT(21));
1051
1052            lu_ret.pdSlot[1] = ept_pde_ept_pde_2m_new(0, 0, 0, 0, 0, 0, 0);
1053        }
1054        break;
1055    }
1056    default:
1057        /* we did not allow mapping additional page sizes into EPT objects,
1058         * so this should not happen. As we have no way to return an error
1059         * all we can do is assert */
1060        assert(!"Invalid page size for unmap");
1061    }
1062}
1063
1064#endif /* CONFIG_VTX */
1065