seL4/src/arch/x86/64/kernel/vspace.c
2016-10-12 03:07:32 +00:00

1595 lines
56 KiB
C

/*
* Copyright 2016, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(D61_GPL)
*/
#include <config.h>
#include <api/syscall.h>
#include <machine/io.h>
#include <kernel/boot.h>
#include <model/statedata.h>
#include <arch/kernel/vspace.h>
#include <arch/kernel/boot.h>
#include <arch/api/invocation.h>
struct lookupPML4Slot_ret {
exception_t status;
pml4e_t *pml4Slot;
};
typedef struct lookupPML4Slot_ret lookupPML4Slot_ret_t;
/* For the boot code we create two windows into the physical address space
* One is at the same location as the kernel window, and is placed up high
* The other is a 1-to-1 mapping of the first 512gb of memory. The purpose
* of this is to have a 1-to-1 mapping for the low parts of memory, so that
* when we switch paging on, and are still running at physical addresses,
* we don't explode. Then we also want the high mappings so we can start
* running at proper kernel virtual addresses */
pml4e_t boot_pml4[BIT(PML4_INDEX_BITS)] __attribute__((aligned(BIT(seL4_PageBits)))) PHYS_DATA;
pdpte_t boot_pdpt[BIT(PDPT_INDEX_BITS)] __attribute__((aligned(BIT(seL4_PageBits)))) PHYS_DATA;
/* hardware interrupt handlers push up to 6 words onto the stack. THe order of the
words is Error, RIP, CS, FLAGS, RSP, SS */
word_t irq_stack[6] __attribute__((aligned(16)));
/* 'gdt_idt_ptr' is declared globally because of a C-subset restriction.
* It is only used in init_drts(), which therefore is non-reentrant.
*/
gdt_idt_ptr_t gdt_idt_ptr;
BOOT_CODE bool_t
map_kernel_window(
uint32_t num_ioapic,
paddr_t* ioapic_paddrs,
uint32_t num_drhu,
paddr_t* drhu_list
)
{
uint64_t paddr;
uint64_t vaddr;
#ifdef CONFIG_HUGE_PAGE
/* using 1 GiB page size */
/* verify that the kernel window as at the last entry of the PML4 */
assert(GET_PML4_INDEX(PPTR_BASE) == BIT(PML4_INDEX_BITS) - 1);
/* verify that the kernel_base is located in the last entry of the PML4,
* the second last entry of the PDPT, is 1gb aligned and 1gb in size */
assert(GET_PML4_INDEX(KERNEL_BASE) == BIT(PML4_INDEX_BITS) - 1);
assert(GET_PDPT_INDEX(KERNEL_BASE) == BIT(PML4_INDEX_BITS) - 2);
assert(GET_PDPT_INDEX(PPTR_KDEV) == BIT(PML4_INDEX_BITS) - 1);
assert(IS_ALIGNED(KERNEL_BASE, seL4_HugePageBits));
assert(IS_ALIGNED(PPTR_KDEV, seL4_HugePageBits));
/* place the PDPT into the PML4 */
pml4e_ptr_new(&x64KSGlobalPML4[GET_PML4_INDEX(PPTR_BASE)],
0, /* xd */
kpptr_to_paddr(x64KSGlobalPDPT),
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
/* put the 1GB kernel_base mapping into the PDPT */
pdpte_pdpte_1g_ptr_new(&x64KSGlobalPDPT[GET_PDPT_INDEX(KERNEL_BASE)],
0, /* xd */
PADDR_BASE,
0, /* PAT */
1, /* global */
0, /* dirty */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
/* also map the physical memory into the big kernel window */
paddr = 0;
vaddr = PPTR_BASE;
for (paddr = 0; paddr < PADDR_TOP;
paddr += BIT(seL4_HugePageBits)) {
int pdpte_index = GET_PDPT_INDEX(vaddr);
pdpte_pdpte_1g_ptr_new(&x64KSGlobalPDPT[pdpte_index],
0, /* xd */
paddr, /* physical address */
0, /* PAT */
1, /* global */
0, /* dirty */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
vaddr += BIT(seL4_HugePageBits);
}
/* put the PD into the PDPT */
pdpte_pdpte_pd_ptr_new(&x64KSGlobalPDPT[GET_PDPT_INDEX(PPTR_KDEV)],
0, /* xd */
kpptr_to_paddr(x64KSGlobalPD),
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
/* put the PT into the PD */
pde_pde_small_ptr_new(&x64KSGlobalPD[0],
0, /* xd */
kpptr_to_paddr(x64KSGlobalPT),
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
#else
int pd_index = 0;
/* use 2 MiB page size */
/* verify that the kernel window as at the last entry of the PML4 */
assert(GET_PML4_INDEX(PPTR_BASE) == BIT(PML4_INDEX_BITS) - 1);
/* verify that the kernel_base is located in the last entry of the PML4,
* the second last entry of the PDPT, is 1gb aligned and 1gb in size */
assert(GET_PML4_INDEX(KERNEL_BASE) == BIT(PML4_INDEX_BITS) - 1);
assert(GET_PDPT_INDEX(KERNEL_BASE) == BIT(PML4_INDEX_BITS) - 2);
assert(GET_PDPT_INDEX(PPTR_KDEV) == BIT(PML4_INDEX_BITS) - 1);
assert(IS_ALIGNED(KERNEL_BASE, seL4_HugePageBits));
assert(IS_ALIGNED(PPTR_KDEV, seL4_HugePageBits));
/* place the PDPT into the PML4 */
pml4e_ptr_new(&x64KSGlobalPML4[GET_PML4_INDEX(PPTR_BASE)],
0, /* xd */
kpptr_to_paddr(x64KSGlobalPDPT),
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
for (pd_index = 0; pd_index < PADDR_TOP >> seL4_HugePageBits; pd_index++) {
/* put the 1GB kernel_base mapping into the PDPT */
pdpte_pdpte_pd_ptr_new(&x64KSGlobalPDPT[GET_PDPT_INDEX(PPTR_BASE) + pd_index],
0, /* xd */
kpptr_to_paddr(&x64KSGlobalPDs[pd_index][0]),
0, /* accessed */
0, /* cache disabled */
0, /* write through */
0, /* super user */
1, /* read write */
1 /* present */
);
}
pdpte_pdpte_pd_ptr_new(&x64KSGlobalPDPT[GET_PDPT_INDEX(KERNEL_BASE)],
0, /* xd */
kpptr_to_paddr(&x64KSGlobalPDs[0][0]),
0, /* accessed */
0, /* cache disable */
1, /* write through */
0, /* super user */
1, /* read write */
1 /* present */
);
paddr = 0;
vaddr = PPTR_BASE;
for (paddr = 0; paddr < PADDR_TOP;
paddr += 0x200000) {
int pd_index = GET_PDPT_INDEX(vaddr) - GET_PDPT_INDEX(PPTR_BASE);
int pde_index = GET_PD_INDEX(vaddr);
pde_pde_large_ptr_new(&x64KSGlobalPDs[pd_index][pde_index],
0, /* xd */
paddr,
0, /* pat */
1, /* global */
0, /* dirty */
0, /* accessed */
0, /* cache disabled */
0, /* write through */
0, /* super user */
1, /* read write */
1 /* present */
);
vaddr += 0x200000;
}
/* put the PD into the PDPT */
pdpte_pdpte_pd_ptr_new(&x64KSGlobalPDPT[GET_PDPT_INDEX(PPTR_KDEV)],
0, /* xd */
kpptr_to_paddr(&x64KSGlobalPDs[BIT(PDPT_INDEX_BITS) - 1][0]),
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
/* put the PT into the PD */
pde_pde_small_ptr_new(&x64KSGlobalPDs[BIT(PDPT_INDEX_BITS) - 1][0],
0, /* xd */
kpptr_to_paddr(x64KSGlobalPT),
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
1, /* read_write */
1 /* present */
);
#endif
#if CONFIG_MAX_NUM_TRACE_POINTS > 0
/* use the last PD entry as the benchmark log storage.
* the actual backing physical memory will be filled
* later by using alloc_region */
ksLog = (ks_log_entry_t *)(PPTR_KDEV + 0x200000 * (BIT(PD_INDEX_BITS) - 1));
#endif
/* now map in the kernel devices */
if (!map_kernel_window_devices(x64KSGlobalPT, num_ioapic, ioapic_paddrs, num_drhu, drhu_list)) {
return false;
}
/* In boot code, so fine to just trash everything here */
invalidateTranslationAll();
printf("Mapping kernel window is done\n");
return true;
}
BOOT_CODE void
init_tss(tss_t *tss)
{
word_t base = (word_t)&irq_stack[6];
tss_ptr_new(
tss,
sizeof(*tss), /* io map base */
0, 0, /* ist 7 */
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0, /* ist 1*/
0, 0, /* rsp 2 */
0, 0, /* rsp 1 */
0, 0 /* rsp 0 */
);
/* set the IO map to all 1 to block user IN/OUT instructions */
memset(&x86KStss.io_map[0], 0xff, sizeof(x86KStss.io_map));
/* Set up a 'stack frame' for interrupts for the hardware to dump
* state to */
tss_ptr_set_rsp0_l(&x86KStss.tss, (uint32_t)base);
tss_ptr_set_rsp0_u(&x86KStss.tss, (uint32_t)(base >> 32));
}
BOOT_CODE void
init_syscall_msrs(void)
{
x86_wrmsr(IA32_LSTAR_MSR, (uint64_t)&handle_fastsyscall);
// mask bit 9 in the kernel (which is the interrupt enable bit)
// also mask bit 8, which is the Trap Flag, to prevent the kernel
// from single stepping
x86_wrmsr(IA32_FMASK_MSR, BIT(9) | BIT(8));
x86_wrmsr(IA32_STAR_MSR, ((uint64_t)SEL_CS_0 << 32) | ((uint64_t)SEL_CS_3 << 48));
}
BOOT_CODE void
init_gdt(gdt_entry_t *gdt, tss_t *tss)
{
uint64_t tss_base = (uint64_t)tss;
gdt_tss_t gdt_tss;
gdt[GDT_NULL] = gdt_entry_gdt_null_new();
gdt[GDT_CS_0] = gdt_entry_gdt_code_new(
0, /* base high */
1, /* granularity */
0, /* operation size, must be 0 when 64-bit is set */
1, /* long mode */
0, /* avl */
0xf, /* limit high */
1, /* present */
0, /* dpl */
1, /* always 1 for segment */
0, /* base middle */
0, /* base low */
0xffff /* limit low */
);
gdt[GDT_DS_0] = gdt_entry_gdt_data_new(
0, /* base high */
1, /* granularity */
1, /* operation size */
0, /* avl */
0xf, /* seg limit high */
1, /* present */
0, /* dpl */
1, /* always 1 */
0, /* base mid */
0, /* base low */
0xffff /* seg limit low */
);
gdt[GDT_CS_3] = gdt_entry_gdt_code_new(
0, /* base high */
1, /* granularity */
0, /* operation size, must be 0 when 64-bit is set */
1, /* long mode */
0, /* avl */
0xf, /* limit high */
1, /* present */
3, /* dpl */
1, /* always 1 */
0, /* base middle */
0, /* base low */
0xffff /* limit low */
);
gdt[GDT_DS_3] = gdt_entry_gdt_data_new(
0,
1,
1,
0,
0xf,
1,
3,
1,
0,
0,
0xffff
);
gdt[GDT_TLS] = gdt_entry_gdt_data_new(
0,
1,
1,
0,
0xf,
1,
3,
1,
0,
0,
0xffff
);
gdt[GDT_IPCBUF] = gdt_entry_gdt_data_new(
0,
1,
1,
0,
0xf,
1,
3,
1,
0,
0,
0xffff
);
gdt_tss = gdt_tss_new(
tss_base >> 32, /* base 63 - 32 */
(tss_base & 0xff000000UL) >> 24, /* base 31 - 24 */
1, /* granularity */
0, /* avl */
0, /* limit high */
1, /* present */
0, /* dpl */
9, /* desc type */
(tss_base & 0xff0000UL) >> 16, /* base 23-16 */
(tss_base & 0xffffUL), /* base 15 - 0 */
sizeof(tss_io_t) - 1
);
gdt[GDT_TSS].words[0] = gdt_tss.words[0];
gdt[GDT_TSS + 1].words[0] = gdt_tss.words[1];
}
BOOT_CODE void
init_idt_entry(idt_entry_t *idt, interrupt_t interrupt, void(*handler)(void))
{
uint64_t handler_addr = (uint64_t)handler;
uint64_t dpl = 3;
if (interrupt < int_trap_min && interrupt != int_software_break_request) {
dpl = 0;
}
idt[interrupt] = idt_entry_interrupt_gate_new(
handler_addr >> 32, /* offset 63 - 32 */
((handler_addr >> 16) & 0xffff),
1, /* present */
dpl, /* dpl */
0, /* ist */
SEL_CS_0, /* segment selector */
(handler_addr & 0xffff) /* offset 15 - 0 */
);
}
void setVMRoot(tcb_t *tcb)
{
cap_t threadRoot;
asid_t asid;
pml4e_t *pml4;
findVSpaceForASID_ret_t find_ret;
cr3_t cr3;
threadRoot = TCB_PTR_CTE_PTR(tcb, tcbVTable)->cap;
if (cap_get_capType(threadRoot) != cap_pml4_cap ||
!cap_pml4_cap_get_capPML4IsMapped(threadRoot)) {
setCurrentVSpaceRoot(kpptr_to_paddr(x64KSGlobalPML4), 0);
return;
}
pml4 = PML4E_PTR(cap_pml4_cap_get_capPML4BasePtr(threadRoot));
asid = cap_pml4_cap_get_capPML4MappedASID(threadRoot);
find_ret = findVSpaceForASID(asid);
if (unlikely(find_ret.status != EXCEPTION_NONE || find_ret.vspace_root != pml4)) {
setCurrentVSpaceRoot(kpptr_to_paddr(x64KSGlobalPML4), 0);
return;
}
cr3 = cr3_new(pptr_to_paddr(pml4), asid);
if (getCurrentCR3().words[0] != cr3.words[0]) {
setCurrentCR3(cr3, 1);
}
}
BOOT_CODE void
init_dtrs(void)
{
gdt_idt_ptr.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1;
gdt_idt_ptr.base = (uint64_t)x86KSgdt;
x64_install_gdt(&gdt_idt_ptr);
gdt_idt_ptr.limit = (sizeof(idt_entry_t) * (int_max + 1 )) - 1;
gdt_idt_ptr.base = (uint64_t)x86KSidt;
x64_install_idt(&gdt_idt_ptr);
x64_install_ldt(SEL_NULL);
x64_install_tss(SEL_TSS);
}
BOOT_CODE void
map_it_frame_cap(cap_t pd_cap, cap_t frame_cap)
{
pml4e_t *pml4 = PML4_PTR(pptr_of_cap(pd_cap));
pdpte_t *pdpt;
pde_t *pd;
pte_t *pt;
vptr_t vptr = cap_frame_cap_get_capFMappedAddress(frame_cap);
void *pptr = (void*)cap_frame_cap_get_capFBasePtr(frame_cap);
assert(cap_frame_cap_get_capFMapType(frame_cap) == X86_MappingVSpace);
assert(cap_frame_cap_get_capFMappedASID(frame_cap) != asidInvalid);
pml4 += GET_PML4_INDEX(vptr);
assert(pml4e_ptr_get_present(pml4));
pdpt = paddr_to_pptr(pml4e_ptr_get_pdpt_base_address(pml4));
pdpt += GET_PDPT_INDEX(vptr);
assert(pdpte_pdpte_pd_ptr_get_present(pdpt));
pd = paddr_to_pptr(pdpte_pdpte_pd_ptr_get_pd_base_address(pdpt));
pd += GET_PD_INDEX(vptr);
assert(pde_pde_small_ptr_get_present(pd));
pt = paddr_to_pptr(pde_pde_small_ptr_get_pt_base_address(pd));
pte_ptr_new(
pt + GET_PT_INDEX(vptr),
0, /* xd */
pptr_to_paddr(pptr), /* page_base_address */
0, /* global */
0, /* pat */
0, /* dirty */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
1, /* super_user */
1, /* read_write */
1 /* present */
);
}
static BOOT_CODE void
map_it_pdpt_cap(cap_t vspace_cap, cap_t pdpt_cap)
{
pml4e_t *pml4 = PML4_PTR(pptr_of_cap(vspace_cap));
pdpte_t *pdpt = PDPT_PTR(cap_pdpt_cap_get_capPDPTBasePtr(pdpt_cap));
vptr_t vptr = cap_pdpt_cap_get_capPDPTMappedAddress(pdpt_cap);
assert(cap_pdpt_cap_get_capPDPTIsMapped(pdpt_cap));
pml4e_ptr_new(
pml4 + GET_PML4_INDEX(vptr),
0, /* xd */
pptr_to_paddr(pdpt), /* pdpt_base_address */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
1, /* super_user */
1, /* read_write */
1 /* present */
);
}
BOOT_CODE void
map_it_pd_cap(cap_t vspace_cap, cap_t pd_cap)
{
pml4e_t *pml4 = PML4_PTR(pptr_of_cap(vspace_cap));
pdpte_t *pdpt;
pde_t *pd = PD_PTR(cap_page_directory_cap_get_capPDBasePtr(pd_cap));
vptr_t vptr = cap_page_directory_cap_get_capPDMappedAddress(pd_cap);
assert(cap_page_directory_cap_get_capPDIsMapped(pd_cap));
pml4 += GET_PML4_INDEX(vptr);
assert(pml4e_ptr_get_present(pml4));
pdpt = paddr_to_pptr(pml4e_ptr_get_pdpt_base_address(pml4));
pdpte_pdpte_pd_ptr_new(
pdpt + GET_PDPT_INDEX(vptr),
0, /* xd */
pptr_to_paddr(pd), /* pd_base_address */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
1, /* super_user */
1, /* read_write */
1 /* present */
);
}
BOOT_CODE void
map_it_pt_cap(cap_t vspace_cap, cap_t pt_cap)
{
pml4e_t *pml4 = PML4_PTR(pptr_of_cap(vspace_cap));
pdpte_t *pdpt;
pde_t *pd;
pte_t *pt = PT_PTR(cap_page_table_cap_get_capPTBasePtr(pt_cap));
vptr_t vptr = cap_page_table_cap_get_capPTMappedAddress(pt_cap);
assert(cap_page_table_cap_get_capPTIsMapped(pt_cap));
pml4 += GET_PML4_INDEX(vptr);
assert(pml4e_ptr_get_present(pml4));
pdpt = paddr_to_pptr(pml4e_ptr_get_pdpt_base_address(pml4));
pdpt += GET_PDPT_INDEX(vptr);
assert(pdpte_pdpte_pd_ptr_get_present(pdpt));
pd = paddr_to_pptr(pdpte_pdpte_pd_ptr_get_pd_base_address(pdpt));
pde_pde_small_ptr_new(
pd + GET_PD_INDEX(vptr),
0, /* xd */
pptr_to_paddr(pt), /* pt_base_address */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
1, /* super_user */
1, /* read_write */
1 /* present */
);
}
BOOT_CODE void*
map_temp_boot_page(void* entry, uint32_t large_pages)
{
/* this function is for legacy 32-bit systems where the ACPI tables might
* collide with the kernel window. Here we just assert that the table is
* in fact in the lower 4GiB region (which is already 1-to-1 mapped) and
* continue */
assert((word_t)entry < BIT(32));
return entry;
}
static BOOT_CODE cap_t
create_it_pdpt_cap(cap_t vspace_cap, pptr_t pptr, vptr_t vptr, asid_t asid)
{
cap_t cap;
cap = cap_pdpt_cap_new(
asid, /* capPDPTMappedASID */
pptr, /* capPDPTBasePtr */
1, /* capPDPTIsMapped */
vptr /* capPDPTMappedAddress */
);
map_it_pdpt_cap(vspace_cap, cap);
return cap;
}
static BOOT_CODE cap_t
create_it_pd_cap(cap_t vspace_cap, pptr_t pptr, vptr_t vptr, asid_t asid)
{
cap_t cap;
cap = cap_page_directory_cap_new(
asid, /* capPDMappedASID */
pptr, /* capPDBasePtr */
1, /* capPDIsMapped */
vptr /* capPDMappedAddress */
);
map_it_pd_cap(vspace_cap, cap);
return cap;
}
static BOOT_CODE cap_t
create_it_pt_cap(cap_t vspace_cap, pptr_t pptr, vptr_t vptr, asid_t asid)
{
cap_t cap;
cap = cap_page_table_cap_new(
asid, /* capPTMappedASID */
pptr, /* capPTBasePtr */
1, /* capPTIsMapped */
vptr /* capPTMappedAddress */
);
map_it_pt_cap(vspace_cap, cap);
return cap;
}
BOOT_CODE cap_t
create_it_address_space(cap_t root_cnode_cap, v_region_t it_v_reg)
{
cap_t vspace_cap;
vptr_t vptr;
pptr_t pptr;
seL4_SlotPos slot_pos_before;
seL4_SlotPos slot_pos_after;
slot_pos_before = ndks_boot.slot_pos_cur;
/* create the PML4 */
pptr = alloc_region(seL4_PML4Bits);
if (!pptr) {
return cap_null_cap_new();
}
memzero(PML4_PTR(pptr), BIT(seL4_PML4Bits));
copyGlobalMappings(PML4_PTR(pptr));
vspace_cap = cap_pml4_cap_new(
IT_ASID, /* capPML4MappedASID */
pptr, /* capPML4BasePtr */
1 /* capPML4IsMapped */
);
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapInitThreadVSpace), vspace_cap);
/* Create any PDPTs needed for the user land image */
for (vptr = ROUND_DOWN(it_v_reg.start, PML4_INDEX_OFFSET);
vptr < it_v_reg.end;
vptr += BIT(PML4_INDEX_OFFSET)) {
pptr = alloc_region(seL4_PDPTBits);
if (!pptr) {
return cap_null_cap_new();
}
memzero(PDPT_PTR(pptr), BIT(seL4_PDPTBits));
if (!provide_cap(root_cnode_cap,
create_it_pdpt_cap(vspace_cap, pptr, vptr, IT_ASID))
) {
return cap_null_cap_new();
}
}
/* Create any PDs needed for the user land image */
for (vptr = ROUND_DOWN(it_v_reg.start, PDPT_INDEX_OFFSET);
vptr < it_v_reg.end;
vptr += BIT(PDPT_INDEX_OFFSET)) {
pptr = alloc_region(seL4_PageDirBits);
if (!pptr) {
return cap_null_cap_new();
}
memzero(PD_PTR(pptr), BIT(seL4_PageDirBits));
if (!provide_cap(root_cnode_cap,
create_it_pd_cap(vspace_cap, pptr, vptr, IT_ASID))
) {
return cap_null_cap_new();
}
}
/* Create any PTs needed for the user land image */
for (vptr = ROUND_DOWN(it_v_reg.start, PD_INDEX_OFFSET);
vptr < it_v_reg.end;
vptr += BIT(PD_INDEX_OFFSET)) {
pptr = alloc_region(seL4_PageTableBits);
if (!pptr) {
return cap_null_cap_new();
}
memzero(PT_PTR(pptr), BIT(seL4_PageTableBits));
if (!provide_cap(root_cnode_cap,
create_it_pt_cap(vspace_cap, pptr, vptr, IT_ASID))
) {
return cap_null_cap_new();
}
}
slot_pos_after = ndks_boot.slot_pos_cur;
ndks_boot.bi_frame->userImagePaging = (seL4_SlotRegion) {
slot_pos_before, slot_pos_after
};
return vspace_cap;
}
void copyGlobalMappings(vspace_root_t *new_vspace)
{
unsigned long i;
pml4e_t *vspace = (pml4e_t *)new_vspace;
for (i = GET_PML4_INDEX(PPTR_BASE); i < BIT(PML4_INDEX_BITS); i++) {
vspace[i] = x64KSGlobalPML4[i];
}
}
static BOOT_CODE cap_t
create_it_frame_cap(pptr_t pptr, vptr_t vptr, asid_t asid, bool_t use_large, seL4_Word map_type)
{
vm_page_size_t frame_size;
if (use_large) {
frame_size = X86_LargePage;
} else {
frame_size = X86_SmallPage;
}
return
cap_frame_cap_new(
asid, /* capFMappedASID */
pptr, /* capFBasePtr */
frame_size, /* capFSize */
map_type, /* capFMapType */
vptr, /* capFMappedAddress */
wordFromVMRights(VMReadWrite), /* capFVMRights */
0 /* capFIsDevice */
);
}
BOOT_CODE cap_t
create_unmapped_it_frame_cap(pptr_t pptr, bool_t use_large)
{
return create_it_frame_cap(pptr, 0, asidInvalid, use_large, X86_MappingNone);
}
BOOT_CODE cap_t
create_mapped_it_frame_cap(cap_t vspace_cap, pptr_t pptr, vptr_t vptr, asid_t asid, bool_t use_large, bool_t executable UNUSED)
{
cap_t cap = create_it_frame_cap(pptr, vptr, asid, use_large, X86_MappingVSpace);
map_it_frame_cap(vspace_cap, cap);
return cap;
}
/* ====================== BOOT CODE FINISHES HERE ======================== */
exception_t
performASIDPoolInvocation(asid_t asid, asid_pool_t *poolPtr, cte_t *vspaceCapSlot)
{
cap_pml4_cap_ptr_set_capPML4MappedASID(&vspaceCapSlot->cap, asid);
cap_pml4_cap_ptr_set_capPML4IsMapped(&vspaceCapSlot->cap, 1);
poolPtr->array[asid & MASK(asidLowBits)] = PML4_PTR(cap_pml4_cap_get_capPML4BasePtr(vspaceCapSlot->cap));
return EXCEPTION_NONE;
}
bool_t CONST
isVTableRoot(cap_t cap)
{
return cap_get_capType(cap) == cap_pml4_cap;
}
bool_t CONST
isValidNativeRoot(cap_t cap)
{
return isVTableRoot(cap) &&
cap_pml4_cap_get_capPML4IsMapped(cap);
}
static pdpte_t CONST
makeUserPDPTEHugePage(paddr_t paddr, vm_attributes_t vm_attr, vm_rights_t vm_rights)
{
return pdpte_pdpte_1g_new(
0, /* xd */
paddr, /* physical address */
0, /* PAT */
0, /* global */
0, /* dirty */
0, /* accessed */
vm_attributes_get_x86PCDBit(vm_attr), /* cache disabled */
vm_attributes_get_x86PWTBit(vm_attr), /* write through */
SuperUserFromVMRights(vm_rights), /* super user */
WritableFromVMRights(vm_rights), /* read write */
1 /* present */
);
}
static pdpte_t CONST
makeUserPDPTEHugePageInvalid(void)
{
return pdpte_pdpte_1g_new(
0, /* xd */
0, /* physical address */
0, /* PAT */
0, /* global */
0, /* dirty */
0, /* accessed */
0, /* cache disabled */
0, /* write through */
0, /* super user */
0, /* read write */
0 /* present */
);
}
pde_t CONST
makeUserPDELargePage(paddr_t paddr, vm_attributes_t vm_attr, vm_rights_t vm_rights)
{
return pde_pde_large_new(
0, /* xd */
paddr, /* page_base_address */
vm_attributes_get_x86PATBit(vm_attr), /* pat */
0, /* global */
0, /* dirty */
0, /* accessed */
vm_attributes_get_x86PCDBit(vm_attr), /* cache_disabled */
vm_attributes_get_x86PWTBit(vm_attr), /* write_through */
SuperUserFromVMRights(vm_rights), /* super_user */
WritableFromVMRights(vm_rights), /* read_write */
1 /* present */
);
}
pde_t CONST
makeUserPDEPageTable(paddr_t paddr, vm_attributes_t vm_attr)
{
return pde_pde_small_new(
0, /* xd */
paddr, /* pt_base_address */
0, /* accessed */
vm_attributes_get_x86PCDBit(vm_attr), /* cache_disabled */
vm_attributes_get_x86PWTBit(vm_attr), /* write_through */
1, /* super_user */
1, /* read_write */
1 /* present */
);
}
pde_t CONST
makeUserPDELargePageInvalid(void)
{
return pde_pde_large_new(
0, /* xd */
0, /* page_base_address */
0, /* pat */
0, /* global */
0, /* dirty */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
0, /* read_write */
0 /* present */
);
}
pde_t CONST
makeUserPDEPageTableInvalid(void)
{
return pde_pde_small_new(
0, /* xd */
0, /* pt_base_addr */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
0, /* read_write */
0 /* present */
);
}
pte_t CONST
makeUserPTE(paddr_t paddr, vm_attributes_t vm_attr, vm_rights_t vm_rights)
{
return pte_new(
0, /* xd */
paddr, /* page_base_address */
0, /* global */
vm_attributes_get_x86PATBit(vm_attr), /* pat */
0, /* dirty */
0, /* accessed */
vm_attributes_get_x86PCDBit(vm_attr), /* cache_disabled */
vm_attributes_get_x86PWTBit(vm_attr), /* write_through */
SuperUserFromVMRights(vm_rights), /* super_user */
WritableFromVMRights(vm_rights), /* read_write */
1 /* present */
);
}
pte_t CONST
makeUserPTEInvalid(void)
{
return pte_new(
0, /* xd */
0, /* page_base_address */
0, /* global */
0, /* pat */
0, /* dirty */
0, /* accessed */
0, /* cache_disabled */
0, /* write_through */
0, /* super_user */
0, /* read_write */
0 /* present */
);
}
static lookupPML4Slot_ret_t
lookupPML4Slot(vspace_root_t*pml4, vptr_t vptr)
{
lookupPML4Slot_ret_t ret;
pml4e_t *pml4e = PML4E_PTR(pml4);
word_t pml4Index = GET_PML4_INDEX(vptr);
ret.status = EXCEPTION_NONE;
ret.pml4Slot = pml4e + pml4Index;
return ret;
}
static lookupPDPTSlot_ret_t
lookupPDPTSlot(vspace_root_t *pml4, vptr_t vptr)
{
lookupPML4Slot_ret_t pml4Slot = lookupPML4Slot(pml4, vptr);
lookupPDPTSlot_ret_t ret;
if (pml4Slot.status != EXCEPTION_NONE) {
ret.pdptSlot = NULL;
ret.status = pml4Slot.status;
return ret;
}
if (!pml4e_ptr_get_present(pml4Slot.pml4Slot)) {
current_lookup_fault = lookup_fault_missing_capability_new(PML4_INDEX_OFFSET);
ret.pdptSlot = NULL;
ret.status = EXCEPTION_LOOKUP_FAULT;
return ret;
} else {
pdpte_t *pdpt;
pdpte_t *pdptSlot;
word_t pdptIndex = GET_PDPT_INDEX(vptr);
pdpt = paddr_to_pptr(pml4e_ptr_get_pdpt_base_address(pml4Slot.pml4Slot));
pdptSlot = pdpt + pdptIndex;
ret.status = EXCEPTION_NONE;
ret.pdptSlot = pdptSlot;
return ret;
}
}
lookupPDSlot_ret_t
lookupPDSlot(vspace_root_t *pml4, vptr_t vptr)
{
lookupPDPTSlot_ret_t pdptSlot;
lookupPDSlot_ret_t ret;
pdptSlot = lookupPDPTSlot(pml4, vptr);
if (pdptSlot.status != EXCEPTION_NONE) {
ret.pdSlot = NULL;
ret.status = pdptSlot.status;
return ret;
}
if ((pdpte_ptr_get_page_size(pdptSlot.pdptSlot) != pdpte_pdpte_pd) ||
!pdpte_pdpte_pd_ptr_get_present(pdptSlot.pdptSlot)) {
current_lookup_fault = lookup_fault_missing_capability_new(PDPT_INDEX_OFFSET);
ret.pdSlot = NULL;
ret.status = EXCEPTION_LOOKUP_FAULT;
return ret;
} else {
pde_t *pd;
pde_t *pdSlot;
word_t pdIndex = GET_PD_INDEX(vptr);
pd = paddr_to_pptr(pdpte_pdpte_pd_ptr_get_pd_base_address(pdptSlot.pdptSlot));
pdSlot = pd + pdIndex;
ret.status = EXCEPTION_NONE;
ret.pdSlot = pdSlot;
return ret;
}
}
static void
flushPD(vspace_root_t *vspace, word_t vptr, pde_t *pd, asid_t asid)
{
/* clearing the entire PCID vs flushing the virtual addresses
* one by one using invplg.
* choose the easy way, invalidate the PCID
*/
invalidatePCID(INVPCID_TYPE_SINGLE, (void*)0, asid);
}
static void
flushPDPT(vspace_root_t *vspace, word_t vptr, pdpte_t *pdpt, asid_t asid)
{
/* similar here */
invalidatePCID(INVPCID_TYPE_SINGLE, (void*)0, asid);
return;
}
void
hwASIDInvalidate(asid_t asid)
{
invalidatePCID(INVPCID_TYPE_SINGLE, 0, asid);
}
void
unmapPageDirectory(asid_t asid, vptr_t vaddr, pde_t *pd)
{
findVSpaceForASID_ret_t find_ret;
lookupPDPTSlot_ret_t lu_ret;
find_ret = findVSpaceForASID(asid);
if (find_ret.status != EXCEPTION_NONE) {
return;
}
lu_ret = lookupPDPTSlot(find_ret.vspace_root, vaddr);
if (lu_ret.status != EXCEPTION_NONE) {
return;
}
/* check if the PDPT has the PD */
if (! (pdpte_ptr_get_page_size(lu_ret.pdptSlot) == pdpte_pdpte_pd &&
pdpte_pdpte_pd_ptr_get_present(lu_ret.pdptSlot) &&
(pdpte_pdpte_pd_ptr_get_pd_base_address(lu_ret.pdptSlot) == pptr_to_paddr(pd)))) {
return;
}
flushPD(find_ret.vspace_root, vaddr, pd, asid);
*lu_ret.pdptSlot = pdpte_pdpte_pd_new(
0, /* xd */
0, /* paddr */
0, /* accessed */
0, /* cache disabled */
0, /* write through */
0, /* super user */
0, /* read write */
0 /* present */
);
invalidatePageStructureCacheASID(pptr_to_paddr(find_ret.vspace_root), asid);
}
static exception_t
performX64PageDirectoryInvocationUnmap(cap_t cap, cte_t *ctSlot)
{
if (cap_page_directory_cap_get_capPDIsMapped(cap)) {
pde_t *pd = PDE_PTR(cap_page_directory_cap_get_capPDBasePtr(cap));
unmapPageDirectory(
cap_page_directory_cap_get_capPDMappedASID(cap),
cap_page_directory_cap_get_capPDMappedAddress(cap),
pd
);
clearMemory((void *)pd, cap_get_capSizeBits(cap));
}
cap_page_directory_cap_ptr_set_capPDIsMapped(&(ctSlot->cap), 0);
return EXCEPTION_NONE;
}
static exception_t
performX64PageDirectoryInvocationMap(cap_t cap, cte_t *ctSlot, pdpte_t pdpte, pdpte_t *pdptSlot, vspace_root_t *vspace)
{
ctSlot->cap = cap;
*pdptSlot = pdpte;
invalidatePageStructureCacheASID(pptr_to_paddr(vspace), cap_page_directory_cap_get_capPDMappedASID(cap));
return EXCEPTION_NONE;
}
static exception_t
decodeX64PageDirectoryInvocation(
word_t label,
word_t length,
cte_t* cte,
cap_t cap,
extra_caps_t extraCaps,
word_t* buffer
)
{
word_t vaddr;
vm_attributes_t vm_attr;
cap_t vspaceCap;
vspace_root_t *vspace;
pdpte_t pdpte;
paddr_t paddr;
asid_t asid;
lookupPDPTSlot_ret_t pdptSlot;
if (label == X86PageDirectoryUnmap) {
if (!isFinalCapability(cte)) {
current_syscall_error.type = seL4_RevokeFirst;
userError("X86PageDirectory: Cannot unmap if more than one cap exist.");
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(ksCurThread, ThreadState_Restart);
return performX64PageDirectoryInvocationUnmap(cap, cte);
}
if (label != X86PageDirectoryMap) {
userError("X64Directory: Illegal operation.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
if (length < 2 || extraCaps.excaprefs[0] == NULL) {
userError("X64PageDirectory: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
if (cap_page_directory_cap_get_capPDIsMapped(cap)) {
userError("X64PageDirectory: PD is already mapped to a PML4.");
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
vaddr = getSyscallArg(0, buffer) & (~MASK(PDPT_INDEX_OFFSET));
vm_attr = vmAttributesFromWord(getSyscallArg(1, buffer));
vspaceCap = extraCaps.excaprefs[0]->cap;
if (!isValidNativeRoot(vspaceCap)) {
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 1;
return EXCEPTION_SYSCALL_ERROR;
}
vspace = (vspace_root_t *)pptr_of_cap(vspaceCap);
asid = cap_get_capMappedASID(vspaceCap);
if (vaddr >= PPTR_USER_TOP) {
userError("X64PageDirectory: Mapping address too high.");
current_syscall_error.type = seL4_InvalidArgument;
current_syscall_error.invalidArgumentNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
pdptSlot = lookupPDPTSlot(vspace, vaddr);
if (pdptSlot.status != EXCEPTION_NONE) {
current_syscall_error.type = seL4_FailedLookup;
current_syscall_error.failedLookupWasSource = false;
return EXCEPTION_SYSCALL_ERROR;
}
if ((pdpte_ptr_get_page_size(pdptSlot.pdptSlot) == pdpte_pdpte_pd &&
pdpte_pdpte_pd_ptr_get_present(pdptSlot.pdptSlot)) ||
(pdpte_ptr_get_page_size(pdptSlot.pdptSlot) == pdpte_pdpte_1g
&& pdpte_pdpte_1g_ptr_get_present(pdptSlot.pdptSlot))) {
current_syscall_error.type = seL4_DeleteFirst;
return EXCEPTION_SYSCALL_ERROR;
}
paddr = pptr_to_paddr(PDE_PTR(cap_page_directory_cap_get_capPDBasePtr(cap)));
pdpte = pdpte_pdpte_pd_new(
0, /* xd */
paddr, /* paddr */
0, /* accessed */
vm_attributes_get_x86PCDBit(vm_attr), /* cache disabled */
vm_attributes_get_x86PWTBit(vm_attr), /* write through */
1, /* super user */
1, /* read write */
1 /* present */
);
cap = cap_page_directory_cap_set_capPDIsMapped(cap, 1);
cap = cap_page_directory_cap_set_capPDMappedASID(cap, asid);
cap = cap_page_directory_cap_set_capPDMappedAddress(cap, vaddr);
setThreadState(ksCurThread, ThreadState_Restart);
return performX64PageDirectoryInvocationMap(cap, cte, pdpte, pdptSlot.pdptSlot, vspace);
}
static void unmapPDPT(asid_t asid, vptr_t vaddr, pdpte_t *pdpt)
{
findVSpaceForASID_ret_t find_ret;
lookupPML4Slot_ret_t lu_ret;
find_ret = findVSpaceForASID(asid);
if (find_ret.status != EXCEPTION_NONE) {
return;
}
lu_ret = lookupPML4Slot(find_ret.vspace_root, vaddr);
if (lu_ret.status != EXCEPTION_NONE) {
return;
}
/* check if the PML4 has the PDPT */
if (! (pml4e_ptr_get_present(lu_ret.pml4Slot) &&
pml4e_ptr_get_pdpt_base_address(lu_ret.pml4Slot) == pptr_to_paddr(pdpt))) {
return;
}
flushPDPT(find_ret.vspace_root, vaddr, pdpt, asid);
*lu_ret.pml4Slot = pml4e_new(
0, /* xd */
0, /* pdpt_base_addr */
0, /* accessed */
0, /* cache_disabled */
0, /* write through */
0, /* super user */
0, /* read_write */
0 /* present */
);
}
static exception_t
performX64PDPTInvocationUnmap(cap_t cap, cte_t *ctSlot)
{
if (cap_pdpt_cap_get_capPDPTIsMapped(cap)) {
pdpte_t *pdpt = PDPTE_PTR(cap_pdpt_cap_get_capPDPTBasePtr(cap));
unmapPDPT(cap_pdpt_cap_get_capPDPTMappedASID(cap),
cap_pdpt_cap_get_capPDPTMappedAddress(cap),
pdpt);
clearMemory((void *)pdpt, cap_get_capSizeBits(cap));
}
cap_pdpt_cap_ptr_set_capPDPTIsMapped(&(ctSlot->cap), 0);
return EXCEPTION_NONE;
}
static exception_t
performX64PDPTInvocationMap(cap_t cap, cte_t *ctSlot, pml4e_t pml4e, pml4e_t *pml4Slot, vspace_root_t *vspace)
{
ctSlot->cap = cap;
*pml4Slot = pml4e;
invalidatePageStructureCacheASID(pptr_to_paddr(vspace), cap_pdpt_cap_get_capPDPTMappedASID(cap));
return EXCEPTION_NONE;
}
static exception_t
decodeX64PDPTInvocation(
word_t label,
word_t length,
cte_t *cte,
cap_t cap,
extra_caps_t extraCaps,
word_t *buffer)
{
word_t vaddr;
vm_attributes_t attr;
lookupPML4Slot_ret_t pml4Slot;
cap_t vspaceCap;
vspace_root_t *vspace;
pml4e_t pml4e;
paddr_t paddr;
asid_t asid;
if (label == X86PDPTUnmap) {
if (!isFinalCapability(cte)) {
current_syscall_error.type = seL4_RevokeFirst;
userError("X86PDPT: Cannot unmap if more than one cap exist.");
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(ksCurThread, ThreadState_Restart);
return performX64PDPTInvocationUnmap(cap, cte);
}
if (label != X86PDPTMap) {
userError("X86PDPT: Illegal operation.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
if (length < 2 || extraCaps.excaprefs[0] == NULL) {
userError("X64PDPT: Truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
if (cap_pdpt_cap_get_capPDPTIsMapped(cap)) {
userError("X64PDPT: PDPT is already mapped to a PML4.");
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
vaddr = getSyscallArg(0, buffer) & (~MASK(PML4_INDEX_OFFSET));
attr = vmAttributesFromWord(getSyscallArg(1, buffer));
vspaceCap = extraCaps.excaprefs[0]->cap;
if (!isValidNativeRoot(vspaceCap)) {
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 1;
return EXCEPTION_SYSCALL_ERROR;
}
vspace = (vspace_root_t *)pptr_of_cap(vspaceCap);
asid = cap_get_capMappedASID(vspaceCap);
if (vaddr >= PPTR_USER_TOP) {
userError("X64PDPT: Mapping address too high.");
current_syscall_error.type = seL4_InvalidArgument;
current_syscall_error.invalidArgumentNumber = 0;
return EXCEPTION_SYSCALL_ERROR;
}
pml4Slot = lookupPML4Slot(vspace, vaddr);
if (pml4Slot.status != EXCEPTION_NONE) {
current_syscall_error.type = seL4_FailedLookup;
current_syscall_error.failedLookupWasSource = false;
return EXCEPTION_SYSCALL_ERROR;
}
if (pml4e_ptr_get_present(pml4Slot.pml4Slot)) {
current_syscall_error.type = seL4_DeleteFirst;
return EXCEPTION_SYSCALL_ERROR;
}
paddr = pptr_to_paddr(PDPTE_PTR((cap_pdpt_cap_get_capPDPTBasePtr(cap))));
pml4e = pml4e_new(
0,
paddr,
0,
vm_attributes_get_x86PCDBit(attr),
vm_attributes_get_x86PWTBit(attr),
1,
1,
1
);
cap = cap_pdpt_cap_set_capPDPTIsMapped(cap, 1);
cap = cap_pdpt_cap_set_capPDPTMappedASID(cap, asid);
cap = cap_pdpt_cap_set_capPDPTMappedAddress(cap, vaddr);
setThreadState(ksCurThread, ThreadState_Restart);
return performX64PDPTInvocationMap(cap, cte, pml4e, pml4Slot.pml4Slot, vspace);
}
exception_t
decodeX86ModeMMUInvocation(
word_t label,
word_t length,
cptr_t cptr,
cte_t* cte,
cap_t cap,
extra_caps_t extraCaps,
word_t* buffer
)
{
switch (cap_get_capType(cap)) {
case cap_pml4_cap:
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
case cap_pdpt_cap:
return decodeX64PDPTInvocation(label, length, cte, cap, extraCaps, buffer);
case cap_page_directory_cap:
return decodeX64PageDirectoryInvocation(label, length, cte, cap, extraCaps, buffer);
default:
fail("Invalid arch cap type");
}
}
void modeUnmapPage(vm_page_size_t page_size, vspace_root_t *vroot, vptr_t vaddr, void *pptr)
{
if (config_set(CONFIG_HUGE_PAGE) && page_size == X64_HugePage) {
pdpte_t *pdpte;
lookupPDPTSlot_ret_t pdpt_ret = lookupPDPTSlot(vroot, vaddr);
if (pdpt_ret.status != EXCEPTION_NONE) {
return;
}
pdpte = pdpt_ret.pdptSlot;
if (! (pdpte_ptr_get_page_size(pdpte) == pdpte_pdpte_1g
&& pdpte_pdpte_1g_ptr_get_present(pdpte)
&& (pdpte_pdpte_1g_ptr_get_page_base_address(pdpte)
== pptr_to_paddr(pptr)))) {
return;
}
*pdpte = makeUserPDPTEHugePageInvalid();
return;
}
fail("Invalid page type");
}
static exception_t
performX64ModeMapRemapPage(cap_t cap, cte_t *ctSlot, pdpte_t pdpte, pdpte_t *pdptSlot, vspace_root_t *vspace)
{
ctSlot->cap = cap;
*pdptSlot = pdpte;
invalidatePageStructureCacheASID(pptr_to_paddr(vspace), cap_frame_cap_get_capFMappedASID(cap));
return EXCEPTION_NONE;
}
exception_t
decodeX86ModeMapRemapPage(word_t label, vm_page_size_t page_size, cte_t *cte, cap_t cap, vspace_root_t *vroot, vptr_t vaddr, paddr_t paddr, vm_rights_t vm_rights, vm_attributes_t vm_attr)
{
if (config_set(CONFIG_HUGE_PAGE) && page_size == X64_HugePage) {
pdpte_t pdpte;
pdpte_t *pdptSlot;
lookupPDPTSlot_ret_t lu_ret;
lu_ret = lookupPDPTSlot(vroot, vaddr);
if (lu_ret.status != EXCEPTION_NONE) {
current_syscall_error.type = seL4_FailedLookup;
current_syscall_error.failedLookupWasSource = false;
return EXCEPTION_SYSCALL_ERROR;
}
pdptSlot = lu_ret.pdptSlot;
switch (label) {
case X86PageMap: {
/* check for existing page directory */
if ((pdpte_ptr_get_page_size(pdptSlot) == pdpte_pdpte_pd) &&
(pdpte_pdpte_pd_ptr_get_present(pdptSlot))) {
current_syscall_error.type = seL4_DeleteFirst;
return EXCEPTION_SYSCALL_ERROR;
}
/* check for existing huge page */
if ((pdpte_ptr_get_page_size(pdptSlot) == pdpte_pdpte_1g) &&
(pdpte_pdpte_1g_ptr_get_present(pdptSlot))) {
current_syscall_error.type = seL4_DeleteFirst;
return EXCEPTION_SYSCALL_ERROR;
}
pdpte = makeUserPDPTEHugePage(paddr, vm_attr, vm_rights);
setThreadState(ksCurThread, ThreadState_Restart);
return performX64ModeMapRemapPage(cap, cte, pdpte, pdptSlot, vroot);
}
case X86PageRemap: {
/* check for existing page directory */
if ((pdpte_ptr_get_page_size(pdptSlot) == pdpte_pdpte_pd) &&
(pdpte_pdpte_pd_ptr_get_present(pdptSlot))) {
current_syscall_error.type = seL4_DeleteFirst;
return EXCEPTION_SYSCALL_ERROR;
}
pdpte = makeUserPDPTEHugePage(paddr, vm_attr, vm_rights);
setThreadState(ksCurThread, ThreadState_Restart);
return performX64ModeMapRemapPage(cap, cte, pdpte, pdptSlot, vroot);
}
default: {
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
}
}
fail("Invalid Page type");
}
#ifdef CONFIG_PRINTING
typedef struct readWordFromVSpace_ret {
exception_t status;
word_t value;
} readWordFromVSpace_ret_t;
static readWordFromVSpace_ret_t
readWordFromVSpace(vspace_root_t *vspace, word_t vaddr)
{
readWordFromVSpace_ret_t ret;
lookupPTSlot_ret_t ptSlot;
lookupPDSlot_ret_t pdSlot;
lookupPDPTSlot_ret_t pdptSlot;
paddr_t paddr;
word_t offset;
pptr_t kernel_vaddr;
word_t *value;
pdptSlot = lookupPDPTSlot(vspace, vaddr);
if (pdptSlot.status == EXCEPTION_NONE &&
pdpte_ptr_get_page_size(pdptSlot.pdptSlot) == pdpte_pdpte_1g &&
pdpte_pdpte_1g_ptr_get_present(pdptSlot.pdptSlot)) {
paddr = pdpte_pdpte_1g_ptr_get_page_base_address(pdptSlot.pdptSlot);
offset = vaddr & MASK(seL4_HugePageBits);
} else {
pdSlot = lookupPDSlot(vspace, vaddr);
if (pdSlot.status == EXCEPTION_NONE &&
((pde_ptr_get_page_size(pdSlot.pdSlot) == pde_pde_large) &&
pde_pde_large_ptr_get_present(pdSlot.pdSlot))) {
paddr = pde_pde_large_ptr_get_page_base_address(pdSlot.pdSlot);
offset = vaddr & MASK(seL4_LargePageBits);
} else {
ptSlot = lookupPTSlot(vspace, vaddr);
if (ptSlot.status == EXCEPTION_NONE && pte_ptr_get_present(ptSlot.ptSlot)) {
paddr = pte_ptr_get_page_base_address(ptSlot.ptSlot);
offset = vaddr & MASK(seL4_PageBits);
} else {
ret.status = EXCEPTION_LOOKUP_FAULT;
return ret;
}
}
}
kernel_vaddr = (word_t)paddr_to_pptr(paddr);
value = (word_t*)(kernel_vaddr + offset);
ret.status = EXCEPTION_NONE;
ret.value = *value;
return ret;
}
void
Arch_userStackTrace(tcb_t *tptr)
{
cap_t threadRoot;
vspace_root_t *vspace_root;
word_t sp;
int i;
threadRoot = TCB_PTR_CTE_PTR(tptr, tcbVTable)->cap;
/* lookup the PD */
if (cap_get_capType(threadRoot) != cap_pml4_cap) {
printf("Invalid vspace\n");
return;
}
vspace_root = (vspace_root_t*)pptr_of_cap(threadRoot);
sp = getRegister(tptr, RSP);
/* check for alignment so we don't have to worry about accessing
* words that might be on two different pages */
if (!IS_ALIGNED(sp, WORD_SIZE_BITS)) {
printf("RSP not aligned\n");
return;
}
for (i = 0; i < CONFIG_USER_STACK_TRACE_LENGTH; i++) {
word_t address = sp + (i * sizeof(word_t));
readWordFromVSpace_ret_t result;
result = readWordFromVSpace(vspace_root, address);
if (result.status == EXCEPTION_NONE) {
printf("0x%lx: 0x%lx\n", (long)address, (long)result.value);
} else {
printf("0x%lx: INVALID\n", (long)address);
}
}
}
#endif /* CONFIG_PRINTING */