arm/tk1: a checkpoint for SMMU implementation

This commit is contained in:
Yanyan Shen 2016-05-13 16:42:53 +10:00
parent c4692926a8
commit 879d9724c1
18 changed files with 760 additions and 11 deletions

View file

@ -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())

View file

@ -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
};

View file

@ -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 <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);
#endif

View file

@ -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

View file

@ -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 <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 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

View file

@ -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"/>

View file

@ -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__ */

View file

@ -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) */

View file

@ -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),
],

View file

@ -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 */
);

View file

@ -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:

View file

@ -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>
@ -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);

View file

@ -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

View file

@ -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 <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;
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;
}

View file

@ -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)

View file

@ -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

View file

@ -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(

206
src/plat/tk1/machine/smmu.c Normal file
View file

@ -0,0 +1,206 @@
#include <types.h>
#include <plat/machine/smmu.h>
#include <arch/linker.h>
#include <plat/machine/devices.h>
#include <plat/machine/hardware.h>
#include <object/structures.h>
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;
}