Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions hw/riscv/riscv-iommu-bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ struct riscv_iommu_pq_record {
#define RISCV_IOMMU_CAP_PD17 BIT_ULL(39)
#define RISCV_IOMMU_CAP_PD20 BIT_ULL(40)

#define RISCV_IOMMU_CAP_GIPC BIT_ULL(56)

enum riscv_iommu_igs_modes {
RISCV_IOMMU_CAP_IGS_MSI = 0,
RISCV_IOMMU_CAP_IGS_WSI,
Expand Down Expand Up @@ -302,6 +304,8 @@ struct riscv_iommu_dc {
#define RISCV_IOMMU_DC_TC_SBE BIT_ULL(10)
#define RISCV_IOMMU_DC_TC_SXL BIT_ULL(11)

#define RISCV_IOMMU_DC_TC_GIPC BIT_ULL(24)

/* Second-stage (aka G-stage) context fields */
#define RISCV_IOMMU_DC_IOHGATP_PPN RISCV_IOMMU_ATP_PPN_FIELD
#define RISCV_IOMMU_DC_IOHGATP_GSCID GENMASK_ULL(59, 44)
Expand Down
164 changes: 160 additions & 4 deletions hw/riscv/riscv-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,149 @@ static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s,
return true;
}

/**
* pdt_memory_read: PDT wrapper of dma_memory_read.
*
* @s: IOMMU Device State
* @ctx: Device Translation Context with devid and pasid set
* @addr: address within that address space
* @buf: buffer with the data transferred
* @len: length of the data transferred
* @attrs: memory transaction attributes
*/
static MemTxResult pdt_memory_read(RISCVIOMMUState *s,
RISCVIOMMUContext *ctx,
dma_addr_t addr,
void *buf, dma_addr_t len,
MemTxAttrs attrs)
{
uint64_t gatp_mode, pte;
struct {
unsigned char step;
unsigned char levels;
unsigned char ptidxbits;
unsigned char ptesize;
} sc;
MemTxResult ret;
dma_addr_t base = addr;

/* GIPC detect */
if ((ctx->tc & RISCV_IOMMU_DC_TC_GIPC) && (s->cap & RISCV_IOMMU_CAP_GIPC)) {
goto out;
}

/* G stages translation mode */
gatp_mode = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD);
if (gatp_mode == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) {
goto out;
}

/* G stages translation tables root pointer */
base = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD));

/* Start at step 0 */
sc.step = 0;

if (s->fctl & RISCV_IOMMU_FCTL_GXL) {
/* 32bit mode for GXL == 1 */
switch (gatp_mode) {
case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4:
if (!(s->cap & RISCV_IOMMU_CAP_SV32X4)) {
return MEMTX_ACCESS_ERROR;
}
sc.levels = 2;
sc.ptidxbits = 10;
sc.ptesize = 4;
break;
default:
return MEMTX_ACCESS_ERROR;
}
} else {
/* 64bit mode for GXL == 0 */
switch (gatp_mode) {
case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
if (!(s->cap & RISCV_IOMMU_CAP_SV39X4)) {
return MEMTX_ACCESS_ERROR;
}
sc.levels = 3;
sc.ptidxbits = 9;
sc.ptesize = 8;
break;
case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
if (!(s->cap & RISCV_IOMMU_CAP_SV48X4)) {
return MEMTX_ACCESS_ERROR;
}
sc.levels = 4;
sc.ptidxbits = 9;
sc.ptesize = 8;
break;
case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
if (!(s->cap & RISCV_IOMMU_CAP_SV57X4)) {
return MEMTX_ACCESS_ERROR;
}
sc.levels = 5;
sc.ptidxbits = 9;
sc.ptesize = 8;
break;
default:
return MEMTX_ACCESS_ERROR;
}
}

do {
const unsigned va_bits = (sc.step ? 0 : 2) + sc.ptidxbits;
const unsigned va_skip = TARGET_PAGE_BITS + sc.ptidxbits *
(sc.levels - 1 - sc.step);
const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1);
const dma_addr_t pte_addr = base + idx * sc.ptesize;

/* Address range check before first level lookup */
if (!sc.step) {
const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1;
if ((addr & va_mask) != addr) {
return MEMTX_ACCESS_ERROR;
}
}

/* Read page table entry */
if (sc.ptesize == 4) {
uint32_t pte32 = 0;
ret = ldl_le_dma(s->target_as, pte_addr, &pte32, attrs);
pte = pte32;
} else {
ret = ldq_le_dma(s->target_as, pte_addr, &pte, attrs);
}
if (ret != MEMTX_OK)
return ret;

sc.step++;
hwaddr ppn = pte >> PTE_PPN_SHIFT;

if (!(pte & PTE_V)) {
return MEMTX_ACCESS_ERROR; /* Invalid PTE */
} else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
base = PPN_PHYS(ppn); /* Inner PTE, continue walking */
} else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W */
} else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
return MEMTX_ACCESS_ERROR; /* Reserved leaf PTE flags: PTE_W + PTE_X */
} else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) {
return MEMTX_ACCESS_ERROR; /* Misaligned PPN */
} else {
/* Leaf PTE, translation completed. */
base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1));
break;
}

if (sc.step == sc.levels) {
return MEMTX_ACCESS_ERROR; /* Can't find leaf PTE */
}
} while (1);

out:
return dma_memory_read(s->target_as, base, buf, len, attrs);
}

/*
* RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk
*
Expand All @@ -884,6 +1027,7 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
const size_t dc_len = sizeof(dc) >> dc_fmt;
int depth;
uint64_t de;
bool gipc = false;

switch (mode) {
case RISCV_IOMMU_DDTP_MODE_OFF:
Expand Down Expand Up @@ -1029,16 +1173,20 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED;
}

if ((ctx->tc & RISCV_IOMMU_DC_TC_GIPC) && (s->cap & RISCV_IOMMU_CAP_GIPC)) {
gipc = true;
}

for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) {
riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK);

/*
* Select process id index bits based on process directory tree
* level. See IOMMU Specification, 2.2. Process-Directory-Table.
*/
const int split = depth * 9 + 8;
const int split = depth * 9 + (gipc ? 7 : 8);
addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK;
if (dma_memory_read(s->target_as, addr, &de, sizeof(de),
if (pdt_memory_read(s, ctx, addr, &de, sizeof(de),
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
}
Expand All @@ -1052,15 +1200,19 @@ static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx)
riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_PD_WALK);

/* Leaf entry in PDT */
addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK;
if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2,
addr |= (ctx->process_id << (gipc ? 5 : 4)) & ~TARGET_PAGE_MASK;
if (pdt_memory_read(s, ctx, addr, &dc.ta,
gipc ? sizeof(uint64_t) * 4 : sizeof(uint64_t) * 2,
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT;
}

/* Use FSC and TA from process directory entry. */
ctx->ta = le64_to_cpu(dc.ta);
ctx->satp = le64_to_cpu(dc.fsc);
if (gipc) {
ctx->gatp = le64_to_cpu(dc.msiptp);
}

if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) {
return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID;
Expand Down Expand Up @@ -2354,6 +2506,9 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp)
RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4 |
RISCV_IOMMU_CAP_SVRSW60T59B;
}
if (s->enable_gipc) {
s->cap |= RISCV_IOMMU_CAP_GIPC;
}

if (s->hpm_cntrs > 0) {
/* Clip number of HPM counters to maximum supported (31). */
Expand Down Expand Up @@ -2508,6 +2663,7 @@ static const Property riscv_iommu_properties[] = {
TYPE_MEMORY_REGION, MemoryRegion *),
DEFINE_PROP_UINT8("hpm-counters", RISCVIOMMUState, hpm_cntrs,
RISCV_IOMMU_IOCOUNT_NUM),
DEFINE_PROP_BOOL("gipc", RISCVIOMMUState, enable_gipc, TRUE),
};

static void riscv_iommu_class_init(ObjectClass *klass, const void *data)
Expand Down
1 change: 1 addition & 0 deletions hw/riscv/riscv-iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct RISCVIOMMUState {
bool enable_ats; /* Enable ATS support */
bool enable_s_stage; /* Enable S/VS-Stage translation */
bool enable_g_stage; /* Enable G-Stage translation */
bool enable_gipc; /* Enable G-Stage table in Process Context */

/* IOMMU Internal State */
uint64_t ddtp; /* Validated Device Directory Tree Root Pointer */
Expand Down