Merge pull request #199 in SEL4/sel4 from ~YSHEN/sel4k:feature/arm_hyp_tk1 to arm_hyp
* commit 'eee71654b2c7e13ff282109845073794b337b060': arm-hyp/tk1: add ARM_HYP as a dependency arm-hyp/tk1: remove unnecessary check arm-hyp/tk1: make style arm-hyp/tk1: address review comments. arm-hyp/tk1: address review comments. arm-hyp/tk1: should trap SMC from guest kernel arm-hyp/tk1: remove const number arm-hyp/tk1: make style arm-hyp/tk1: add default SMMU translation for VM arm-hyp/tk1: enable SMMU interrupts arm-hyp/tk1: reserve the SMMU interrupt arm-hyp/tk1: fix bugs found during testing arm-hyp/tk1: add the missing return arm-hyp/tk1: put iospace caps to bootinfo arm-hyp/tk1: disable/enable SMMU in secure mode arm/tk1: connect objects with SMMU invocations arm/tk1: a checkpoint for iospace.c arm/tk1: a checkpoint for SMMU implementation
This commit is contained in:
commit
4b357744e0
26 changed files with 1327 additions and 38 deletions
9
Kconfig
9
Kconfig
|
|
@ -241,6 +241,15 @@ config ARM_HYPERVISOR_SUPPORT
|
|||
help
|
||||
Support for TK1 SoC-specific SystemMMU
|
||||
|
||||
config ARM_SMMU_VM_DEFAULT_MAPPING
|
||||
bool "Enable SystemMMU default mapping for guest VM"
|
||||
depends on PLAT_TK1 && ARM_SMMU && ARM_HYPERVISOR_SUPPORT
|
||||
help
|
||||
Enable the default translation for guest VM: all
|
||||
physical memory reserved for a VM is allowed to
|
||||
be accessed with read/write/nonsecure permissions.
|
||||
|
||||
|
||||
|
||||
source "$KERNEL_PATH/src/arch/arm/Kconfig"
|
||||
source "$KERNEL_PATH/src/plat/pc99/Kconfig"
|
||||
|
|
|
|||
|
|
@ -188,7 +188,10 @@ static inline void setCurrentPDPL2(paddr_t pa) {}
|
|||
static inline void invalidateHypTLB(void) {}
|
||||
static inline void writeContextIDPL2(word_t pd) {}
|
||||
static inline void writeContextIDAndPD(word_t id, word_t pd) {}
|
||||
static inline paddr_t addressTranslateS1CPR(vptr_t vaddr) { return vaddr; }
|
||||
static inline paddr_t addressTranslateS1CPR(vptr_t vaddr)
|
||||
{
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
#endif /* !ARM_HYP */
|
||||
#endif /* __ARCH_MACHINE_PL2_32_H */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
-- @TAG(GD_GPL)
|
||||
--
|
||||
|
||||
#include <config.h>
|
||||
-- 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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
@ -325,6 +329,8 @@ cap_get_archCapSizeBits(cap_t cap)
|
|||
case cap_vcpu_cap:
|
||||
return VCPU_SIZE_BITS;
|
||||
#endif
|
||||
case cap_io_page_table_cap:
|
||||
return seL4_IOPageTableBits;
|
||||
|
||||
default:
|
||||
/* Unreachable, but GCC can't figure that out */
|
||||
|
|
@ -362,6 +368,9 @@ cap_get_archCapPtr(cap_t cap)
|
|||
return VCPU_PTR(cap_vcpu_cap_get_capVCPUPtr(cap));
|
||||
#endif
|
||||
|
||||
case cap_io_page_table_cap:
|
||||
return (void *)(cap_io_page_table_cap_get_capIOPTBasePtr(cap));
|
||||
|
||||
default:
|
||||
/* Unreachable, but GCC can't figure that out */
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -95,4 +95,6 @@ exception_t decodeARMMMUInvocation(word_t invLabel, word_t length, cptr_t cptr,
|
|||
word_t *buffer);
|
||||
|
||||
|
||||
bool_t CONST isIOSpaceFrame(cap_t cap);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@ static inline void clearMemory(word_t* ptr, word_t bits)
|
|||
cleanCacheRange_PoU((word_t)ptr, (word_t)ptr + BIT(bits) - 1,
|
||||
addrFromPPtr(ptr));
|
||||
}
|
||||
|
||||
static inline void clearMemoryRAM(word_t* ptr, word_t bits)
|
||||
{
|
||||
memzero(ptr, BIT(bits));
|
||||
cleanCacheRange_RAM((word_t)ptr, (word_t)ptr + BIT(bits) - 1,
|
||||
addrFromPPtr(ptr));
|
||||
}
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
#endif /* __ARCH_MACHINE_H */
|
||||
|
|
|
|||
29
include/arch/arm/arch/object/iospace.h
Normal file
29
include/arch/arm/arch/object/iospace.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 <types.h>
|
||||
#include <api/failures.h>
|
||||
#include <object/structures.h>
|
||||
|
||||
|
||||
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);
|
||||
void clearIOPageDirectory(cap_t cap);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ exception_t invokeVCPUSetTCB(vcpu_t *vcpu, tcb_t *tcb);
|
|||
#else /* end of ARM_HYP */
|
||||
|
||||
/* used in boot.c with a guard, use a marco to avoid exposing vcpu_t */
|
||||
#define vcpu_restore(x)
|
||||
#define vcpu_restore(x)
|
||||
#define vcpu_switch(x)
|
||||
static inline void VGICMaintenance(void) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -216,6 +216,8 @@ enum IRQConstants {
|
|||
#define INTERRUPT_NS_PGPT INTERRUPT_PPI_14
|
||||
#define INTERRUPT_VGPT INTERRUPT_PPI_11
|
||||
#define INTERRUPT_HGPT INTERRUPT_PPI_10
|
||||
#define INTERRUPT_SMMU INTERRUPT_MC
|
||||
|
||||
|
||||
/* the kernel runs in secure supervisor mode by default */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -41,6 +43,14 @@
|
|||
#define VGICI_REQ_PADDR (ARM_PERIPHBASE + 0x4000) /* hyp view for requesting CPU */
|
||||
#define VGICI_ALL_PADDR (ARM_PERIPHBASE + 0x5000) /* hyp view for all CPUs */
|
||||
#define VGICI_VM_PADDR (ARM_PERIPHBASE + 0x6000) /* hyp view for VM view */
|
||||
#define PCIE_0_CFG_PADDR (0x01000000) /* PCIE lane 0 config register */
|
||||
#define PCIE_1_CFG_PADDR (0x01001000) /* PCIE lane 1 config register */
|
||||
#define PCIE_PCA0_1_PADDR (0x01002000) /* PCA 0 and 1 */
|
||||
#define PCIE_PADS_AFI_PADDR (0x01003000) /* Pads and AFI */
|
||||
#define PCIE_A1_PADDR (0x01000000) /* 16 MB */
|
||||
#define PCIE_A2_PADDR (0x02000000) /* 224 MB */
|
||||
#define PCIE_A3_PADDR (0x10000000) /* 763 MB */
|
||||
#define R8169_NIC_PADDR (0x13000000) /* r8169 NIC 1 MB */
|
||||
#define GRAPH_HOST_PADDR (0x54000000) /* 16 MB */
|
||||
#define GPU_PADDR (0x57000000) /* 144 MB */
|
||||
#define UP_TAG_PADDR (0x60000000) /* 4 KB */
|
||||
|
|
@ -81,7 +91,8 @@
|
|||
#define HDA_PADDR (0x70030000) /* 64 KB */
|
||||
#define MIOBFM_PADDR (0x70200000) /* 64 KB */
|
||||
#define AUDIO_PADDR (0x70300000) /* 64 KB */
|
||||
#define XUSB_HOST_PADDR (0x70090000) /* 40 KB */
|
||||
#define XUSB_HOST_PADDR (0x70090000) /* 36 KB */
|
||||
#define XUSB_PADCTL_PADDR (0x7009f000) /* 4 KB */
|
||||
#define XUSB_DEV_PADDR (0x700d0000) /* 40 KB */
|
||||
#define DDS_PADDR (0x700a0000) /* 8KB 4608 bytes */
|
||||
#define SDMMC_1_PADDR (0x700b0000) /* 4KB 512 bytes */
|
||||
|
|
@ -104,4 +115,9 @@
|
|||
#define USB3_PADDR (0x7d008000) /* 8 KB region, 6 KB */
|
||||
#define UARTA_PADDR (0x70006000)
|
||||
|
||||
/* physical memory regions allocated for virtual machines */
|
||||
#define VM_HOST_PA_START 0xb0000000
|
||||
#define VM_GUEST_PA_START 0x80000000
|
||||
#define VM_HOST_PA_SIZE 0x40000000
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,19 +27,23 @@ tagged_union iopde page_size {
|
|||
block iopde_pt {
|
||||
field read 1
|
||||
field write 1
|
||||
field secure 1
|
||||
field nonsecure 1
|
||||
field page_size 1
|
||||
padding 6
|
||||
field_high address 22
|
||||
}
|
||||
|
||||
-- adding a 2-bit padding before address is because
|
||||
-- we do not support addressing more than 4 GiB memory
|
||||
|
||||
block iopde_4m {
|
||||
field read 1
|
||||
field write 1
|
||||
field secure 1
|
||||
field nonsecure 1
|
||||
field page_size 1
|
||||
padding 6
|
||||
field_high address 12
|
||||
padding 2
|
||||
field_high address 10
|
||||
padding 10
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +52,8 @@ block iopde_4m {
|
|||
block iopte {
|
||||
field read 1
|
||||
field write 1
|
||||
field secure 1
|
||||
field nonsecure 1
|
||||
padding 7
|
||||
field_high address 22
|
||||
padding 2
|
||||
field_high address 20
|
||||
}
|
||||
|
|
|
|||
167
include/plat/tk1/plat/machine/smmu.h
Normal file
167
include/plat/tk1/plat/machine/smmu.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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 <types.h>
|
||||
|
||||
#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 reserved0; /* 0x24 */
|
||||
uint32_t reserved1; /* 0x28 */
|
||||
uint32_t reserved2; /* 0x2c */
|
||||
uint32_t smmu_tlb_flush; /* 0x30 */
|
||||
uint32_t smmu_ptc_flush; /* 0x34 */
|
||||
uint32_t reserved3[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 reserved4; /* 0x248 */
|
||||
uint32_t reserved5; /* 0x24c */
|
||||
uint32_t smmu_hc_asid; /* 0x250 */
|
||||
uint32_t smmu_hda_asid; /* 0x254 */
|
||||
uint32_t smmu_isp2_asid; /* 0x258 */
|
||||
uint32_t reserved6; /* 0x25c */
|
||||
uint32_t reserved7; /* 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 reserved8; /* 0x290 */
|
||||
uint32_t smmu_tsec_asid; /* 0x294 */
|
||||
uint32_t smmu_ppcs1_asid; /* 0x298 */
|
||||
uint32_t reserved9[217];
|
||||
uint32_t smmu_tlb_set_sel_mask; /* 0x600 */
|
||||
uint32_t reserved10[237];
|
||||
uint32_t smmu_ptc_flush_1; /* 0x9b8 */
|
||||
uint32_t reserved11[51];
|
||||
uint32_t smmu_dc1_asid; /* 0xa88 */
|
||||
uint32_t reserved12; /* 0xa8c */
|
||||
uint32_t reserved13; /* 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;
|
||||
}
|
||||
|
||||
inline static uint32_t
|
||||
plat_smmu_get_asid_by_module_id(uint32_t mid)
|
||||
{
|
||||
if (mid < SMMU_FIRST_ASID || mid > SMMU_LAST_ASID) {
|
||||
return asidInvalid;
|
||||
}
|
||||
|
||||
/* we have one-to-one mapping from module id to ASID */
|
||||
return mid;
|
||||
|
||||
}
|
||||
|
||||
/** MODIFIES: [*] */
|
||||
int plat_smmu_init(void);
|
||||
|
||||
/** MODIFIES: [*] */
|
||||
void plat_smmu_tlb_flush_all(void);
|
||||
|
||||
/** MODIFIES: [*] */
|
||||
void plat_smmu_ptc_flush_all(void);
|
||||
|
||||
iopde_t *plat_smmu_lookup_iopd_by_asid(uint32_t asid);
|
||||
void plat_smmu_handle_interrupt(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -18,6 +18,14 @@
|
|||
<method id="ARMPageTableUnmap" name="Unmap">
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="seL4_ARM_IOPageTable">
|
||||
<method id="ARMIOPageTableMap" name="Map">
|
||||
<param dir="in" name="iospace" type="seL4_ARM_IOSpace"/>
|
||||
<param dir="in" name="ioaddr" type="seL4_Word"/>
|
||||
</method>
|
||||
<method id="ARMIOPageTableUnmap" name="Unmap">
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="seL4_ARM_Page">
|
||||
<method id="ARMPageMap" name="Map">
|
||||
<param dir="in" name="pd" type="seL4_ARM_PageDirectory"/>
|
||||
|
|
@ -31,6 +39,11 @@
|
|||
<param dir="in" name="attr" type="seL4_ARM_VMAttributes"/>
|
||||
</method>
|
||||
<method id="ARMPageUnmap" name="Unmap"/>
|
||||
<method id="ARMPageMapIO" name="MapIO">
|
||||
<param dir="in" name="iospace" type="seL4_ARM_IOSpace"/>
|
||||
<param dir="in" name="rights" type="seL4_CapRights"/>
|
||||
<param dir="in" name="ioaddr" type="seL4_Word"/>
|
||||
</method>
|
||||
<method id="ARMPageClean_Data" name="Clean_Data">
|
||||
<param dir="in" name="start_offset" type="seL4_Word"/>
|
||||
<param dir="in" name="end_offset" type="seL4_Word"/>
|
||||
|
|
|
|||
|
|
@ -20,11 +20,18 @@ typedef enum _object {
|
|||
seL4_ARM_PageDirectoryObject,
|
||||
#ifdef ARM_HYP
|
||||
seL4_ARM_VCPUObject,
|
||||
#endif
|
||||
#ifdef CONFIG_ARM_SMMU
|
||||
seL4_ARM_IOPageTableObject,
|
||||
#endif
|
||||
seL4_ObjectTypeCount
|
||||
} seL4_ArchObjectType;
|
||||
|
||||
typedef seL4_Word object_t;
|
||||
|
||||
#ifndef CONFIG_ARM_SMMU
|
||||
#define seL4_ARM_IOPageTableObject 0xffff
|
||||
#endif
|
||||
|
||||
#endif /* __ARCH_OBJECTTYPE_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__ */
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ enum {
|
|||
#define seL4_PageDirBits 14
|
||||
#define seL4_ASIDPoolBits 12
|
||||
#define seL4_ARM_VCPUBits 12
|
||||
#define seL4_IOPageTableBits 12
|
||||
/* word size */
|
||||
#define seL4_WordBits (sizeof(seL4_Word) * 8)
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <plat/machine/devices.h>
|
||||
#include <plat/machine/hardware.h>
|
||||
#include <armv/context_switch.h>
|
||||
#include <arch/object/iospace.h>
|
||||
|
||||
/* ARM uses multiple identical mappings in a page table / page directory to construct
|
||||
* large mappings. In both cases it happens to be 16 entries, which can be calculated by
|
||||
|
|
@ -513,6 +514,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 */
|
||||
);
|
||||
|
|
@ -1128,6 +1132,16 @@ isValidVTableRoot(cap_t cap)
|
|||
cap_page_directory_cap_get_capPDIsMapped(cap);
|
||||
}
|
||||
|
||||
bool_t CONST
|
||||
isIOSpaceFrame(cap_t cap)
|
||||
{
|
||||
#ifdef CONFIG_ARM_SMMU
|
||||
return cap_get_capType(cap) == cap_small_frame_cap && cap_small_frame_cap_get_capFIsIOSpace(cap);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
setVMRoot(tcb_t *tcb)
|
||||
{
|
||||
|
|
@ -2544,6 +2558,13 @@ decodeARMFrameInvocation(word_t invLabel, word_t length,
|
|||
vm_page_size_t frameSize;
|
||||
vm_attributes_t attr;
|
||||
|
||||
if (isIOSpaceFrame(cap)) {
|
||||
userError("ARMFrameRemap: Attempting to remap frame mapped into an IOSpace");
|
||||
current_syscall_error.type = seL4_IllegalOperation;
|
||||
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
if (unlikely(length < 2 || excaps.excaprefs[0] == NULL)) {
|
||||
current_syscall_error.type =
|
||||
seL4_TruncatedMessage;
|
||||
|
|
@ -2644,8 +2665,16 @@ decodeARMFrameInvocation(word_t invLabel, word_t length,
|
|||
}
|
||||
|
||||
case ARMPageUnmap: {
|
||||
setThreadState(ksCurThread, ThreadState_Restart);
|
||||
return performPageInvocationUnmap(cap, cte);
|
||||
if (isIOSpaceFrame(cap)) {
|
||||
return decodeARMIOUnMapInvocation(invLabel, length, cte, cap, excaps);
|
||||
} else {
|
||||
setThreadState(ksCurThread, ThreadState_Restart);
|
||||
return performPageInvocationUnmap(cap, cte);
|
||||
}
|
||||
}
|
||||
|
||||
case ARMPageMapIO: {
|
||||
return decodeARMIOMapInvocation(invLabel, length, cte, cap, excaps, buffer);
|
||||
}
|
||||
|
||||
case ARMPageClean_Data:
|
||||
|
|
|
|||
|
|
@ -74,6 +74,22 @@ Arch_deriveCap(cte_t *slot, cap_t cap)
|
|||
return ret;
|
||||
#endif
|
||||
|
||||
case cap_io_space_cap:
|
||||
ret.cap = cap;
|
||||
ret.status = EXCEPTION_NONE;
|
||||
return ret;
|
||||
|
||||
case cap_io_page_table_cap:
|
||||
if (cap_io_page_table_cap_get_capIOPTIsMapped(cap)) {
|
||||
ret.cap = cap;
|
||||
ret.status = EXCEPTION_NONE;
|
||||
} else {
|
||||
userError("Deriving a IOPT cap without an assigned IOASID");
|
||||
current_syscall_error.type = seL4_IllegalOperation;
|
||||
ret.cap = cap_null_cap_new();
|
||||
ret.status = EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
return ret;
|
||||
default:
|
||||
/* This assert has no equivalent in haskell,
|
||||
* as the options are restricted by type */
|
||||
|
|
@ -140,6 +156,10 @@ Arch_finaliseCap(cap_t cap, bool_t final)
|
|||
|
||||
case cap_small_frame_cap:
|
||||
if (cap_small_frame_cap_get_capFMappedASID(cap)) {
|
||||
if (isIOSpaceFrame(cap)) {
|
||||
unmapIOPage(cap);
|
||||
break;
|
||||
}
|
||||
unmapPage(ARMSmallPage,
|
||||
cap_small_frame_cap_get_capFMappedASID(cap),
|
||||
cap_small_frame_cap_get_capFMappedAddress(cap),
|
||||
|
|
@ -163,6 +183,21 @@ Arch_finaliseCap(cap_t cap, bool_t final)
|
|||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case cap_io_space_cap:
|
||||
if (final) {
|
||||
clearIOPageDirectory(cap);
|
||||
}
|
||||
break;
|
||||
|
||||
case cap_io_page_table_cap:
|
||||
if (final && cap_io_page_table_cap_get_capIOPTIsMapped(cap)) {
|
||||
deleteIOPageTable(cap);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return cap_null_cap_new();
|
||||
|
|
@ -182,6 +217,8 @@ resetMemMapping(cap_t cap)
|
|||
case cap_page_directory_cap:
|
||||
/* We don't need to worry about clearing ASID and Address here, only whether it is mapped */
|
||||
return cap_page_directory_cap_set_capPDIsMapped(cap, 0);
|
||||
case cap_io_page_table_cap:
|
||||
return cap_io_page_table_cap_set_capIOPTIsMapped(cap, 0);
|
||||
}
|
||||
|
||||
return cap;
|
||||
|
|
@ -270,6 +307,15 @@ Arch_recycleCap(bool_t is_final, cap_t cap)
|
|||
return cap;
|
||||
#endif
|
||||
|
||||
case cap_io_space_cap:
|
||||
Arch_finaliseCap(cap, true);
|
||||
return cap;
|
||||
|
||||
case cap_io_page_table_cap:
|
||||
clearMemoryRAM(cap_get_capPtr(cap), cap_get_capSizeBits(cap));
|
||||
Arch_finaliseCap(cap, is_final);
|
||||
return resetMemMapping(cap);
|
||||
|
||||
default:
|
||||
fail("Arch_recycleCap: invalid cap type");
|
||||
}
|
||||
|
|
@ -344,6 +390,19 @@ Arch_sameRegionAs(cap_t cap_a, cap_t cap_b)
|
|||
break;
|
||||
#endif
|
||||
|
||||
case cap_io_space_cap:
|
||||
if (cap_get_capType(cap_b) == cap_io_space_cap) {
|
||||
return cap_io_space_cap_get_capModuleID(cap_a) ==
|
||||
cap_io_space_cap_get_capModuleID(cap_b);
|
||||
}
|
||||
break;
|
||||
|
||||
case cap_io_page_table_cap:
|
||||
if (cap_get_capType(cap_b) == cap_io_page_table_cap) {
|
||||
return cap_io_page_table_cap_get_capIOPTBasePtr(cap_a) ==
|
||||
cap_io_page_table_cap_get_capIOPTBasePtr(cap_b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -390,6 +449,8 @@ Arch_getObjectSize(word_t t)
|
|||
return PTE_SIZE_BITS + PT_BITS;
|
||||
case seL4_ARM_PageDirectoryObject:
|
||||
return PDE_SIZE_BITS + PD_BITS;
|
||||
case seL4_ARM_IOPageTableObject:
|
||||
return seL4_IOPageTableBits;
|
||||
#ifdef ARM_HYP
|
||||
case seL4_ARM_VCPUObject:
|
||||
return VCPU_SIZE_BITS;
|
||||
|
|
@ -417,7 +478,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:
|
||||
|
|
@ -497,6 +562,12 @@ Arch_createObject(object_t t, void *regionBase, word_t userSize)
|
|||
return cap_vcpu_cap_new(VCPU_REF(regionBase));
|
||||
#endif
|
||||
|
||||
case seL4_ARM_IOPageTableObject:
|
||||
memzero(regionBase, 1 << seL4_IOPageTableBits);
|
||||
cleanCacheRange_RAM((word_t)regionBase,
|
||||
(word_t)regionBase + (1 << seL4_IOPageTableBits) - 1,
|
||||
addrFromPPtr(regionBase));
|
||||
return cap_io_page_table_cap_new(0, asidInvalid, (word_t)regionBase, 0);
|
||||
default:
|
||||
/*
|
||||
* This is a conflation of the haskell error: "Arch.createNewCaps
|
||||
|
|
@ -512,13 +583,18 @@ Arch_decodeInvocation(word_t invLabel, word_t length, cptr_t cptr,
|
|||
cte_t *slot, cap_t cap, extra_caps_t excaps,
|
||||
word_t *buffer)
|
||||
{
|
||||
switch (cap_get_capType(cap)) {
|
||||
case cap_io_space_cap:
|
||||
return decodeARMIOSpaceInvocation(invLabel, cap);
|
||||
case cap_io_page_table_cap:
|
||||
return decodeARMIOPTInvocation(invLabel, length, slot, cap, excaps, buffer);
|
||||
#ifdef ARM_HYP
|
||||
if (cap_get_capType(cap) == cap_vcpu_cap) {
|
||||
case cap_vcpu_cap:
|
||||
return decodeARMVCPUInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
|
||||
|
||||
#endif /* end of ARM_HYP */
|
||||
default:
|
||||
return decodeARMMMUInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
|
||||
}
|
||||
#endif /* ARM_HYP */
|
||||
return decodeARMMMUInvocation(invLabel, length, cptr, slot, cap, excaps, buffer);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <arch/kernel/vspace.h>
|
||||
#include <arch/benchmark.h>
|
||||
#include <arch/user_access.h>
|
||||
#include <arch/object/iospace.h>
|
||||
#include <arch/linker.h>
|
||||
#include <plat/machine/hardware.h>
|
||||
#include <machine.h>
|
||||
|
|
@ -134,6 +135,9 @@ init_irqs(cap_t root_cnode_cap)
|
|||
if (config_set(ARM_HYP)) {
|
||||
setIRQState(IRQReserved, INTERRUPT_VGIC_MAINTENANCE);
|
||||
}
|
||||
if (config_set(CONFIG_ARM_SMMU)) {
|
||||
setIRQState(IRQReserved, INTERRUPT_SMMU);
|
||||
}
|
||||
|
||||
/* provide the IRQ control cap */
|
||||
write_slot(SLOT_PTR(pptr_of_cap(root_cnode_cap), seL4_CapIRQControl), cap_irq_control_cap_new());
|
||||
|
|
@ -234,6 +238,14 @@ try_init_kernel(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (config_set(CONFIG_ARM_SMMU)) {
|
||||
ndks_boot.bi_frame->ioSpaceCaps = create_iospace_caps(root_cnode_cap);
|
||||
if (ndks_boot.bi_frame->ioSpaceCaps.start == 0 &&
|
||||
ndks_boot.bi_frame->ioSpaceCaps.end == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
539
src/arch/arm/object/iospace.c
Normal file
539
src/arch/arm/object/iospace.c
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
/*
|
||||
* 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 <config.h>
|
||||
|
||||
#include <api/syscall.h>
|
||||
#include <machine/io.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <arch/api/invocation.h>
|
||||
#include <arch/object/iospace.h>
|
||||
#include <arch/model/statedata.h>
|
||||
#include <object/structures.h>
|
||||
#include <arch/linker.h>
|
||||
#include <plat/machine/smmu.h>
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
#define IOPDE_VALID_MASK 0xe0000000
|
||||
#define IOPTE_EMPTY_MASK 0xe0000000
|
||||
|
||||
static bool_t
|
||||
isIOPDEValid(iopde_t *iopde)
|
||||
{
|
||||
assert(iopde != 0);
|
||||
if ((iopde->words[0] & IOPDE_VALID_MASK) == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool_t
|
||||
isIOPTEEmpty(iopte_t *iopte)
|
||||
{
|
||||
assert(iopte != 0);
|
||||
if ((iopte->words[0] & IOPTE_EMPTY_MASK) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!isIOPDEValid(pd_ret.iopdSlot) ||
|
||||
iopde_ptr_get_page_size(pd_ret.iopdSlot) != iopde_iopde_pt) {
|
||||
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));
|
||||
|
||||
if (pt == 0) {
|
||||
pt_ret.status = EXCEPTION_LOOKUP_FAULT;
|
||||
pt_ret.ioptSlot = 0;
|
||||
return pt_ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* the 0 is reserved as an invalidASID,
|
||||
* assuming each module is assigned an unique ASID
|
||||
* and the ASIDs are contiguous
|
||||
* */
|
||||
for (i = 1; 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;
|
||||
}
|
||||
}
|
||||
end = ndks_boot.slot_pos_cur;
|
||||
printf("Region [%x to %x) for SMMU caps\n", (unsigned int)start, (unsigned int)end);
|
||||
return (seL4_SlotRegion) {
|
||||
start, end
|
||||
};
|
||||
}
|
||||
|
||||
static exception_t
|
||||
performARMIOPTInvocationMap(cap_t cap, cte_t *slot, iopde_t *iopdSlot,
|
||||
iopde_t iopde, uint32_t asid, paddr_t io_address)
|
||||
{
|
||||
|
||||
|
||||
*iopdSlot = iopde;
|
||||
cleanCacheRange_RAM((word_t)iopdSlot,
|
||||
((word_t)iopdSlot) + sizeof(iopde_t),
|
||||
addrFromPPtr(iopdSlot));
|
||||
|
||||
plat_smmu_tlb_flush_all();
|
||||
plat_smmu_ptc_flush_all();
|
||||
|
||||
cap = cap_io_page_table_cap_set_capIOPTIsMapped(cap, 1);
|
||||
cap = cap_io_page_table_cap_set_capIOPTASID(cap, asid);
|
||||
cap = cap_io_page_table_cap_set_capIOPTMappedAddress(cap, io_address);
|
||||
|
||||
slot->cap = cap;
|
||||
return EXCEPTION_NONE;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
word_t paddr;
|
||||
uint16_t module_id;
|
||||
uint32_t asid;
|
||||
iopde_t *pd;
|
||||
iopde_t iopde;
|
||||
lookupIOPDSlot_ret_t lu_ret;
|
||||
|
||||
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) {
|
||||
userError("IOPTInvocation: Truncated message.");
|
||||
current_syscall_error.type = seL4_TruncatedMessage;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
if (invLabel != ARMIOPageTableMap ) {
|
||||
userError("IOPTInvocation: Invalid operation.");
|
||||
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)) {
|
||||
userError("IOPTMap: Cap already mapped.");
|
||||
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) {
|
||||
userError("IOPTMap: Invalid IOSpace cap.");
|
||||
current_syscall_error.type = seL4_InvalidCapability;
|
||||
current_syscall_error.invalidCapNumber = 1;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
module_id = cap_io_space_cap_get_capModuleID(io_space);
|
||||
asid = plat_smmu_get_asid_by_module_id(module_id);
|
||||
if (asid == asidInvalid) {
|
||||
userError("IOPTMap: Invalid IOASID.");
|
||||
current_syscall_error.type = seL4_InvalidCapability;
|
||||
current_syscall_error.invalidCapNumber = 1;
|
||||
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
paddr = pptr_to_paddr((void *)cap_io_page_table_cap_get_capIOPTBasePtr(cap));
|
||||
pd = (iopde_t *)plat_smmu_lookup_iopd_by_asid(asid);
|
||||
|
||||
if (pd == 0) {
|
||||
userError("IOPTMap: IOPD not found.");
|
||||
current_syscall_error.type = seL4_InvalidCapability;
|
||||
current_syscall_error.invalidCapNumber = 1;
|
||||
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
lu_ret = lookupIOPDSlot(pd, io_address);
|
||||
|
||||
if (isIOPDEValid(lu_ret.iopdSlot)) {
|
||||
userError("IOPTMap: Delete first.");
|
||||
current_syscall_error.type = seL4_DeleteFirst;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
iopde = iopde_iopde_pt_new(
|
||||
1, /* read */
|
||||
1, /* write */
|
||||
1, /* nonsecure */
|
||||
paddr
|
||||
);
|
||||
|
||||
setThreadState(ksCurThread, ThreadState_Restart);
|
||||
return performARMIOPTInvocationMap(cap, slot, lu_ret.iopdSlot, iopde, asid, io_address);
|
||||
}
|
||||
|
||||
static exception_t
|
||||
performARMIOMapInvocation(cap_t cap, cte_t *slot, iopte_t *ioptSlot,
|
||||
iopte_t iopte, uint32_t asid, paddr_t io_address)
|
||||
{
|
||||
*ioptSlot = iopte;
|
||||
cleanCacheRange_RAM((word_t)ioptSlot,
|
||||
((word_t)ioptSlot) + sizeof(iopte_t),
|
||||
addrFromPPtr(ioptSlot));
|
||||
|
||||
plat_smmu_tlb_flush_all();
|
||||
plat_smmu_ptc_flush_all();
|
||||
|
||||
#ifdef CONFIG_ARM_SMMU
|
||||
cap = cap_small_frame_cap_set_capFIsIOSpace(cap, 1);
|
||||
#endif
|
||||
cap = cap_small_frame_cap_set_capFMappedASID(cap, asid);
|
||||
cap = cap_small_frame_cap_set_capFMappedAddress(cap, io_address);
|
||||
slot->cap = cap;
|
||||
|
||||
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;
|
||||
uint32_t asid;
|
||||
iopde_t *pd;
|
||||
iopte_t iopte;
|
||||
vm_rights_t frame_cap_rights;
|
||||
cap_rights_t dma_cap_rights_mask;
|
||||
lookupIOPTSlot_ret_t lu_ret;
|
||||
|
||||
if (excaps.excaprefs[0] == NULL || length < 2) {
|
||||
userError("IOMap: Truncated message.");
|
||||
current_syscall_error.type = seL4_TruncatedMessage;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
if (generic_frame_cap_get_capFSize(cap) != ARMSmallPage) {
|
||||
userError("IOMap: Invalid cap type.");
|
||||
current_syscall_error.type = seL4_InvalidCapability;
|
||||
current_syscall_error.invalidCapNumber = 0;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
if (cap_small_frame_cap_get_capFMappedASID(cap) != asidInvalid) {
|
||||
userError("IOMap: Frame all ready mapped.");
|
||||
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_small_frame_cap_get_capFBasePtr(cap));
|
||||
|
||||
if (cap_get_capType(io_space) != cap_io_space_cap) {
|
||||
userError("IOMap: Invalid IOSpace cap.");
|
||||
current_syscall_error.type = seL4_InvalidCapability;
|
||||
current_syscall_error.invalidCapNumber = 1;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
module_id = cap_io_space_cap_get_capModuleID(io_space);
|
||||
asid = plat_smmu_get_asid_by_module_id(module_id);
|
||||
|
||||
if (asid == asidInvalid) {
|
||||
userError("IOMap: Invalid IOSpace ASID.");
|
||||
current_syscall_error.type = seL4_InvalidCapability;
|
||||
current_syscall_error.invalidCapNumber = 1;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
pd = (iopde_t *)plat_smmu_lookup_iopd_by_asid(asid);
|
||||
if (pd == 0) {
|
||||
userError("IOMap: Invalid IOSpace cap.");
|
||||
current_syscall_error.type = seL4_InvalidCapability;
|
||||
current_syscall_error.invalidCapNumber = 1;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
lu_ret = lookupIOPTSlot(pd, io_address);
|
||||
if (lu_ret.status != EXCEPTION_NONE) {
|
||||
userError("IOMap: Lookup failed.");
|
||||
current_syscall_error.type = seL4_FailedLookup;
|
||||
current_syscall_error.failedLookupWasSource = false;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
if (!isIOPTEEmpty(lu_ret.ioptSlot)) {
|
||||
userError("IOMap: Delete first.");
|
||||
current_syscall_error.type = seL4_DeleteFirst;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
frame_cap_rights = cap_small_frame_cap_get_capFVMRights(cap);
|
||||
dma_cap_rights_mask = rightsFromWord(getSyscallArg(0, buffer));
|
||||
|
||||
if ((frame_cap_rights == VMReadOnly) && cap_rights_get_capAllowRead(dma_cap_rights_mask)) {
|
||||
/* read only */
|
||||
iopte = iopte_new(
|
||||
1, /* read */
|
||||
0, /* write */
|
||||
1, /* nonsecure */
|
||||
paddr
|
||||
);
|
||||
} else if (frame_cap_rights == VMReadWrite) {
|
||||
if (cap_rights_get_capAllowRead(dma_cap_rights_mask) &&
|
||||
!cap_rights_get_capAllowWrite(dma_cap_rights_mask)) {
|
||||
/* read only */
|
||||
iopte = iopte_new(
|
||||
1, /* read */
|
||||
0, /* write */
|
||||
1, /* nonsecure */
|
||||
paddr
|
||||
);
|
||||
} else if (!cap_rights_get_capAllowRead(dma_cap_rights_mask) &&
|
||||
cap_rights_get_capAllowWrite(dma_cap_rights_mask)) {
|
||||
/* write only */
|
||||
iopte = iopte_new(
|
||||
0, /* read */
|
||||
1, /* write */
|
||||
1, /* nonsecure */
|
||||
paddr
|
||||
);
|
||||
} else if (cap_rights_get_capAllowRead(dma_cap_rights_mask) &&
|
||||
cap_rights_get_capAllowWrite(dma_cap_rights_mask)) {
|
||||
/* read write */
|
||||
iopte = iopte_new(
|
||||
1, /* read */
|
||||
1, /* write */
|
||||
1, /* nonsecure */
|
||||
paddr
|
||||
);
|
||||
} else {
|
||||
userError("IOMap: Invalid argument.");
|
||||
current_syscall_error.type = seL4_InvalidArgument;
|
||||
current_syscall_error.invalidArgumentNumber = 0;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* VMKernelOnly */
|
||||
userError("IOMap: Invalid argument.");
|
||||
current_syscall_error.type = seL4_InvalidArgument;
|
||||
current_syscall_error.invalidArgumentNumber = 0;
|
||||
return EXCEPTION_SYSCALL_ERROR;
|
||||
}
|
||||
|
||||
setThreadState(ksCurThread, ThreadState_Restart);
|
||||
return performARMIOMapInvocation(cap, slot, lu_ret.ioptSlot, iopte, asid, io_address);
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
lu_ret = lookupIOPDSlot(pd, io_address);
|
||||
if (lu_ret.status != EXCEPTION_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isIOPDEValid(lu_ret.iopdSlot) &&
|
||||
iopde_ptr_get_page_size(lu_ret.iopdSlot) == iopde_iopde_pt &&
|
||||
iopde_iopde_pt_ptr_get_address(lu_ret.iopdSlot) != (pptr_to_paddr((void *)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));
|
||||
|
||||
|
||||
/* nice to have: 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_small_frame_cap_get_capFMappedAddress(cap);
|
||||
asid = cap_small_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) != pptr_to_paddr((void *)cap_small_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;
|
||||
}
|
||||
|
||||
void clearIOPageDirectory(cap_t cap)
|
||||
{
|
||||
iopde_t *pd;
|
||||
uint32_t asid = cap_io_space_cap_get_capModuleID(cap);
|
||||
word_t size = BIT((SMMU_PD_BITS));
|
||||
pd = (iopde_t *)plat_smmu_lookup_iopd_by_asid(asid);
|
||||
|
||||
if (pd == 0) {
|
||||
return;
|
||||
}
|
||||
memset((void *)pd, 0, size);
|
||||
cleanCacheRange_RAM((word_t)pd, (word_t)pd + size, addrFromPPtr(pd));
|
||||
|
||||
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
|
||||
)
|
||||
{
|
||||
unmapIOPage(slot->cap);
|
||||
slot->cap = cap_small_frame_cap_set_capFMappedAddress(slot->cap, 0);
|
||||
#ifdef CONFIG_ARM_SMMU
|
||||
slot->cap = cap_small_frame_cap_set_capFIsIOSpace(slot->cap, 0);
|
||||
#endif
|
||||
slot->cap = cap_small_frame_cap_set_capFMappedASID(slot->cap, asidInvalid);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
/* Available physical memory regions on platform (RAM minus kernel image). */
|
||||
/* NOTE: Regions are not allowed to be adjacent! */
|
||||
|
||||
/* 1 MiB starting from 0xa7f00000 is reserved by the elfloader for monitor mode hooks */
|
||||
const p_region_t BOOT_RODATA avail_p_regs[] = {
|
||||
// { .start = 0x80000000, .end = 0xf0000000 }
|
||||
{ .start = 0x80000000, .end = 0xb0000000 }
|
||||
{ .start = 0x80000000, .end = 0xa7f00000 }
|
||||
};
|
||||
|
||||
BOOT_CODE int get_num_avail_p_regs(void)
|
||||
|
|
@ -40,13 +40,17 @@ BOOT_CODE p_region_t get_avail_p_reg(word_t i)
|
|||
#define PAGE_SIZE (1 << PAGE_BITS)
|
||||
#define SECTION_SIZE (1 << SECTION_BITS)
|
||||
|
||||
#define VM_PA_START 0xb0000000
|
||||
#define VM_PA_SIZE 0x10000000
|
||||
|
||||
const p_region_t BOOT_RODATA dev_p_regs[] = {
|
||||
|
||||
{ VM_PA_START, VM_PA_START + VM_PA_SIZE },
|
||||
{ VM_HOST_PA_START, VM_HOST_PA_START + VM_HOST_PA_SIZE },
|
||||
{ VGICI_VM_PADDR, VGICI_VM_PADDR + PAGE_SIZE },
|
||||
{ PCIE_0_CFG_PADDR, PCIE_0_CFG_PADDR + PAGE_SIZE },
|
||||
{ PCIE_1_CFG_PADDR, PCIE_1_CFG_PADDR + PAGE_SIZE },
|
||||
{ PCIE_PCA0_1_PADDR, PCIE_PCA0_1_PADDR + PAGE_SIZE },
|
||||
{ PCIE_PADS_AFI_PADDR, PCIE_PADS_AFI_PADDR + PAGE_SIZE },
|
||||
{ PCIE_A2_PADDR, PCIE_A2_PADDR + SECTION_SIZE },
|
||||
{ R8169_NIC_PADDR, R8169_NIC_PADDR + SECTION_SIZE},
|
||||
{ GRAPH_HOST_PADDR, GRAPH_HOST_PADDR + (SECTION_SIZE * 16) }, /* 16 MB */
|
||||
{ GPU_PADDR, GPU_PADDR + (SECTION_SIZE * 144) }, /* 144 MB */
|
||||
{ UP_TAG_PADDR, UP_TAG_PADDR + PAGE_SIZE }, /* 4 KB */
|
||||
|
|
@ -81,13 +85,16 @@ 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 */
|
||||
{ MIOBFM_PADDR, MIOBFM_PADDR + (PAGE_SIZE * 16) }, /* 64 KB */
|
||||
{ AUDIO_PADDR, AUDIO_PADDR + (PAGE_SIZE * 16) }, /* 64 KB */
|
||||
{ XUSB_HOST_PADDR, XUSB_HOST_PADDR + (PAGE_SIZE * 10) }, /* 40 KB */
|
||||
{ XUSB_HOST_PADDR, XUSB_HOST_PADDR + (PAGE_SIZE * 9) }, /* 36 KB */
|
||||
{ XUSB_PADCTL_PADDR, XUSB_PADCTL_PADDR + PAGE_SIZE }, /* 4 KB */
|
||||
{ XUSB_DEV_PADDR, XUSB_DEV_PADDR + (PAGE_SIZE * 10) }, /* 40 KB */
|
||||
{ DDS_PADDR, DDS_PADDR + (PAGE_SIZE * 2) }, /* 8KB 4608 bytes */
|
||||
{ SDMMC_1_PADDR, SDMMC_1_PADDR + PAGE_SIZE }, /* 4KB 512 bytes */
|
||||
|
|
@ -137,6 +144,11 @@ handleReservedIRQ(irq_t irq)
|
|||
return;
|
||||
}
|
||||
|
||||
if (config_set(CONFIG_ARM_SMMU) && (irq == INTERRUPT_SMMU)) {
|
||||
plat_smmu_handle_interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Received reserved IRQ: %d\n", (int)irq);
|
||||
}
|
||||
|
||||
|
|
@ -167,14 +179,27 @@ map_kernel_devices(void)
|
|||
|
||||
if (config_set(ARM_HYP)) {
|
||||
map_kernel_frame(
|
||||
GIC_VCPUCTRL_PADDR,
|
||||
GIC_VCPUCTRL_PPTR,
|
||||
VMKernelOnly,
|
||||
vm_attributes_new(
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
GIC_VCPUCTRL_PADDR,
|
||||
GIC_VCPUCTRL_PPTR,
|
||||
VMKernelOnly,
|
||||
vm_attributes_new(
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (config_set(CONFIG_ARM_SMMU)) {
|
||||
map_kernel_frame(
|
||||
MC_PADDR,
|
||||
SMMU_PPTR,
|
||||
VMKernelOnly,
|
||||
vm_attributes_new(
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
324
src/plat/tk1/machine/smmu.c
Normal file
324
src/plat/tk1/machine/smmu.c
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
#include <types.h>
|
||||
#include <config.h>
|
||||
#include <plat/machine/smmu.h>
|
||||
#include <arch/linker.h>
|
||||
#include <plat/machine/devices.h>
|
||||
#include <plat/machine/hardware.h>
|
||||
#include <object/structures.h>
|
||||
|
||||
|
||||
|
||||
#define SMMU_CONFIG_OFFSET 0x10
|
||||
|
||||
#define PTB_DATA_BASE_SHIFT 12
|
||||
#define PTB_DATA_READ BIT(31)
|
||||
#define PTB_DATA_WRITE BIT(30)
|
||||
#define PTB_DATA_NONSECURE BIT(29)
|
||||
#define PTB_DATA_BASE_PD_MASK 0x3fffff
|
||||
|
||||
#define MODULE_ASID_ENABLE BIT(31)
|
||||
|
||||
#define PTC_FLUSH_ALL 0
|
||||
#define PTC_FLUSH_ADR 1
|
||||
|
||||
#define TLB_ASID_MATCH BIT(31)
|
||||
#define TLB_FLUSH_ALL (0)
|
||||
#define TLB_FLUSH_SECTION (2)
|
||||
#define TLB_FLUSH_GROUP (3)
|
||||
|
||||
#define MC_DECERR_MTS_BIT 16u
|
||||
#define MC_SECERR_SEC_BIT 13u
|
||||
#define MC_DECERR_VPR_BIT 12u
|
||||
#define MC_APB_ASID_UPDATE_BIT 11u
|
||||
#define MC_SMMU_PAGE_BIT 10u
|
||||
#define MC_ARBITRATION_EMEM_BIT 9u
|
||||
#define MC_SECURITY_BIT 8u
|
||||
#define MC_DECERR_EMEM_BIT 6u
|
||||
|
||||
|
||||
#define MC_ERR_ID_MASK 0x7f
|
||||
#define MC_ERR_ADR_MASK 0x7000
|
||||
#define MC_ERR_RW_MASK 0x10000
|
||||
#define MC_ERR_SEC_MASK 0x20000
|
||||
#define MC_ERR_SWAP_MASK 0x40000
|
||||
#define MC_ERR_ADR_HI_MASK 0x300000
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_NONSECURE_MASK 0x2000000
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_WRITE_MASK 0x4000000
|
||||
#define MC_ERR_INVALID_SMMU_PAGE_READ_MASK 0x8000000
|
||||
#define MC_ERR_TYPE_MASK 0x70000000
|
||||
#define MC_ERR_TYPE_SHIFT 28
|
||||
|
||||
#define MC_ERR_TYPE_RSVD 0
|
||||
#define MC_ERR_TYPE_DECERR_EMEM 2
|
||||
#define MC_ERR_TYPE_SECURITY 3
|
||||
#define MC_ERR_TYPE_SECURITY_CARVEOUT 4
|
||||
#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6
|
||||
|
||||
#define IOPDE_4M_INDEX_SHIFT 22
|
||||
|
||||
static volatile tk1_mc_regs_t *smmu_regs = (volatile tk1_mc_regs_t *)(SMMU_PPTR);
|
||||
|
||||
static void
|
||||
do_smmu_enable(void)
|
||||
{
|
||||
volatile uint32_t *config = (volatile uint32_t *)(MC_PADDR + SMMU_CONFIG_OFFSET);
|
||||
*config = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
do_smmu_disable(void)
|
||||
{
|
||||
volatile uint32_t *config = (volatile uint32_t *)(MC_PADDR + SMMU_CONFIG_OFFSET);
|
||||
*config = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
smmu_disable(void)
|
||||
{
|
||||
if (config_set(ARM_HYP)) {
|
||||
/* in hyp mode, we need call the hook in monitor mode */
|
||||
/* we need physical address here */
|
||||
paddr_t addr = addrFromPPtr(&do_smmu_disable);
|
||||
asm (".arch_extension sec\n");
|
||||
asm volatile ("mov r0, %0\n\t"
|
||||
"dsb\nisb\n"
|
||||
"smc #0\n"
|
||||
::"r"(addr));
|
||||
} else {
|
||||
/* in secure mode, can enable it directly */
|
||||
smmu_regs->smmu_config = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void
|
||||
smmu_enable(void)
|
||||
{
|
||||
if (config_set(ARM_HYP)) {
|
||||
paddr_t addr = addrFromPPtr(&do_smmu_enable);
|
||||
asm (".arch_extension sec\n");
|
||||
asm volatile ("mov r0, %0\n\t"
|
||||
"dsb\nisb\n"
|
||||
"smc #0\n"
|
||||
::"r"(addr));
|
||||
} else {
|
||||
smmu_regs->smmu_config = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
plat_smmu_ptc_flush_all(void)
|
||||
{
|
||||
uint32_t cmd = PTC_FLUSH_ALL;
|
||||
smmu_regs->smmu_ptc_flush = cmd;
|
||||
}
|
||||
|
||||
void
|
||||
plat_smmu_tlb_flush_all(void)
|
||||
{
|
||||
uint32_t cmd = TLB_FLUSH_ALL;
|
||||
smmu_regs->smmu_tlb_flush = cmd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Using 4 MiB mapping for the Linxu guest VM.
|
||||
* This is a temporary solution for enabling guest VM
|
||||
* devices that need DMA while still providing some
|
||||
* protections. Once the device untyped feature is done,
|
||||
* this code should be replaced with proper user-mode
|
||||
* VM initialisation code.
|
||||
*/
|
||||
|
||||
static void
|
||||
plat_smmu_vm_mapping(word_t iopd, word_t gpa, word_t pa, word_t size)
|
||||
{
|
||||
iopde_t *iopde = (iopde_t *)iopd;
|
||||
while (size > 0) {
|
||||
word_t index = gpa >> IOPDE_4M_INDEX_SHIFT;
|
||||
iopde_iopde_4m_ptr_new(
|
||||
iopde + index,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
pa
|
||||
);
|
||||
gpa += BIT(IOPDE_4M_INDEX_SHIFT);
|
||||
pa += BIT(IOPDE_4M_INDEX_SHIFT);
|
||||
size -= BIT(IOPDE_4M_INDEX_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
BOOT_CODE int
|
||||
plat_smmu_init(void)
|
||||
{
|
||||
uint32_t asid = 1;
|
||||
int i = 0;
|
||||
|
||||
smmu_disable();
|
||||
|
||||
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));
|
||||
if (config_set(CONFIG_ARM_SMMU_VM_DEFAULT_MAPPING)) {
|
||||
plat_smmu_vm_mapping((word_t)pd, VM_GUEST_PA_START, VM_HOST_PA_START, VM_HOST_PA_SIZE);
|
||||
}
|
||||
cleanCacheRange_RAM((word_t)pd, ((word_t)pd + BIT(SMMU_PD_BITS)),
|
||||
addrFromPPtr(pd));
|
||||
|
||||
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();
|
||||
|
||||
/* also need to unmask interrupts */
|
||||
smmu_regs->intmask = BIT(MC_APB_ASID_UPDATE_BIT) | BIT(MC_SMMU_PAGE_BIT) |
|
||||
BIT(MC_DECERR_MTS_BIT) | BIT(MC_SECERR_SEC_BIT) |
|
||||
BIT(MC_DECERR_VPR_BIT) | BIT(MC_ARBITRATION_EMEM_BIT) |
|
||||
BIT(MC_SECURITY_BIT) | BIT(MC_DECERR_EMEM_BIT);
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
plat_smmu_handle_interrupt(void)
|
||||
{
|
||||
uint32_t status = smmu_regs->intstatus;
|
||||
uint32_t clear_status = 0;
|
||||
|
||||
if (status & BIT(MC_DECERR_MTS_BIT)) {
|
||||
clear_status |= BIT(MC_DECERR_MTS_BIT);
|
||||
}
|
||||
if (status & BIT(MC_SECERR_SEC_BIT)) {
|
||||
clear_status |= BIT(MC_SECERR_SEC_BIT);
|
||||
}
|
||||
if (status & BIT(MC_DECERR_VPR_BIT)) {
|
||||
clear_status |= BIT(MC_DECERR_VPR_BIT);
|
||||
}
|
||||
if (status & BIT(MC_ARBITRATION_EMEM_BIT)) {
|
||||
clear_status |= BIT(MC_ARBITRATION_EMEM_BIT);
|
||||
}
|
||||
if (status & BIT(MC_SECURITY_BIT)) {
|
||||
clear_status |= BIT(MC_SECURITY_BIT);
|
||||
}
|
||||
if (status & BIT(MC_DECERR_EMEM_BIT)) {
|
||||
clear_status |= BIT(MC_DECERR_EMEM_BIT);
|
||||
}
|
||||
if (status & BIT(MC_APB_ASID_UPDATE_BIT)) {
|
||||
clear_status |= BIT(MC_APB_ASID_UPDATE_BIT);
|
||||
}
|
||||
|
||||
/* we only care about SMMU translation failures */
|
||||
if (status & BIT(MC_SMMU_PAGE_BIT)) {
|
||||
if (config_set(DEBUG)) {
|
||||
uint32_t err_status = smmu_regs->err_status;
|
||||
uint32_t UNUSED err_adr = smmu_regs->err_adr;
|
||||
uint32_t UNUSED id = err_status & MC_ERR_ID_MASK;
|
||||
uint32_t UNUSED rw = (err_status & MC_ERR_RW_MASK);
|
||||
uint32_t UNUSED read = (err_status & MC_ERR_INVALID_SMMU_PAGE_READ_MASK);
|
||||
uint32_t UNUSED write = (err_status & MC_ERR_INVALID_SMMU_PAGE_WRITE_MASK);
|
||||
uint32_t UNUSED nonsecure = (err_status & MC_ERR_INVALID_SMMU_PAGE_NONSECURE_MASK);
|
||||
uint32_t UNUSED type = (err_status & MC_ERR_TYPE_MASK) >> MC_ERR_TYPE_SHIFT;
|
||||
|
||||
printf("SMMU Address translation error:\n");
|
||||
printf("ID: %d address: 0x%x type: %d direction: 0x%x\n", id, err_adr, type, rw);
|
||||
printf("IOPT permission: read 0x%x write 0x%x nonsecure 0x%x\n", read, write, nonsecure);
|
||||
}
|
||||
clear_status |= BIT(MC_SMMU_PAGE_BIT);
|
||||
}
|
||||
|
||||
/* write 1 to clear the interrupt */
|
||||
smmu_regs->intstatus = clear_status;
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue