From 879d9724c13a3aac9d460ef28515f46569ae09e9 Mon Sep 17 00:00:00 2001 From: Yanyan Shen Date: Fri, 13 May 2016 16:42:53 +1000 Subject: [PATCH] arm/tk1: a checkpoint for SMMU implementation --- .../arm/arch/32/mode/object/structures.bf | 21 +- .../arch/arm/arch/32/mode/object/structures.h | 4 + include/arch/arm/arch/object/iospace.h | 28 ++ include/plat/tk1/plat/machine/devices.h | 2 + include/plat/tk1/plat/machine/smmu.h | 148 +++++++++ .../arch_include/arm/interfaces/sel4arch.xml | 13 + libsel4/arch_include/arm/sel4/arch/types.h | 3 + libsel4/include/sel4/bootinfo_types.h | 1 + libsel4/tools/syscall_stub_gen.py | 2 + src/arch/arm/32/kernel/vspace.c | 3 + src/arch/arm/32/object/objecttype.c | 6 +- src/arch/arm/kernel/boot.c | 4 + src/arch/arm/object/Makefile | 2 +- src/arch/arm/object/iospace.c | 299 ++++++++++++++++++ src/arch/arm/object/vcpu.c | 8 + src/plat/tk1/machine/Makefile | 3 +- src/plat/tk1/machine/hardware.c | 18 +- src/plat/tk1/machine/smmu.c | 206 ++++++++++++ 18 files changed, 760 insertions(+), 11 deletions(-) create mode 100644 include/arch/arm/arch/object/iospace.h create mode 100644 include/plat/tk1/plat/machine/smmu.h create mode 100644 src/arch/arm/object/iospace.c create mode 100644 src/plat/tk1/machine/smmu.c diff --git a/include/arch/arm/arch/32/mode/object/structures.bf b/include/arch/arm/arch/32/mode/object/structures.bf index 35e4ee304..771f166db 100644 --- a/include/arch/arm/arch/32/mode/object/structures.bf +++ b/include/arch/arm/arch/32/mode/object/structures.bf @@ -8,6 +8,7 @@ -- @TAG(GD_GPL) -- +#include -- Default base size: uint32_t base 32 @@ -25,7 +26,12 @@ block small_frame_cap { field capFVMRights 2 field_high capFMappedAddress 20 +#ifdef CONFIG_ARM_SMMU + field capFIsIOSpace 1 + field capFMappedASIDHigh 7 +#else field capFMappedASIDHigh 8 +#endif field_high capFBasePtr 20 field capType 4 } @@ -127,14 +133,15 @@ block io_page_directory_cap (capType, capIOPDIsMapped, capIOPDASID, capIOPDBaseP field capType 8 } -block io_page_table_cap (capType, capIOPTIsMapped, capIOPTASID, capIOPTBasePtr) { - field_high capIOPTBasePtr 20 - padding 12 +block io_page_table_cap (capType, capIOPTIsMapped, capIOPTASID, capIOPTBasePtr, capIOPTMappedAddress) { + field_high capIOPTBasePtr 20 + padding 12 - padding 16 - field capIOPTASID 7 - field capIOPTIsMapped 1 - field capType 8 + field_high capIOPTMappedAddress 10 + padding 6 + field capIOPTASID 7 + field capIOPTIsMapped 1 + field capType 8 } -- NB: odd numbers are arch caps (see isArchCap()) diff --git a/include/arch/arm/arch/32/mode/object/structures.h b/include/arch/arm/arch/32/mode/object/structures.h index 92fbe63d2..b561a4c5a 100644 --- a/include/arch/arm/arch/32/mode/object/structures.h +++ b/include/arch/arm/arch/32/mode/object/structures.h @@ -112,7 +112,11 @@ struct user_data { typedef struct user_data user_data_t; enum asidSizeConstants { +#ifdef CONFIG_ARM_SMMU + asidHighBits = 7, +#else asidHighBits = 8, +#endif asidLowBits = 10 }; diff --git a/include/arch/arm/arch/object/iospace.h b/include/arch/arm/arch/object/iospace.h new file mode 100644 index 000000000..09c26708e --- /dev/null +++ b/include/arch/arm/arch/object/iospace.h @@ -0,0 +1,28 @@ +/* + * Copyright 2016, General Dynamics C4 Systems + * + * 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(GD_GPL) + */ + +#ifndef __ARCH_OBJECT_IOSPACE_H +#define __ARCH_OBJECT_IOSPACE_H + +#include +#include +#include + + +seL4_SlotRegion create_iospace_caps(cap_t root_cnode_cap); +exception_t decodeARMIOPTInvocation(word_t invLabel, uint32_t length, cte_t* slot, cap_t cap, extra_caps_t excaps, word_t* buffer); +exception_t decodeARMIOMapInvocation(word_t invLabel, uint32_t length, cte_t* slot, cap_t cap, extra_caps_t excaps, word_t* buffer); +exception_t decodeARMIOUnMapInvocation(word_t invLabel, uint32_t length, cte_t* slot, cap_t cap, extra_caps_t excaps); +exception_t decodeARMIOSpaceInvocation(word_t invLabel, cap_t cap); +void unmapIOPage(cap_t cap); +void deleteIOPageTable(cap_t cap); + +#endif + diff --git a/include/plat/tk1/plat/machine/devices.h b/include/plat/tk1/plat/machine/devices.h index c24d5569e..a8973fbc2 100644 --- a/include/plat/tk1/plat/machine/devices.h +++ b/include/plat/tk1/plat/machine/devices.h @@ -21,6 +21,8 @@ #define GIC_CONTROLLER_PPTR 0xfff04000 /* HYP mode kernel devices */ #define GIC_VCPUCTRL_PPTR 0xfff06000 +/* SMMU registers */ +#define SMMU_PPTR 0Xfff07000 #define GIC_PL390_CONTROLLER_PPTR GIC_CONTROLLER_PPTR #define GIC_PL390_DISTRIBUTOR_PPTR GIC_DISTRIBUTOR_PPTR diff --git a/include/plat/tk1/plat/machine/smmu.h b/include/plat/tk1/plat/machine/smmu.h new file mode 100644 index 000000000..a4b1e9187 --- /dev/null +++ b/include/plat/tk1/plat/machine/smmu.h @@ -0,0 +1,148 @@ +/* + * Copyright 2016, General Dynamics C4 Systems + * + * 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(GD_GPL) + */ + +#ifndef __PLAT_SMMU_H +#define __PLAT_SMMU_H + +#include + +#define IOASID_SIZE_BITS 7 + + + +/* The SystemMMU control registers are part of memory controller */ + +typedef struct { + uint32_t intstatus; /* 0x00 */ + uint32_t intmask; /* 0x04 */ + uint32_t err_status; /* 0x08 */ + uint32_t err_adr; /* 0x0c */ + uint32_t smmu_config; /* 0x10 */ + uint32_t smmu_tlb_config; /* 0x14 */ + uint32_t smmu_ptc_config; /* 0x18 */ + uint32_t smmu_ptb_asid; /* 0x1c */ + uint32_t smmu_ptb_data; /* 0x20 */ + uint32_t rev0; /* 0x24 */ + uint32_t rev1; /* 0x28 */ + uint32_t rev2; /* 0x2c */ + uint32_t smmu_tlb_flush; /* 0x30 */ + uint32_t smmu_ptc_flush; /* 0x34 */ + uint32_t rev3[124]; + uint32_t smmu_translation_enable_0; /* 0x228 */ + uint32_t smmu_translation_enable_1; /* 0x22c */ + uint32_t smmu_translation_enable_2; /* 0x230 */ + uint32_t smmu_translation_enable_3; /* 0x234 */ + uint32_t smmu_afi_asid; /* 0x238 */ + uint32_t smmu_avpc_asid; /* 0x23c */ + uint32_t smmu_dc_asid; /* 0x240 */ + uint32_t smmu_dcb_asid; /* 0x244 */ + uint32_t rev4; /* 0x248 */ + uint32_t rev5; /* 0x24c */ + uint32_t smmu_hc_asid; /* 0x250 */ + uint32_t smmu_hda_asid; /* 0x254 */ + uint32_t smmu_isp2_asid; /* 0x258 */ + uint32_t rev6; /* 0x25c */ + uint32_t rev7; /* 0x260 */ + uint32_t smmu_msenc_asid; /* 0x264 */ + uint32_t smmu_nv_asid; /* 0x268 */ + uint32_t smmu_nv2_asid; /* 0x26c */ + uint32_t smmu_ppcs_asid; /* 0x270 */ + uint32_t smmu_sata_asid; /* 0x274 */ + uint32_t smmu_vde_asid; /* 0x27c */ + uint32_t smmu_vi_asid; /* 0x280 */ + uint32_t smmu_vic_asid; /* 0x284 */ + uint32_t smmu_xusb_host_asid; /* 0x288 */ + uint32_t smmu_xusb_dev_asid; /* 0x28c */ + uint32_t rev8; /* 0x290 */ + uint32_t smmu_tsec_asid; /* 0x294 */ + uint32_t smmu_ppcs1_asid; /* 0x298 */ + uint32_t rev9[217]; + uint32_t smmu_tlb_set_sel_mask; /* 0x600 */ + uint32_t rev10[237]; + uint32_t smmu_ptc_flush_1; /* 0x9b8 */ + uint32_t rev11[51]; + uint32_t smmu_dc1_asid; /* 0xa88 */ + uint32_t rev12; /* 0xa8c */ + uint32_t rev13; /* 0xa90 */ + uint32_t smmu_sdmmc1a_asid; /* 0xa94 */ + uint32_t smmu_sdmmc2a_asid; /* 0xa98 */ + uint32_t smmu_sdmmc3a_asid; /* 0xa9c */ + uint32_t smmu_sdmmc4a_asid; /* 0xaa0 */ + uint32_t smmu_isp2b_asid; /* 0xaa4 */ + uint32_t smmu_gpu_asid; /* 0xaa8 */ + uint32_t smmu_gpub_asid; /* 0xaac */ + uint32_t smmu_ppcs2_asid; /* 0xab0 */ +} tk1_mc_regs_t; + +/* we start to allocate IO ASIDs from 1, and each module's ASID + * is fixed (i.e. users are not allowed to dynamically allocate + * ASIDs and assign them to devices). + */ +#define SMMU_FIRST_ASID 1 +#define SMMU_AFI_ASID 1 +#define SMMU_AVPC_ASID 2 +#define SMMU_DC_ASID 3 +#define SMMU_DCB_ASID 4 +#define SMMU_HC_ASID 5 +#define SMMU_HDA_ASID 6 +#define SMMU_ISP2_ASID 7 +#define SMMU_MSENC_ASID 8 +#define SMMU_NV_ASID 9 +#define SMMU_NV2_ASID 10 +#define SMMU_PPCS_ASID 11 +#define SMMU_SATA_ASID 12 +#define SMMU_VDE_ASID 13 +#define SMMU_VI_ASID 14 +#define SMMU_VIC_ASID 15 +#define SMMU_XUSB_HOST_ASID 16 +#define SMMU_XUSB_DEV_ASID 17 +#define SMMU_TSEC_ASID 18 +#define SMMU_PPCS1_ASID 19 +#define SMMU_DC1_ASID 20 +#define SMMU_SDMMC1A_ASID 21 +#define SMMU_SDMMC2A_ASID 22 +#define SMMU_SDMMC3A_ASID 23 +#define SMMU_SDMMC4A_ASID 24 +#define SMMU_ISP2B_ASID 25 +#define SMMU_GPU_ASID 26 +#define SMMU_GPUB_ASID 27 +#define SMMU_PPCS2_ASID 28 +#define SMMU_LAST_ASID 28 + +#define ARM_PLAT_NUM_SMMU 28 + +#define SMMU_PD_BITS 12 +#define SMMU_PT_BITS 12 + +#define SMMU_IOPD_INDEX_MASK 0xffc00000 +#define SMMU_IOPD_INDEX_SHIFT 20 +#define SMMU_IOPT_INDEX_MASK 0x3ff000 +#define SMMU_IOPT_INDEX_SHIFT 12 + +inline static uint32_t +plat_smmu_iopd_index(word_t io_address) +{ + uint32_t ret = (io_address & SMMU_IOPD_INDEX_MASK) >> SMMU_IOPD_INDEX_SHIFT; + return ret; +} + +inline static uint32_t +plat_smmu_iopt_index(word_t io_address) +{ + uint32_t ret = (io_address & SMMU_IOPT_INDEX_MASK) >> SMMU_IOPT_INDEX_SHIFT; + return ret; +} + +int plat_smmu_init(void); +void plat_smmu_tlb_flush_all(void); +void plat_smmu_ptc_flush_all(void); +iopde_t *plat_smmu_lookup_iopd_by_asid(uint32_t asid); + +#endif diff --git a/libsel4/arch_include/arm/interfaces/sel4arch.xml b/libsel4/arch_include/arm/interfaces/sel4arch.xml index 6c3ed354a..a1575aac6 100644 --- a/libsel4/arch_include/arm/interfaces/sel4arch.xml +++ b/libsel4/arch_include/arm/interfaces/sel4arch.xml @@ -18,6 +18,14 @@ + + + + + + + + @@ -31,6 +39,11 @@ + + + + + diff --git a/libsel4/arch_include/arm/sel4/arch/types.h b/libsel4/arch_include/arm/sel4/arch/types.h index 2b7019124..8e3316913 100644 --- a/libsel4/arch_include/arm/sel4/arch/types.h +++ b/libsel4/arch_include/arm/sel4/arch/types.h @@ -20,5 +20,8 @@ typedef seL4_CPtr seL4_ARM_PageDirectory; typedef seL4_CPtr seL4_ARM_ASIDControl; typedef seL4_CPtr seL4_ARM_ASIDPool; typedef seL4_CPtr seL4_ARM_VCPU; +typedef seL4_CPtr seL4_ARM_IOSpace; +typedef seL4_CPtr seL4_ARM_IOPageTable; + #endif /* __ARCH_SEL4TYPES_H__ */ diff --git a/libsel4/include/sel4/bootinfo_types.h b/libsel4/include/sel4/bootinfo_types.h index 5b302022f..06ae109c0 100644 --- a/libsel4/include/sel4/bootinfo_types.h +++ b/libsel4/include/sel4/bootinfo_types.h @@ -57,6 +57,7 @@ typedef struct { seL4_SlotRegion userImageFrames; /* userland-image frame caps */ seL4_SlotRegion userImagePaging; /* userland-image paging structure caps */ seL4_SlotRegion untyped; /* untyped-object caps (untyped caps) */ + seL4_SlotRegion ioSpaceCaps; /* IOSpace caps for ARM SMMU */ seL4_PAddr untypedPaddrList [CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS]; /* physical address of each untyped cap */ seL4_Uint8 untypedSizeBitsList[CONFIG_MAX_NUM_BOOTINFO_UNTYPED_CAPS]; /* size (2^n) bytes of each untyped cap */ seL4_Uint8 initThreadCNodeSizeBits; /* initial thread's root CNode size (2^n slots) */ diff --git a/libsel4/tools/syscall_stub_gen.py b/libsel4/tools/syscall_stub_gen.py index 5327a750f..a7af603f2 100644 --- a/libsel4/tools/syscall_stub_gen.py +++ b/libsel4/tools/syscall_stub_gen.py @@ -242,6 +242,8 @@ def InitTypes(): CapType("seL4_ARM_ASIDControl"), CapType("seL4_ARM_ASIDPool"), CapType("seL4_ARM_VCPU"), + CapType("seL4_ARM_IOSpace"), + CapType("seL4_ARM_IOPageTable"), StructType("seL4_UserContext", WORD_SIZE_BITS * 17), ], diff --git a/src/arch/arm/32/kernel/vspace.c b/src/arch/arm/32/kernel/vspace.c index ebdc40cc9..9a51df9fe 100644 --- a/src/arch/arm/32/kernel/vspace.c +++ b/src/arch/arm/32/kernel/vspace.c @@ -513,6 +513,9 @@ create_it_frame_cap(pptr_t pptr, vptr_t vptr, asid_t asid, bool_t use_large) ASID_LOW(asid), /* capFMappedASIDLow */ wordFromVMRights(VMReadWrite), /* capFVMRights */ vptr, /* capFMappedAddress */ +#ifdef CONFIG_ARM_SMMU + 0, /* IOSpace */ +#endif ASID_HIGH(asid), /* capFMappedASIDHigh */ pptr /* capFBasePtr */ ); diff --git a/src/arch/arm/32/object/objecttype.c b/src/arch/arm/32/object/objecttype.c index 0cac096b4..08dc69477 100644 --- a/src/arch/arm/32/object/objecttype.c +++ b/src/arch/arm/32/object/objecttype.c @@ -417,7 +417,11 @@ Arch_createObject(object_t t, void *regionBase, word_t userSize) return cap_small_frame_cap_new( ASID_LOW(asidInvalid), VMReadWrite, - 0, ASID_HIGH(asidInvalid), + 0, +#ifdef CONFIG_ARM_SMMU + 0, +#endif + ASID_HIGH(asidInvalid), (word_t)regionBase); case seL4_ARM_LargePageObject: diff --git a/src/arch/arm/kernel/boot.c b/src/arch/arm/kernel/boot.c index b7296891e..8d45b5ce1 100644 --- a/src/arch/arm/kernel/boot.c +++ b/src/arch/arm/kernel/boot.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -234,6 +235,9 @@ try_init_kernel( return false; } + if (config_set(CONFIG_ARM_SMMU)) { + create_iospace_caps(root_cnode_cap); + } /* Construct an initial address space with enough virtual addresses * to cover the user image + ipc buffer and bootinfo frames */ it_pd_cap = create_it_address_space(root_cnode_cap, it_v_reg); diff --git a/src/arch/arm/object/Makefile b/src/arch/arm/object/Makefile index 3c259eb96..13aa54560 100644 --- a/src/arch/arm/object/Makefile +++ b/src/arch/arm/object/Makefile @@ -10,7 +10,7 @@ DIRECTORIES += src/arch/arm/object -ARCH_C_SOURCES += object/interrupt.c object/tcb.c +ARCH_C_SOURCES += object/interrupt.c object/tcb.c object/iospace.c ifneq (ARM_HYP,) ARCH_C_SOURCES += object/vcpu.c diff --git a/src/arch/arm/object/iospace.c b/src/arch/arm/object/iospace.c new file mode 100644 index 000000000..820ee7905 --- /dev/null +++ b/src/arch/arm/object/iospace.c @@ -0,0 +1,299 @@ +/* + * Copyright 2016, General Dynamics C4 Systems + * + * 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(GD_GPL) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct lookupIOPDSlot_ret { + exception_t status; + iopde_t *iopdSlot; +} lookupIOPDSlot_ret_t; + +typedef struct lookupIOPTSlot_ret { + exception_t status; + iopte_t *ioptSlot; +} lookupIOPTSlot_ret_t; + +static lookupIOPDSlot_ret_t +lookupIOPDSlot(iopde_t *iopd, word_t io_address) +{ + lookupIOPDSlot_ret_t ret; + uint32_t index = plat_smmu_iopd_index(io_address); + ret.status = EXCEPTION_NONE; + ret.iopdSlot = iopd + index; + return ret; +} + +static lookupIOPTSlot_ret_t +lookupIOPTSlot(iopde_t *iopd, word_t io_address) +{ + lookupIOPTSlot_ret_t pt_ret; + uint32_t index; + iopte_t *pt; + + lookupIOPDSlot_ret_t pd_ret = lookupIOPDSlot(iopd, io_address); + if (pd_ret.status != EXCEPTION_NONE) { + pt_ret.status = EXCEPTION_LOOKUP_FAULT; + pt_ret.ioptSlot = 0; + return pt_ret; + } + index = plat_smmu_iopt_index(io_address); + pt = (iopte_t *)paddr_to_pptr(iopde_iopde_pt_ptr_get_address(pd_ret.iopdSlot)); + pt_ret.status = EXCEPTION_NONE; + pt_ret.ioptSlot = pt + index; + return pt_ret; +} + +BOOT_CODE seL4_SlotRegion +create_iospace_caps(cap_t root_cnode_cap) +{ + seL4_SlotPos start = ndks_boot.slot_pos_cur; + seL4_SlotPos end = 0; + cap_t io_space_cap; + int i = 0; + int num_smmu = plat_smmu_init(); + + if (num_smmu == 0) { + printf("SMMU init failuer\n"); + return (seL4_SlotRegion) S_REG_EMPTY; + } + + for (i = 0; i < num_smmu; i++) { + io_space_cap = cap_io_space_cap_new(i, i); + if (!provide_cap(root_cnode_cap, io_space_cap)) { + return (seL4_SlotRegion) S_REG_EMPTY; + } + } + return (seL4_SlotRegion) {start, end}; +} + + +exception_t +decodeARMIOPTInvocation( + word_t invLabel, + uint32_t length, + cte_t* slot, + cap_t cap, + extra_caps_t excaps, + word_t* buffer +) +{ + cap_t io_space; + word_t io_address; + uint16_t module_id; + (void)io_address; + + if (invLabel == ARMIOPageTableUnmap) { + deleteIOPageTable(slot->cap); + slot->cap = cap_io_page_table_cap_set_capIOPTIsMapped(slot->cap, 0); + + setThreadState(ksCurThread, ThreadState_Restart); + return EXCEPTION_NONE; + } + + if (excaps.excaprefs[0] == NULL || length < 1) { + current_syscall_error.type = seL4_TruncatedMessage; + return EXCEPTION_SYSCALL_ERROR; + } + + if (invLabel != ARMIOPageTableMap ) { + current_syscall_error.type = seL4_IllegalOperation; + return EXCEPTION_SYSCALL_ERROR; + } + + io_space = excaps.excaprefs[0]->cap; + io_address = getSyscallArg(0, buffer) & ~MASK(PAGE_BITS); + + if (cap_io_page_table_cap_get_capIOPTIsMapped(cap)) { + current_syscall_error.type = seL4_InvalidCapability; + current_syscall_error.invalidCapNumber = 0; + return EXCEPTION_SYSCALL_ERROR; + } + + if (cap_get_capType(io_space) != cap_io_space_cap) { + current_syscall_error.type = seL4_InvalidCapability; + current_syscall_error.invalidCapNumber = 0; + return EXCEPTION_SYSCALL_ERROR; + } + + module_id= cap_io_space_cap_get_capModuleID(io_space); + if (module_id == asidInvalid) { + current_syscall_error.type = seL4_InvalidCapability; + current_syscall_error.invalidCapNumber = 0; + + return EXCEPTION_SYSCALL_ERROR; + } + + + slot->cap = cap; + + setThreadState(ksCurThread, ThreadState_Restart); + return EXCEPTION_NONE; +} + +exception_t +decodeARMIOMapInvocation( + word_t invLabel, + uint32_t length, + cte_t* slot, + cap_t cap, + extra_caps_t excaps, + word_t* buffer +) +{ + cap_t io_space; + paddr_t io_address; + paddr_t paddr; + uint32_t module_id; + (void)paddr; + (void)io_address; + + if (excaps.excaprefs[0] == NULL || length < 2) { + current_syscall_error.type = seL4_TruncatedMessage; + return EXCEPTION_SYSCALL_ERROR; + } + + if (cap_frame_cap_get_capFSize(cap) != ARMSmallPage) { + current_syscall_error.type = seL4_InvalidCapability; + current_syscall_error.invalidCapNumber = 0; + return EXCEPTION_SYSCALL_ERROR; + } + + if (cap_frame_cap_get_capFMappedASID(cap) != asidInvalid) { + current_syscall_error.type = seL4_InvalidCapability; + current_syscall_error.invalidCapNumber = 0; + return EXCEPTION_SYSCALL_ERROR; + } + + io_space = excaps.excaprefs[0]->cap; + io_address = getSyscallArg(1, buffer) & ~MASK(PAGE_BITS); + paddr = pptr_to_paddr((void*)cap_frame_cap_get_capFBasePtr(cap)); + + if (cap_get_capType(io_space) != cap_io_space_cap) { + current_syscall_error.type = seL4_InvalidCapability; + current_syscall_error.invalidCapNumber = 0; + return EXCEPTION_SYSCALL_ERROR; + } + + module_id = cap_io_space_cap_get_capModuleID(io_space); + + if (module_id == asidInvalid) { + current_syscall_error.type = seL4_InvalidCapability; + current_syscall_error.invalidCapNumber = 0; + return EXCEPTION_SYSCALL_ERROR; + } + + slot->cap = cap; + + setThreadState(ksCurThread, ThreadState_Restart); + return EXCEPTION_NONE; +} + +void deleteIOPageTable(cap_t io_pt_cap) +{ + + uint32_t asid; + iopde_t *pd; + lookupIOPDSlot_ret_t lu_ret; + word_t io_address; + if (cap_io_page_table_cap_get_capIOPTIsMapped(io_pt_cap)) { + io_pt_cap = cap_io_page_table_cap_set_capIOPTIsMapped(io_pt_cap, 0); + asid = cap_io_page_table_cap_get_capIOPTASID(io_pt_cap); + pd = (iopde_t *)plat_smmu_lookup_iopd_by_asid(asid); + io_address = cap_io_page_table_cap_get_capIOPTMappedAddress(io_pt_cap); + + if (pd == 0) { + } + + lu_ret = lookupIOPDSlot(pd, io_address); + if (lu_ret.status != EXCEPTION_NONE) { + + } + + if (iopde_iopde_pt_ptr_get_address(lu_ret.iopdSlot) != cap_io_page_table_cap_get_capIOPTBasePtr(io_pt_cap)) { + return; + } + + iopde_iopde_pt_ptr_new(lu_ret.iopdSlot, 0, 0, 0, 0); + cleanCacheRange_RAM((word_t)lu_ret.iopdSlot, + ((word_t)lu_ret.iopdSlot) + sizeof(iopde_t), + addrFromPPtr(lu_ret.iopdSlot)); + + + /* TODO flush by address and asid */ + plat_smmu_tlb_flush_all(); + plat_smmu_ptc_flush_all(); + } +} + +void unmapIOPage(cap_t cap) +{ + lookupIOPTSlot_ret_t lu_ret; + iopde_t *pd; + word_t io_address; + uint32_t asid; + + io_address = cap_frame_cap_get_capFMappedAddress(cap); + asid = cap_frame_cap_get_capFMappedASID(cap); + pd = (iopde_t *)plat_smmu_lookup_iopd_by_asid(asid); + + if (pd == 0) { + return; + } + + lu_ret = lookupIOPTSlot(pd, io_address); + + if (lu_ret.status != EXCEPTION_NONE) { + return; + } + if (iopte_ptr_get_address(lu_ret.ioptSlot) != cap_frame_cap_get_capFBasePtr(cap)) { + return; + } + + iopte_ptr_new(lu_ret.ioptSlot, 0, 0, 0, 0); + cleanCacheRange_RAM((word_t)lu_ret.ioptSlot, + ((word_t)lu_ret.ioptSlot) + sizeof(iopte_t), + addrFromPPtr(lu_ret.ioptSlot)); + + plat_smmu_tlb_flush_all(); + plat_smmu_ptc_flush_all(); + return; +} + +exception_t +decodeARMIOUnMapInvocation( + word_t invLabel, + uint32_t length, + cte_t* slot, + cap_t cap, + extra_caps_t excaps +) +{ + setThreadState(ksCurThread, ThreadState_Restart); + return EXCEPTION_NONE; +} + +exception_t decodeARMIOSpaceInvocation(word_t invLabel, cap_t cap) +{ + userError("IOSpace capability has no invocations"); + current_syscall_error.type = seL4_IllegalOperation; + return EXCEPTION_SYSCALL_ERROR; +} + diff --git a/src/arch/arm/object/vcpu.c b/src/arch/arm/object/vcpu.c index ae9343812..6f3287e46 100644 --- a/src/arch/arm/object/vcpu.c +++ b/src/arch/arm/object/vcpu.c @@ -43,9 +43,17 @@ #define HCR_SWIO BIT( 1) /* set/way invalidate override */ #define HCR_VM BIT( 0) /* Virtualization MMU enable */ +#ifdef CONFIG_ARM_SMMU +/* Trap WFI/WFE/SMC and override CPSR.AIF */ +#define HCR_COMMON ( HCR_TWE | HCR_TWI | HCR_AMO | HCR_IMO \ + | HCR_FMO | HCR_DC | HCR_VM) +#else + /* Trap WFI/WFE/SMC and override CPSR.AIF */ #define HCR_COMMON ( HCR_TSC | HCR_TWE | HCR_TWI | HCR_AMO | HCR_IMO \ | HCR_FMO | HCR_DC | HCR_VM) + +#endif /* Allow native tasks to run at PL1, but restrict access */ #define HCR_NATIVE ( HCR_COMMON | HCR_TGE | HCR_TVM | HCR_TTLB | HCR_TCACHE \ | HCR_TAC | HCR_SWIO) diff --git a/src/plat/tk1/machine/Makefile b/src/plat/tk1/machine/Makefile index 1af784eaf..346ecd7ad 100644 --- a/src/plat/tk1/machine/Makefile +++ b/src/plat/tk1/machine/Makefile @@ -11,7 +11,8 @@ DIRECTORIES += src/plat/$(PLAT)/machine PLAT_C_SOURCES += machine/hardware.c \ - machine/l2cache.c + machine/l2cache.c \ + machine/smmu.c ifdef DEBUG PLAT_C_SOURCES += machine/io.c diff --git a/src/plat/tk1/machine/hardware.c b/src/plat/tk1/machine/hardware.c index ef2063dfb..dc43fec1f 100644 --- a/src/plat/tk1/machine/hardware.c +++ b/src/plat/tk1/machine/hardware.c @@ -23,7 +23,8 @@ const p_region_t BOOT_RODATA avail_p_regs[] = { // { .start = 0x80000000, .end = 0xf0000000 } - { .start = 0x80000000, .end = 0xb0000000 } + //{ .start = 0x80000000, .end = 0xb0000000 } + { .start = 0x80000000, .end = 0xa7f00000 } }; BOOT_CODE int get_num_avail_p_regs(void) @@ -81,7 +82,9 @@ const p_region_t BOOT_RODATA dev_p_regs[] = { { TSENSOR_PADDR, TSENSOR_PADDR + PAGE_SIZE }, /* 4 KB */ { CEC_PADDR, CEC_PADDR + PAGE_SIZE }, /* 4 KB */ { ATOMICS_PADDR, ATOMICS_PADDR + (PAGE_SIZE * 2) }, /* 8 KB */ +#ifndef CONFIG_ARM_SMMU { MC_PADDR, MC_PADDR + PAGE_SIZE }, /* 4 KB */ +#endif { EMC_PADDR, EMC_PADDR + PAGE_SIZE }, /* 4 KB */ { SATA_PADDR, SATA_PADDR + (PAGE_SIZE * 16) }, /* 64 KB */ { HDA_PADDR, HDA_PADDR + (PAGE_SIZE * 16) }, /* 64 KB */ @@ -178,6 +181,19 @@ map_kernel_devices(void) ); } + if (config_set(CONFIG_ARM_SMMU)) { + map_kernel_frame( + MC_PADDR, + SMMU_PPTR, + VMKernelOnly, + vm_attributes_new( + false, + false, + false + ) + ); + } + #if defined DEBUG || defined RELEASE_PRINTF /* map kernel device: UART */ map_kernel_frame( diff --git a/src/plat/tk1/machine/smmu.c b/src/plat/tk1/machine/smmu.c new file mode 100644 index 000000000..ab405b36e --- /dev/null +++ b/src/plat/tk1/machine/smmu.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include + + +static volatile tk1_mc_regs_t *smmu_regs = (volatile tk1_mc_regs_t *)(SMMU_PPTR); + + +void printkk(void); +void +printkk(void) +{ + volatile uint32_t *reg = (volatile uint32_t *)(0x70006300); + int i = 'a'; + for (; i < 'z'; i++) { + *reg = i; + } +} + +#define SMMU_CONFIG_OFFSET 0x10 + +static void +__smmu_enable(void) +{ + volatile uint32_t *config = (volatile uint32_t *)(MC_PADDR + SMMU_CONFIG_OFFSET); + *config = 1; +} + +static void +__smmu_disable(void) +{ + volatile uint32_t *config = (volatile uint32_t *)(MC_PADDR + SMMU_CONFIG_OFFSET); + *config = 0; +} + + +static inline void +smmu_disable(void) +{ + /* we need physical address here */ + uint32_t addr = (uint32_t)&__smmu_disable; + addr -= 0x60000000; + asm (".arch_extension sec\n"); + asm volatile ("mov r0, %0\n\t" + "dsb\nisb\n" + "smc #0\n" + ::"r"(addr)); + + return; +} + +static inline void +smmu_enable(void) +{ + uint32_t addr = (uint32_t)&__smmu_enable; + addr -= 0x60000000; + asm (".arch_extension sec\n"); + asm volatile ("mov r0, %0\n\t" + "dsb\nisb\n" + "smc #0\n" + ::"r"(addr)); + + return; +} + + +#define PTB_DATA_BASE_SHIFT 12 +#define PTB_DATA_READ (1ul << 31) +#define PTB_DATA_WRITE (1ul << 30) +#define PTB_DATA_NONSECURE (1ul << 29) +#define PTB_DATA_BASE_PD_MASK 0x3fffff + +#define MODULE_ASID_ENABLE (1ul << 31) + +static uint32_t +make_ptb_data(uint32_t pd_base, bool_t read, bool_t write, bool_t nonsecure) +{ + uint32_t ret = 0; + ret = (pd_base >> PTB_DATA_BASE_SHIFT); + + if (read) { + ret |= PTB_DATA_READ; + } + if (write) { + ret |= PTB_DATA_WRITE; + } + if (nonsecure) { + ret |= PTB_DATA_NONSECURE; + } + + return ret; +} + +static uint32_t +ptb_data_get_pd_base(uint32_t data) +{ + uint32_t ret = data; + ret &= PTB_DATA_BASE_PD_MASK; + ret <<= PTB_DATA_BASE_SHIFT; + return ret; +} + +#define PTC_FLUSH_ALL 0 +#define PTC_FLUSH_ADR 1 + +void +plat_smmu_ptc_flush_all(void) +{ + uint32_t cmd = PTC_FLUSH_ALL; + smmu_regs->smmu_ptc_flush = cmd; +} + +#define TLB_ASID_MATCH (1ul << 31) +#define TLB_FLUSH_ALL (0) +#define TLB_FLUSH_SECTION (2) +#define TLB_FLUSH_GROUP (3) + +void +plat_smmu_tlb_flush_all(void) +{ + uint32_t cmd = TLB_FLUSH_ALL; + smmu_regs->smmu_tlb_flush = cmd; +} + +BOOT_CODE int +plat_smmu_init(void) +{ + int asid = 1; + int i = 0; + (void) (smmu_regs); + (void) (asid); + smmu_disable(); + printf("smmu disabled\n"); + + for (i = 0; i < ARM_PLAT_NUM_SMMU; i++) { + iopde_t *pd = (iopde_t *)alloc_region(SMMU_PD_BITS); + if (pd == 0) { + printf("Failed to allocate SMMU IOPageDirectory for ASID %d\n", asid); + return 0; + } + memset(pd, 0, BIT(SMMU_PD_BITS)); + smmu_regs->smmu_ptb_asid = asid; + /* make it read/write/nonsecure but all translation entries are invalid */ + smmu_regs->smmu_ptb_data = make_ptb_data(pptr_to_paddr(pd), true, true, true); + asid++; + } + printf("Total %d IOASID set up\n", (asid - 1)); + + /* now assign IOASID to each module */ + smmu_regs->smmu_afi_asid = SMMU_AFI_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_avpc_asid = SMMU_AVPC_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_dc_asid = SMMU_DC_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_dcb_asid = SMMU_DCB_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_hc_asid = SMMU_HC_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_hda_asid = SMMU_HDA_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_isp2_asid = SMMU_ISP2_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_msenc_asid = SMMU_MSENC_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_nv_asid = SMMU_NV_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_nv2_asid = SMMU_NV2_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_ppcs_asid = SMMU_PPCS_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_sata_asid = SMMU_SATA_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_vde_asid = SMMU_VDE_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_vi_asid = SMMU_VI_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_vic_asid = SMMU_VIC_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_xusb_host_asid = SMMU_XUSB_HOST_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_xusb_dev_asid = SMMU_XUSB_DEV_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_tsec_asid = SMMU_TSEC_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_ppcs1_asid = SMMU_PPCS1_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_sdmmc1a_asid = SMMU_SDMMC1A_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_sdmmc2a_asid = SMMU_SDMMC2A_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_sdmmc3a_asid = SMMU_SDMMC3A_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_sdmmc4a_asid = SMMU_SDMMC4A_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_isp2b_asid = SMMU_ISP2B_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_gpu_asid = SMMU_GPU_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_gpub_asid = SMMU_GPUB_ASID | MODULE_ASID_ENABLE; + smmu_regs->smmu_ppcs2_asid = SMMU_PPCS2_ASID | MODULE_ASID_ENABLE; + + /* flush page table cache */ + plat_smmu_ptc_flush_all(); + /* flush TLB */ + plat_smmu_tlb_flush_all(); + smmu_enable(); + printf("smmu enabled\n"); + + return ARM_PLAT_NUM_SMMU; +} + + +iopde_t * +plat_smmu_lookup_iopd_by_asid(uint32_t asid) +{ + iopde_t *pd = 0; + uint32_t data = 0; + if (asid < SMMU_FIRST_ASID || asid > SMMU_LAST_ASID) { + return 0; + } + + smmu_regs->smmu_ptb_asid = asid; + data = smmu_regs->smmu_ptb_data; + pd = (iopde_t *)(paddr_to_pptr(ptb_data_get_pd_base(data))); + return pd; +} +