Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More RISC-V CHERI PTE bits #117

Merged
merged 12 commits into from
Jul 20, 2021
12 changes: 4 additions & 8 deletions target/mips/cp0_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -1281,21 +1281,17 @@ void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1)
mask &= env->SEGMask;
#endif

#if defined(TARGET_CHERI)
mask |= (1UL << CP0EnHi_CLGU)
| (1UL << CP0EnHi_CLGS)
| (1UL << CP0EnHi_CLGK);
#endif
mask |= CP0EnHi_CLG_MASK;

old = env->CP0_EntryHi;
val = (arg1 & mask) | (old & ~mask);
env->CP0_EntryHi = val;
if (env->CP0_Config3 & (1 << CP0C3_MT)) {
sync_c0_entryhi(env, env->current_tc);
}
/* If the ASID changes, flush qemu's TLB. */
if ((old & env->CP0_EntryHi_ASID_mask) !=
(val & env->CP0_EntryHi_ASID_mask)) {
/* If the ASID or CLG changes, flush qemu's TLB. */
target_ulong tlb_flush_mask = env->CP0_EntryHi_ASID_mask | CP0EnHi_CLG_MASK;
if ((old & tlb_flush_mask) != (val & tlb_flush_mask)) {
tlb_flush(env_cpu(env));
}
log_instr_cop0_update(env, CP0_REGISTER_10, 0, env->CP0_EntryHi);
Expand Down
4 changes: 4 additions & 0 deletions target/mips/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,10 @@ struct CPUMIPSState {
#define CP0EnHi_CLGK 61
#define CP0EnHi_CLGS 60
#define CP0EnHi_CLGU 59
#define CP0EnHi_CLG_MASK \
((1ULL << CP0EnHi_CLGK) | (1ULL << CP0EnHi_CLGS) | (1UL << CP0EnHi_CLGU))
#else
#define CP0EnHi_CLG_MASK 0
#endif
target_ulong CP0_EntryHi_ASID_mask;
/*
Expand Down
6 changes: 4 additions & 2 deletions target/mips/helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,10 +618,11 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address,
env->CP0_Context = (env->CP0_Context & ~0x007fffff) |
((address >> 9) & 0x007ffff0);
env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) |
(env->CP0_EntryHi & CP0EnHi_CLG_MASK) |
(env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) |
(address & (TARGET_PAGE_MASK << 1));
#if defined(TARGET_MIPS64)
env->CP0_EntryHi &= env->SEGMask;
env->CP0_EntryHi &= env->SEGMask | CP0EnHi_CLG_MASK;
env->CP0_XContext =
(env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | /* PTEBase */
(extract64(address, 62, 2) << (env->SEGBITS - 9)) | /* R */
Expand Down Expand Up @@ -960,7 +961,8 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, int rw,
}
pw_pagemask = m >> 12;
update_pagemask(env, pw_pagemask << 13, &pw_pagemask);
pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF);
pw_entryhi = (address & ~0x1fff) |
(env->CP0_EntryHi & (0xFF | CP0EnHi_CLG_MASK));
{
target_ulong tmp_entryhi = env->CP0_EntryHi;
int32_t tmp_pagemask = env->CP0_PageMask;
Expand Down
5 changes: 2 additions & 3 deletions target/mips/op_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -959,8 +959,7 @@ void r4k_helper_tlbr(CPUMIPSState *env)
r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);

#ifdef CPU_CHERI
uint64_t save_clg = env->CP0_EntryHi &
((1 << CP0EnHi_CLGU) | (1 << CP0EnHi_CLGS) | (1 << CP0EnHi_CLGK));
uint64_t save_clg = env->CP0_EntryHi & CP0EnHi_CLG_MASK;
#endif

if (tlb->EHINV) {
Expand Down Expand Up @@ -997,7 +996,7 @@ void r4k_helper_tlbr(CPUMIPSState *env)
}

#ifdef CPU_CHERI
env->CP0_EntryHi |= save_clg;
env->CP0_EntryHi |= save_clg;
#endif
}

Expand Down
4 changes: 4 additions & 0 deletions target/riscv/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ struct CPURISCVState {
target_ulong mbadaddr;
target_ulong medeleg;

#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
target_ulong sccsr;
#endif

#ifdef TARGET_CHERI
// XXX: not implemented properly
cap_register_t UTCC; // SCR 4 User trap code cap. (UTCC)
Expand Down
13 changes: 11 additions & 2 deletions target/riscv/cpu_bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@
#define CSR_UCCSR 0x8C0
#define CSR_SCCSR 0x9C0
#define CSR_MCCSR 0xBC0

/* See Capability Control and Status Registers (CCSRs) in CHERI ISA spec. */
#define XCCSR_ENABLE 0x1 /* Capability extensions enabled */
#define XCCSR_DIRTY 0x2 /* Capability register written */
#define SCCSR_SGCLG 0x4 /* Supervisor Global Cap Load Generation */
#define SCCSR_UGCLG 0x8 /* User Global Cap Load Generation */
#endif

/* mstatus CSR bits */
Expand Down Expand Up @@ -526,8 +532,11 @@
#define PTE_D 0x080 /* Dirty */
#define PTE_SOFT 0x300 /* Reserved for Software */
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
#define PTE_LC 0x4000000000000000 /* Load Cap */
#define PTE_SC 0x8000000000000000 /* Store Cap */
#define PTE_CRG 0x0800000000000000 /* Cap Read Generation */
#define PTE_CRM 0x1000000000000000 /* Cap Read Modifier */
#define PTE_CD 0x2000000000000000 /* Cap Dirty */
#define PTE_CR 0x4000000000000000 /* Cap Read */
#define PTE_CW 0x8000000000000000 /* Cap Write */
#endif

/* Page table PPN shift amount */
Expand Down
110 changes: 103 additions & 7 deletions target/riscv/cpu_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,17 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
extern bool rvfi_debug_output;
#endif

#ifndef RISCV_PTE_TRAPPY
/*
* The PTW logic below supports trapping on any subset of PTE_A, PTE_D, PTE_CD
* being clear during an access that would have them be set. The RISC-V spec
* says that PTE_A and PTE_D always go together, and it's probably most sensible
* that PTE_CD implies PTE_A and PTE_D. So, sensible values for this constant
* are 0, (PTE_A | PTE_D), or (PTE_A | PTE_D | PTE_CD).
*/
#define RISCV_PTE_TRAPPY 0
#endif

/* get_physical_address - get the physical address for this virtual address
*
* Do a page table walk to obtain the physical address corresponding to a
Expand Down Expand Up @@ -583,6 +594,14 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
} else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
/* Reserved leaf PTE flags: PTE_W + PTE_X */
return TRANSLATE_FAIL;
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
} else if ((pte & (PTE_CR | PTE_CRG)) == PTE_CRG) {
/* Reserved CHERI-extended PTE flags: no CR but CRG */
return TRANSLATE_CHERI_FAIL;
} else if ((pte & (PTE_CR | PTE_CRM | PTE_CRG)) == (PTE_CR | PTE_CRG)) {
/* Reserved CHERI-extended PTE flags: CR and no CRM but CRG */
return TRANSLATE_CHERI_FAIL;
#endif
} else if ((pte & PTE_U) && ((mode != PRV_U) &&
(!sum || access_type == MMU_INST_FETCH))) {
/* User PTE flags when not U mode and mstatus.SUM is not set,
Expand All @@ -607,14 +626,45 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
/* Fetch access check failed */
return TRANSLATE_FAIL;
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
} else if (access_type == MMU_DATA_CAP_STORE && !(pte & PTE_SC)) {
/* SC inhibited */
} else if (access_type == MMU_DATA_CAP_STORE && !(pte & PTE_CW)) {
/* CW inhibited */
return TRANSLATE_CHERI_FAIL;
#endif
#if RISCV_PTE_TRAPPY & PTE_A
} else if (!(pte & PTE_A)) {
/* PTE not marked as accessed */
return TRANSLATE_FAIL;
#endif
#if RISCV_PTE_TRAPPY & PTE_D
} else if ((access_type == MMU_DATA_STORE) && !(pte & PTE_D)) {
/* PTE not marked as dirty */
return TRANSLATE_FAIL;
#endif
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
#if RISCV_PTE_TRAPPY & PTE_D
} else if (access_type == MMU_DATA_CAP_STORE && !(pte & PTE_D)) {
/* PTE not marked as dirty for cap store */
return TRANSLATE_FAIL;
#endif
#if RISCV_PTE_TRAPPY & PTE_CD
} else if (access_type == MMU_DATA_CAP_STORE && !(pte & PTE_CD)) {
/* CD clear; force the software trap handler to get involved */
return TRANSLATE_CHERI_FAIL;
#endif
#endif
} else {
/* if necessary, set accessed and dirty bits. */
target_ulong updated_pte = pte | PTE_A |
(access_type == MMU_DATA_STORE ? PTE_D : 0);
target_ulong updated_pte = pte | PTE_A;
switch (access_type) {
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
case MMU_DATA_CAP_STORE:
updated_pte |= PTE_CD;
/* FALLTHROUGH */
#endif
case MMU_DATA_STORE:
updated_pte |= PTE_D;
break;
}

/* Page table updates need to be atomic with MTTCG enabled */
if (updated_pte != pte) {
Expand Down Expand Up @@ -674,10 +724,26 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
*prot |= PAGE_WRITE;
}
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
if ((pte & PTE_LC) == 0) {
*prot |= PAGE_LC_CLEAR;
if ((pte & PTE_CR) == 0) {
if ((pte & PTE_CRM) == 0) {
*prot |= PAGE_LC_CLEAR;
} else {
*prot |= PAGE_LC_TRAP;
}
} else {
if (pte & PTE_CRM) {
/* Cap-loads checked against [SU]GCLG in CCSR using PTE_U */
target_ulong gclgmask =
(pte & PTE_U) ? SCCSR_UGCLG : SCCSR_SGCLG;
bool gclg = (env->sccsr & gclgmask) != 0;
bool lclg = (pte & PTE_CRG) != 0;

if (gclg != lclg) {
*prot |= PAGE_LC_TRAP;
}
}
}
if ((pte & PTE_SC) == 0) {
if ((pte & PTE_CW) == 0) {
*prot |= PAGE_SC_TRAP;
}
#endif
Expand Down Expand Up @@ -910,6 +976,36 @@ static int riscv_cpu_tlb_fill_impl(CPURISCVState *env, vaddr address, int size,
TARGET_FMT_plx " prot %d\n",
__func__, im_address, ret, *pa, prot2);

#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
/*
* CHERI's load-side caveats are enforced only on the guest
* tables at the moment. Because we are about to AND the two
* prot words together, *set* both caveats in prot2 so that
* either bit will be preserved from prot.
*/
prot2 |= PAGE_LC_TRAP | PAGE_LC_CLEAR;

/*
* XXX Eventually we probably want to permit the hypervisor to be
* able to force tag clearing or trapping. That probably looks
* something like this (but details are subject to change):
*
* Host Guest Action on tagged load
* -------- -------- ---------------------
*
* Clear _ Clear tag
* _ Clear Clear tag
*
* Accept Accept Accept
* Accept Trap Supervisor (VS/S) fault
*
* Trap _ Hypervisor (HS) fault
*
* The rest of the bits are AND-ed together as before, and
* get_physical_address already handles the store-side CHERI
* extensions.
*/
#endif
*prot &= prot2;

if (riscv_feature(env, RISCV_FEATURE_PMP) &&
Expand Down
52 changes: 36 additions & 16 deletions target/riscv/csr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1333,33 +1333,52 @@ static int write_pmpaddr(CPURISCVState *env, int csrno, target_ulong val)
#endif

#ifdef TARGET_CHERI

// See Capability Control and Status Registers (CCSRs) in CHERI ISA spec
// The e “enable” bit tells whether capability extensions are enabled or disabled.
#define CCSR_ENABLE 0x1
// The d “dirty” bit tells whether a capability register has been written.
#define CCSR_DIRTY 0x2
// We used to report cause and capcause here, but those have moved to xTVAL

static int read_ccsr(CPURISCVState *env, int csrno, target_ulong *val)
{
// We report the same values for all modes and don't perform dirty tracking
// The capability cause has moved to xTVAL so we don't report it here.
RISCVCPU *cpu = env_archcpu(env);
target_ulong ccsr = 0;
ccsr = set_field(ccsr, CCSR_ENABLE, cpu->cfg.ext_cheri);
ccsr = set_field(ccsr, CCSR_DIRTY, 1); /* Always report dirty */
ccsr = set_field(ccsr, XCCSR_ENABLE, cpu->cfg.ext_cheri);
ccsr = set_field(ccsr, XCCSR_DIRTY, 1); /* Always report dirty */

#if !defined(TARGET_RISCV32)
if (csrno == CSR_SCCSR)
ccsr |= env->sccsr;
#endif

qemu_log_mask(CPU_LOG_INT, "Reading xCCSR(%#x): %x\n", csrno, (int)ccsr);
*val = ccsr;
return 0;
}

static int write_ccsr(CPURISCVState *env, int csrno, target_ulong val)
{
error_report("Attempting to write " TARGET_FMT_lx
"to xCCSR(%#x), this is not supported (yet?).",
val, csrno);
return -1;
switch (csrno) {
default:
error_report("Attempting to write " TARGET_FMT_lx
"to xCCSR(%#x), this is not supported (yet?).",
val, csrno);
return -1;
#if !defined(TARGET_RISCV32)
case CSR_SCCSR: {
static const target_ulong gclgmask = (SCCSR_SGCLG | SCCSR_UGCLG);
/* Take the GCLG bits from the store and update state bits */
env->sccsr = set_field(env->sccsr, gclgmask, get_field(val, gclgmask));
arichardson marked this conversation as resolved.
Show resolved Hide resolved

/*
* Our TLB effectively caches whether the PTE and CCSR bits match at the
* time the PTE is copied up into the TLB. While PTE updates use
* SFENCE.VMA to ensure visibility in the TLB, the CCSR writes must
* implicitly cause TLB invalidation.
*/
tlb_flush(env_cpu(env));
break;
}
#endif
}

return 0;
}

static inline bool csr_needs_asr(CPURISCVState *env, int csrno) {
Expand Down Expand Up @@ -1683,8 +1702,9 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MTINST] = CSR_OP_RW(hmode, mtinst),

#ifdef TARGET_CHERI
// CHERI CSRs: For now we alway report the same values and don't allow
// turning off any of the bits
// CHERI CSRs: For now we always report enabled and dirty and don't support
// turning off CHERI. sccsr contains global capability load generation bits
// that can be written, but the other two are constant.
[CSR_UCCSR] = CSR_OP_FN_RW(umode, read_ccsr, write_ccsr, "uccsr"),
[CSR_SCCSR] = CSR_OP_FN_RW(smode, read_ccsr, write_ccsr, "sccsr"),
[CSR_MCCSR] = CSR_OP_FN_RW(any, read_ccsr, write_ccsr, "mccsr"),
Expand Down
12 changes: 8 additions & 4 deletions target/riscv/monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr,
monitor_printf(mon, TARGET_FMT_lx " " TARGET_FMT_plx " " TARGET_FMT_lx
" %c%c%c%c%c%c%c"
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
"%c%c"
"%c%c%c%c%c"
#endif
"\n",
addr_canonical(va_bits, vaddr),
Expand All @@ -80,8 +80,12 @@ static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr,
attr & PTE_A ? 'a' : '-',
attr & PTE_D ? 'd' : '-'
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
, attr & PTE_LC ? 'L' : '-'
, attr & PTE_SC ? 'S' : '-'
,
attr & PTE_CRG ? 'G' : '-',
attr & PTE_CRM ? 'M' : '-',
attr & PTE_CD ? 'D' : '-',
attr & PTE_CR ? 'R' : '-',
attr & PTE_CW ? 'W' : '-'
#endif
);
}
Expand Down Expand Up @@ -112,7 +116,7 @@ static void walk_pte(Monitor *mon, hwaddr base, target_ulong start,

paddr = (hwaddr)(pte >> PTE_PPN_SHIFT) << PGSHIFT;
#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32)
attr = pte & (PTE_LC | PTE_SC | 0xff);
attr = pte & (PTE_CR | PTE_CW | PTE_CD | PTE_CRM | PTE_CRG | 0xff);
nwf marked this conversation as resolved.
Show resolved Hide resolved
#else
attr = pte & 0xff;
#endif
Expand Down