From d2486def1517b985e02423e5d1fa40943b22f2c5 Mon Sep 17 00:00:00 2001 From: Robert Norton Date: Tue, 27 Apr 2021 17:24:22 +0100 Subject: [PATCH] Add initial memory versioning implementation. It is incomplete and may be slow / buggy notably: 1) version checks are performed during capability permissions checks, using the iotlb to cache a pointer to version memory, which is allocated alongside tag memory in cheri_tagmem.c. It uses one byte per version instead of a nibble like the ARM MTE so a bit space inefficient. 2) Loads and stores via DDC will throw an exception if DDC is versioned. 3) No system register. Note that this is the result of rebasing against upstream and simultaneously squashing the previous branch. There had been quite a lot of upstream changes with conflicts and some not very interesting history so it turned out to be easier that way. --- accel/tcg/cputlb.c | 25 ++- accel/tcg/probe-access.inc.c | 7 + disas/riscv.c | 31 ++- include/exec/cpu-all.h | 2 + include/exec/cpu-defs.h | 22 ++ include/exec/exec-all.h | 3 + include/hw/core/cpu.h | 2 + .../cheri_compressed_cap_128.h | 20 ++ .../cheri_compressed_cap_64.h | 2 + .../cheri_compressed_cap_common.h | 25 ++- target/cheri-common/cheri-helper-common.h | 6 + target/cheri-common/cheri-helper-utils.h | 16 ++ target/cheri-common/cheri-translate-utils.h | 3 +- target/cheri-common/cheri_defs.h | 12 ++ target/cheri-common/cheri_tagmem.c | 200 +++++++++++++++++- target/cheri-common/cheri_tagmem.h | 27 ++- target/cheri-common/cheri_utils.h | 6 + target/cheri-common/cpu_cheri.h | 3 + target/cheri-common/op_helper_cheri_common.c | 166 ++++++++++++++- target/mips/cheri-archspecific-early.h | 1 + target/mips/cheri-archspecific.h | 9 + target/riscv/cheri-archspecific-early.h | 1 + target/riscv/cheri-archspecific.h | 21 ++ target/riscv/cpu.c | 3 +- target/riscv/cpu.h | 1 + target/riscv/cpu_bits.h | 4 + target/riscv/cpu_helper.c | 37 +++- target/riscv/csr.c | 1 + target/riscv/insn32-cheri.decode | 11 +- target/riscv/insn_trans/trans_cheri.c.inc | 18 +- target/riscv/pmp.h | 2 + 31 files changed, 662 insertions(+), 25 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index ef225e0eb98..9de558427ac 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2,6 +2,7 @@ * Common CPU TLB handling * * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2021 Microsoft * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1123,9 +1124,11 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, * Getting tagmem can cause an invalidation, so best to do this before * any other entries are modified. */ + uintptr_t vermem; uintptr_t tagmem = (uintptr_t)cheri_tagmem_for_addr( - env, vaddr, section->mr->ram_block, xlat, size, &prot, tag_setting); + env, vaddr, section->mr->ram_block, xlat, size, &prot, tag_setting, (void **) &vermem); assert((tagmem & TLBENTRYCAP_MASK) == 0); + assert((vermem & TLBENTRYVER_MASK) == 0); #endif address = vaddr_page; @@ -1249,6 +1252,11 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, desc->iotlb[index].tagmem_write |= TLBENTRYCAP_FLAG_TRAP; } + desc->iotlb[index].vermem = vermem; + if (prot & PAGE_SV_TRAP) { + desc->iotlb[index].vermem |= TLBENTRYVER_TRAP; + } + #endif desc->iotlb[index].attrs = attrs; @@ -1459,7 +1467,9 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, #ifdef TARGET_CHERI if (cap_write && ((env_tlb(env)->d[mmu_idx].viotlb[vidx].tagmem_write & TLBENTRYCAP_INVALID_WRITE_MASK) == - TLBENTRYCAP_INVALID_WRITE_VALUE)) { + TLBENTRYCAP_INVALID_WRITE_VALUE || + env_tlb(env)->d[mmu_idx].viotlb[vidx].vermem == + TLBENTRYVER_INVALID)) { continue; } #endif @@ -1591,6 +1601,7 @@ probe_access_internal(CPUArchState *env, target_ulong addr, int fault_size, * done with a single probe. */ case MMU_DATA_CAP_STORE: + case MMU_VERSION_STORE: // we don't currently need the address but treat the same wrt TLB_NOTDIRTY etc. elt_ofs = offsetof(CPUTLBEntry, addr_write); break; case MMU_INST_FETCH: @@ -1613,11 +1624,18 @@ probe_access_internal(CPUArchState *env, target_ulong addr, int fault_size, .tagmem_write & TLBENTRYCAP_INVALID_WRITE_MASK) == TLBENTRYCAP_INVALID_WRITE_VALUE)) tag_write_invalid = true; + if (access_type == MMU_VERSION_STORE && + (env_tlb(env) + ->d[mmu_idx] + .iotlb[tlb_index(env, mmu_idx, addr)] + .vermem == TLBENTRYVER_INVALID)) + tag_write_invalid = true; #endif if (!tlb_hit_page(tlb_addr, page_addr) || tag_write_invalid) { if (!victim_tlb_hit(env, mmu_idx, index, elt_ofs, page_addr, - access_type == MMU_DATA_CAP_STORE)) { + access_type == MMU_DATA_CAP_STORE || + access_type == MMU_VERSION_STORE)) { CPUState *cs = env_cpu(env); CPUClass *cc = CPU_GET_CLASS(cs); @@ -1693,6 +1711,7 @@ probe_access_inlined(CPUArchState *env, target_ulong addr, int size, bool is_write = #ifdef TARGET_CHERI access_type == MMU_DATA_CAP_STORE || + access_type == MMU_VERSION_STORE || #endif access_type == MMU_DATA_STORE; int wp_access = is_write ? BP_MEM_WRITE : BP_MEM_READ; diff --git a/accel/tcg/probe-access.inc.c b/accel/tcg/probe-access.inc.c index f8608383313..d38fd0ff411 100644 --- a/accel/tcg/probe-access.inc.c +++ b/accel/tcg/probe-access.inc.c @@ -32,4 +32,11 @@ void *probe_cap_write(CPUArchState *env, target_ulong addr, int size, return probe_access_inlined(env, addr, size, MMU_DATA_CAP_STORE, mmu_idx, retaddr); } + +void *probe_ver_write(CPUArchState *env, target_ulong addr, int size, + int mmu_idx, uintptr_t retaddr) +{ + return probe_access_inlined(env, addr, size, MMU_VERSION_STORE, mmu_idx, + retaddr); +} #endif diff --git a/disas/riscv.c b/disas/riscv.c index eabb11474b6..7cb50943852 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -3,6 +3,7 @@ * * Copyright (c) 2016-2017 Michael Clark * Copyright (c) 2017-2018 SiFive, Inc. + * Copyright (c) 2021 Microsoft * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -517,6 +518,9 @@ typedef enum { rv_op_cgetaddr, rv_op_csealentry, rv_op_cloadtags, + rv_op_cgetversion, + rv_op_cloadversion, + rv_op_cstoreversion, // Three operand rv_op_cspecialrw, @@ -537,6 +541,8 @@ typedef enum { rv_op_ccseal, rv_op_ctestsubset, rv_op_cseqx, + rv_op_csetversion, + rv_op_camocdecversion, // FP loads/store rv_op_cflw, @@ -662,6 +668,7 @@ static const char rv_freg_name_sym[32][5] = { #define rv_fmt_cd_rs1 "O\tC0,1" #define rv_fmt_rs1_offset "O\t1,o" #define rv_fmt_rs2_offset "O\t2,o" +#define rv_fmt_cs1_rs2 "O\tC1,2" /* pseudo-instruction constraints */ @@ -1234,6 +1241,9 @@ const rv_opcode_data opcode_data[] = { [rv_op_cgetaddr] = { "cgetaddr", rv_codec_r, rv_fmt_rd_cs1, NULL, 0, 0, 0 }, [rv_op_csealentry] = { "csealentry", rv_codec_r, rv_fmt_cd_cs1, NULL, 0, 0, 0 }, [rv_op_cloadtags] = { "cloadtags", rv_codec_r, rv_fmt_rd_cs1, NULL, 0, 0, 0 }, + [rv_op_cgetversion] = { "cgetversion", rv_codec_r, rv_fmt_rd_cs1, NULL, 0, 0, 0 }, + [rv_op_cloadversion] = { "cloadversion", rv_codec_r, rv_fmt_rd_cs1, NULL, 0, 0, 0 }, + [rv_op_cstoreversion] = { "cstoreversion", rv_codec_r, rv_fmt_cs1_rs2, NULL, 0, 0, 0 }, // capmode loads: [rv_op_clb] = { "clb", rv_codec_i, rv_fmt_rd_offset_cs1, NULL, 0, 0, 0 }, @@ -1268,6 +1278,8 @@ const rv_opcode_data opcode_data[] = { [rv_op_ccseal] = { "ccseal", rv_codec_r, rv_fmt_cd_cs1_cs2, NULL, 0, 0, 0 }, [rv_op_ctestsubset] = { "ctestsubset", rv_codec_r, rv_fmt_rd_cs1_cs2, NULL, 0, 0, 0 }, [rv_op_cseqx] = { "cseqx", rv_codec_r, rv_fmt_rd_cs1_cs2, NULL, 0, 0, 0 }, + [rv_op_csetversion] = { "csetversion", rv_codec_r, rv_fmt_cd_cs1_rs2, NULL, 0, 0, 0 }, + [rv_op_camocdecversion] = { "camocdecversion", rv_codec_r, rv_fmt_rd_cs1_cs2, 0, 0, 0 }, // FP load store [rv_op_cflw] = { "cflw", rv_codec_i, rv_fmt_frd_offset_cs1, NULL, 0, 0, 0 }, @@ -1503,6 +1515,17 @@ static rv_opcode decode_cheri_two_op(unsigned func) { case 0b01111: return rv_op_cgetaddr; case 0b10001: return rv_op_csealentry; case 0b10010: return rv_op_cloadtags; + case 0b10011: return rv_op_cgetversion; + // case 0b10101: return rv_op-cloadversions; // XXX not yet + case 0b10110: return rv_op_cloadversion; + default: return rv_op_illegal; + } +} + +static rv_opcode decode_cheri_two_src_op(unsigned func) { + switch (func) { + //case 0b00001: return rv_op_cinvoke; TODO + case 0b00010: return rv_op_cstoreversion; default: return rv_op_illegal; } } @@ -1517,7 +1540,9 @@ static rv_opcode decode_cheri_inst(rv_inst inst) { switch (func) { // 0000000, unused CHERI_THREEOP_CASE(cspecialrw, 0000001, ..... ..... 000 ..... 1011011 @r) - // 0000010-0000111 unused + CHERI_THREEOP_CASE(csetversion, 0000010, ..... ..... 000 ..... 1011011 @r) + CHERI_THREEOP_CASE(camocdecversion, 0000011, ..... ..... 000 ..... 1011011 @r) + // 0000100-0000111 unused CHERI_THREEOP_CASE(csetbounds, 0001000, ..... ..... 000 ..... 1011011 @r) CHERI_THREEOP_CASE(csetboundsexact, 0001001, ..... ..... 000 ..... 1011011 @r) // 0001010 unused @@ -1540,7 +1565,9 @@ static rv_opcode decode_cheri_inst(rv_inst inst) { // 1111011 unused // TODO: 1111100 Used for Stores (see below) // TODO: 1111101 Used for Loads (see below) - // TODO: 1111110 Used for two source ops + // 1111110 Used for two source ops + case 0b111110: + return decode_cheri_two_src_op((inst >> 7) & 0b11111); // 1111111 Used for Source & Dest ops (see above) case 0b111111: return decode_cheri_two_op((inst >> 20) & 0b11111); diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index d46e1cd88cd..15c93130613 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -286,6 +286,8 @@ extern intptr_t qemu_host_page_mask; #define PAGE_C_BITS \ (PAGE_LC_CLEAR | PAGE_LC_TRAP | PAGE_SC_TRAP | PAGE_SC_CLEAR | \ PAGE_LC_TRAP_ANY) +/* Trap on store version */ +#define PAGE_SV_TRAP 0x80000 #else #define PAGE_C_BITS 0 #endif diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index f68a4506f39..5ff76ae5947 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -2,6 +2,7 @@ * common defines for all CPUs * * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2021 Microsoft * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -178,6 +179,17 @@ typedef struct CPUIOTLBEntry { (TLBENTRYCAP_FLAG_TRAP | TLBENTRYCAP_FLAG_CLEAR) #define TLBENTRYCAP_INVALID_WRITE_VALUE (TLBENTRYCAP_FLAG_TRAP) uintptr_t tagmem_write; + +/* Similar to tagmem above but there is no clearing access */ +#define TLBENTRYVER_MASK ((uintptr_t) 1) +/* Trap if attempting to write version */ +#define TLBENTRYVER_TRAP ((uintptr_t) 1) +#define TLBENTRYVER_INVALID ((uintptr_t) (~0 & ~TLBENTRYVER_MASK)) +/* Entry that reads as all zeros, attempts allocation on write */ +#define ALL_ZERO_VERMEM ((void *) TLBENTRYVER_INVALID) + /* Pointer to version memory for page. Will hopefully get away with single pointer + for both reads and writes. */ + uintptr_t vermem; #endif MemTxAttrs attrs; } CPUIOTLBEntry; @@ -189,6 +201,16 @@ typedef struct CPUIOTLBEntry { }) #define IOTLB_GET_TAGMEM_FLAGS(iotlbentry, rw) \ ((uintptr_t)iotlbentry->tagmem_##rw & TLBENTRYCAP_MASK); + +#define IOTLB_GET_VERMEM(iotlbentry) \ + ({ \ + cheri_debug_assert(iotlbentry->vermem != (uintptr_t)0); \ + (void *)((uintptr_t)iotlbentry->vermem & ~TLBENTRYVER_MASK); \ + }) + +#define IOTLB_GET_VERMEM_FLAGS(iotlbentry) \ + (iotlbentry->vermem & TLBENTRYVER_MASK) + /* * Data elements that are per MMU mode, minus the bits accessed by * the TCG fast path. diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index df5a0146aaa..3c453cda516 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -402,6 +402,9 @@ void *probe_read(CPUArchState *env, target_ulong addr, int size, int mmu_idx, #ifdef TARGET_CHERI void *probe_cap_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, uintptr_t retaddr); + +void *probe_ver_write(CPUArchState *env, target_ulong addr, int size, + int mmu_idx, uintptr_t retaddr); #endif /** diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 82289679f61..a0c5506c5c7 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -73,6 +73,8 @@ typedef enum MMUAccessType { MMU_INST_FETCH = 2, MMU_DATA_CAP_LOAD = 3, MMU_DATA_CAP_STORE = 4, + MMU_VERSION_LOAD = 5, /* XXX not currently used because version loads use same perms as data (apparently MMU_DATA_CAP_LOAD is the same) */ + MMU_VERSION_STORE = 6, } MMUAccessType; typedef struct CPUWatchpoint CPUWatchpoint; diff --git a/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_128.h b/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_128.h index 3d6b5fe692a..ca2bd1091c4 100644 --- a/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_128.h +++ b/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_128.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -56,6 +57,8 @@ * Morello embeds the user permissions in the middle of the hardware permissions. */ +#define CC_HAVE_VERSION 1 + // The following macros are expected to be defined #undef CC_BITS #define CC_BITS 128 @@ -125,7 +128,12 @@ enum { _CC_FIELD(HWPERMS, 123, 112), _CC_FIELD(RESERVED, 111, 110), _CC_FIELD(FLAGS, 109, 109), +#ifdef CC_HAVE_VERSION + _CC_FIELD(VERSION, 108, 105), + _CC_FIELD(OTYPE, 104, 91), +#else _CC_FIELD(OTYPE, 108, 91), +#endif _CC_FIELD(EBT, 90, 64), _CC_FIELD(INTERNAL_EXPONENT, 90, 90), @@ -149,6 +157,7 @@ enum { #define CC128_BOT_WIDTH CC128_FIELD_EXP_ZERO_BOTTOM_SIZE #define CC128_BOT_INTERNAL_EXP_WIDTH CC128_FIELD_EXP_NONZERO_BOTTOM_SIZE #define CC128_EXP_LOW_WIDTH CC128_FIELD_EXPONENT_LOW_PART_SIZE +#define CC128_VERSION_BITS CC128_FIELD_VERSION_SIZE #ifdef CC_IS_MORELLO @@ -244,14 +253,25 @@ enum _CC_N(OTypes) { _CC_STATIC_ASSERT_SAME(CC128_MANTISSA_WIDTH, CC128_FIELD_EXP_ZERO_BOTTOM_SIZE); +#ifdef CC_HAVE_VERSION +enum _CC_N(Versions) { + CC128_VERSION_UNVERSIONED = 0, + CC128_MAX_VERSION = ((1u << CC128_VERSION_BITS) - 1u), +}; +#endif + #include "cheri_compressed_cap_common.h" // Sanity-check mask is the expected NULL encoding #ifdef CC_IS_MORELLO _CC_STATIC_ASSERT_SAME(CC128_NULL_XOR_MASK, UINT64_C(0x0000000040070007)); #else +#ifdef CC_HAVE_VERSION +_CC_STATIC_ASSERT_SAME(CC128_NULL_XOR_MASK, UINT64_C(0x000001fffc018004)); +#else _CC_STATIC_ASSERT_SAME(CC128_NULL_XOR_MASK, UINT64_C(0x00001ffffc018004)); #endif +#endif __attribute__((deprecated("Use cc128_compress_raw"))) static inline uint64_t compress_128cap_without_xor(const cc128_cap_t* csp) { diff --git a/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_64.h b/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_64.h index d7304ea4f47..a4bf62d39b2 100644 --- a/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_64.h +++ b/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_64.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -84,6 +85,7 @@ enum { _CC_FIELD(EXPONENT_LOW_PART, 34, 32), _CC_FIELD(RESERVED, 31, 32), /* No reserved bits */ _CC_FIELD(UPERMS, 31, 32), /* No uperms */ + _CC_FIELD(VERSION, 31, 32), /* No version */ }; #pragma GCC diagnostic pop _CC_STATIC_ASSERT_SAME(CC64_FIELD_UPERMS_SIZE, 0); diff --git a/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_common.h b/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_common.h index bc0b9b3edfb..85dfbdc3eb4 100644 --- a/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_common.h +++ b/target/cheri-common/cheri-compressed-cap/cheri_compressed_cap_common.h @@ -3,6 +3,7 @@ * * Copyright (c) 2018 Lawrence Esswood * Copyright (c) 2018-2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -64,6 +65,9 @@ enum { _CC_N(NULL_PESBT) = _CC_ENCODE_FIELD(0, UPERMS) | _CC_ENCODE_FIELD(0, HWPERMS) | _CC_ENCODE_FIELD(0, RESERVED) | _CC_ENCODE_FIELD(0, FLAGS) | _CC_ENCODE_FIELD(1, INTERNAL_EXPONENT) | _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE) | +#ifdef CC_HAVE_VERSION + _CC_ENCODE_FIELD(_CC_N(VERSION_UNVERSIONED), VERSION) | +#endif _CC_ENCODE_FIELD(_CC_N(NULL_T), EXP_NONZERO_TOP) | _CC_ENCODE_FIELD(0, EXP_NONZERO_BOTTOM) | _CC_ENCODE_FIELD(_CC_N(NULL_EXP) >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), EXPONENT_HIGH_PART) | _CC_ENCODE_FIELD(_CC_N(NULL_EXP) & _CC_N(FIELD_EXPONENT_LOW_PART_MAX_VALUE), EXPONENT_LOW_PART), @@ -88,7 +92,7 @@ enum { #define _CC_MAX_TOP _CC_N(MAX_TOP) #define _CC_CURSOR_MASK _CC_N(CURSOR_MASK) // Check that the sizes of the individual fields match up -_CC_STATIC_ASSERT_SAME(_CC_N(FIELD_EBT_SIZE) + _CC_N(FIELD_OTYPE_SIZE) + _CC_N(FIELD_FLAGS_SIZE) + +_CC_STATIC_ASSERT_SAME(_CC_N(FIELD_EBT_SIZE) + _CC_N(FIELD_VERSION_SIZE) + _CC_N(FIELD_OTYPE_SIZE) + _CC_N(FIELD_FLAGS_SIZE) + _CC_N(FIELD_RESERVED_SIZE) + _CC_N(FIELD_HWPERMS_SIZE) + _CC_N(FIELD_UPERMS_SIZE), _CC_ADDR_WIDTH); _CC_STATIC_ASSERT_SAME(_CC_N(FIELD_INTERNAL_EXPONENT_SIZE) + _CC_N(FIELD_EXP_ZERO_TOP_SIZE) + @@ -113,6 +117,9 @@ _CC_STATIC_ASSERT(_CC_N(MAX_RESERVED_OTYPE) <= _CC_N(MAX_REPRESENTABLE_OTYPE), " struct _cc_N(cap); static inline uint8_t _cc_N(get_flags)(const struct _cc_N(cap)* cap); static inline uint32_t _cc_N(get_otype)(const struct _cc_N(cap)* cap); +#ifdef CC_HAVE_VERSION +static inline uint8_t _cc_N(get_version)(const struct _cc_N(cap)* cap); +#endif static inline uint32_t _cc_N(get_perms)(const struct _cc_N(cap)* cap); static inline uint8_t _cc_N(get_reserved)(const struct _cc_N(cap)* cap); static inline uint32_t _cc_N(get_uperms)(const struct _cc_N(cap)* cap); @@ -254,6 +261,9 @@ ALL_WRAPPERS(UPERMS, uperms, uint32_t) ALL_WRAPPERS(OTYPE, otype, uint32_t) ALL_WRAPPERS(FLAGS, flags, uint8_t) ALL_WRAPPERS(RESERVED, reserved, uint8_t) +#ifdef CC_HAVE_VERSION +ALL_WRAPPERS(VERSION, version, uint8_t) +#endif #undef ALL_WRAPPERS /// Extract the bits used for bounds and infer the top two bits of T @@ -696,7 +706,7 @@ static inline bool _cc_N(is_representable_new_addr)(bool sealed, _cc_addr_t base c._cr_cursor = cursor; // important to set as compress assumes this is in bounds c.cr_pesbt = _CC_ENCODE_FIELD(_CC_N(UPERMS_ALL), UPERMS) | _CC_ENCODE_FIELD(_CC_N(PERMS_ALL), HWPERMS) | - _CC_ENCODE_FIELD(sealed ? 42 : _CC_N(OTYPE_UNSEALED), OTYPE); + _CC_ENCODE_FIELD(sealed ? 42 : _CC_N(OTYPE_UNSEALED), OTYPE); /* XXX should set version too? seems redundant */ /* Get an EBT */ bool exact_input = false; _cc_N(update_ebt)(&c, _cc_N(compute_ebt)(base, top, NULL, &exact_input)); @@ -844,6 +854,9 @@ static inline bool _cc_N(setbounds_impl)(_cc_cap_t* cap, _cc_addr_t req_base, _c // TODO: find a faster way to compute top and bot: const _cc_addr_t pesbt = _CC_ENCODE_FIELD(0, UPERMS) | _CC_ENCODE_FIELD(0, HWPERMS) | +#ifdef CC_HAVE_VERSION + _CC_ENCODE_FIELD(_CC_N(VERSION_UNVERSIONED), VERSION) | /* I think setting this here is redundant but zero anyway... */ +#endif _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE) | _CC_ENCODE_FIELD(new_ebt, EBT); _cc_cap_t new_cap; _cc_N(decompress_raw)(pesbt, cursor, cap->cr_tag, &new_cap); @@ -918,8 +931,12 @@ static inline _cc_cap_t _cc_N(make_max_perms_cap)(_cc_addr_t base, _cc_addr_t cu creg._cr_cursor = cursor; creg.cr_bounds_valid = true; creg._cr_top = top; - creg.cr_pesbt = _CC_ENCODE_FIELD(_CC_N(UPERMS_ALL), UPERMS) | _CC_ENCODE_FIELD(_CC_N(PERMS_ALL), HWPERMS) | - _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE); + creg.cr_pesbt = _CC_ENCODE_FIELD(_CC_N(UPERMS_ALL), UPERMS) + | _CC_ENCODE_FIELD(_CC_N(PERMS_ALL), HWPERMS) +#ifdef CC_HAVE_VERSION + | _CC_ENCODE_FIELD(_CC_N(VERSION_UNVERSIONED), VERSION) +#endif + | _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE); creg.cr_tag = true; creg.cr_exp = _CC_N(NULL_EXP); bool exact_input = false; diff --git a/target/cheri-common/cheri-helper-common.h b/target/cheri-common/cheri-helper-common.h index ccf949a8a60..e2ec2f32d30 100644 --- a/target/cheri-common/cheri-helper-common.h +++ b/target/cheri-common/cheri-helper-common.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -66,6 +67,8 @@ DEF_HELPER_FLAGS_2(cgetoffset, 0, tl, env, i32) DEF_HELPER_FLAGS_2(cgetsealed, 0, tl, env, i32) DEF_HELPER_FLAGS_2(cgettag, 0, tl, env, i32) DEF_HELPER_FLAGS_2(cgettype, 0, tl, env, i32) +DEF_HELPER_FLAGS_2(cgetversion, 0, tl, env, i32) +DEF_HELPER_2(cloadversion, tl, env, i32) // Two operands (cap cap) DEF_HELPER_3(ccleartag, void, env, i32, i32) @@ -79,6 +82,7 @@ DEF_HELPER_3(ccheckperm, void, env, i32, tl) DEF_HELPER_3(cgetpccsetoffset, void, env, i32, tl) DEF_HELPER_3(cgetpccincoffset, void, env, i32, tl) DEF_HELPER_3(cgetpccsetaddr, void, env, i32, tl) +DEF_HELPER_3(cstoreversion, void, env, i32, tl) // Two operands (int int) DEF_HELPER_FLAGS_2(crap, TCG_CALL_NO_RWG_SE, tl, env, tl) @@ -118,12 +122,14 @@ DEF_HELPER_4(csetboundsexact, void, env, i32, i32, tl) DEF_HELPER_4(csetflags, void, env, i32, i32, tl) #endif DEF_HELPER_4(csetoffset, void, env, i32, i32, tl) +DEF_HELPER_4(csetversion, void, env, i32, i32, tl) // Three operands (int cap cap) DEF_HELPER_FLAGS_3(csub, 0, tl, env, i32, i32) DEF_HELPER_FLAGS_3(ctestsubset, 0, tl, env, i32, i32) DEF_HELPER_FLAGS_3(cseqx, 0, tl, env, i32, i32) DEF_HELPER_FLAGS_3(ctoptr, 0, tl, env, i32, i32) +DEF_HELPER_3(camocdecversion, tl, env, i32, i32) // Loads+Stores DEF_HELPER_4(cap_load_check, cap_checked_ptr, env, i32, tl, i32) diff --git a/target/cheri-common/cheri-helper-utils.h b/target/cheri-common/cheri-helper-utils.h index 1abe47ece0b..f29b63297fa 100644 --- a/target/cheri-common/cheri-helper-utils.h +++ b/target/cheri-common/cheri-helper-utils.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -39,6 +40,7 @@ #include "cheri_utils.h" #include "cheri-lazy-capregs.h" #include "cheri-bounds-stats.h" +#include "cheri_tagmem.h" #include "tcg/tcg.h" #include "tcg/tcg-op.h" #include "exec/exec-all.h" @@ -135,6 +137,7 @@ static inline void check_cap(CPUArchState *env, const cap_register_t *cr, #endif } +#ifdef TARGET_MIPS static inline target_ulong check_ddc(CPUArchState *env, uint32_t perm, target_ulong ddc_offset, uint32_t len, uintptr_t retpc) @@ -143,8 +146,10 @@ static inline target_ulong check_ddc(CPUArchState *env, uint32_t perm, target_ulong addr = ddc_offset + cap_get_cursor(ddc); check_cap(env, ddc, perm, addr, CHERI_EXC_REGNUM_DDC, len, /*instavail=*/true, retpc); + // NB since this function is used only on mips we don't need a version check return addr; } +#endif static inline bool cheri_have_access_sysregs(CPUArchState* env) { @@ -224,6 +229,7 @@ static inline const char* cheri_cause_str(CheriCapExcCause cause) { case CapEx_PermitCCallViolation: return "Permit_CCall Violation"; case CapEx_PermitUnsealViolation: return "Permit_Unseal Violation"; case CapEx_PermitSetCIDViolation: return "Permit_SetCID Violation"; + case CapEx_VersionViolation: return "Version Violation"; } // default: return "Unknown cause"; __builtin_unreachable(); @@ -352,6 +358,16 @@ cap_check_common_reg(uint32_t required_perms, CPUArchState *env, uint32_t cb, size, access_type, addr); #endif } +#if defined(TARGET_RISCV) && defined (CC_HAVE_VERSION) + cap_version_t ver = cap_get_version(cbp); + if (ver != CAP_VERSION_UNVERSIONED) { + /* This treats RMW as store */ + MMUAccessType rw = required_perms & CAP_PERM_STORE ? MMU_DATA_STORE : MMU_DATA_LOAD; + if (!cheri_version_check(env, addr, size, rw, _host_return_address, ver)) { + raise_version_exception(env, addr, _host_return_address); + } + } +#endif return addr; } diff --git a/target/cheri-common/cheri-translate-utils.h b/target/cheri-common/cheri-translate-utils.h index b7d582bf47b..45c9b6e0aa1 100644 --- a/target/cheri-common/cheri-translate-utils.h +++ b/target/cheri-common/cheri-translate-utils.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -207,7 +208,7 @@ static inline void _generate_special_checked_ptr( tcg_gen_mov_tl((TCGv)checked_addr, integer_addr); } - if (unlikely(!have_cheri_tb_flags(ctx, tb_perm_flags))) { + if (unlikely(!have_cheri_tb_flags(ctx, tb_perm_flags | (use_ddc ? TB_FLAG_CHERI_DDC_UNVERSIONED : 0)))) { // PCC/DDC is untagged, sealed, or missing permissions TCGv_i32 tperms = tcg_const_i32(req_perms); cheri_tcg_prepare_for_unconditional_exception(&ctx->base); diff --git a/target/cheri-common/cheri_defs.h b/target/cheri-common/cheri_defs.h index 90046cc832c..26fecab584a 100644 --- a/target/cheri-common/cheri_defs.h +++ b/target/cheri-common/cheri_defs.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -108,6 +109,15 @@ #define CAP_NULL_PESBT CAP_CC(NULL_PESBT) #define CAP_NULL_XOR_MASK CAP_CC(NULL_XOR_MASK) +#define CAP_VERSION_UNVERSIONED CAP_CC(VERSION_UNVERSIONED) +#define CAP_MAX_VERSION CAP_CC(MAX_VERSION) +/* size of memory version granule. In principle this is independent of CAP_SIZE + but in practice it will probably be the same. */ +# define CHERI_VERSION_GRANULE_SIZE (CHERI_CAP_SIZE) +/* Integer type big enough to hold a memory granule version. The actually + supported number of versions might be smaller e.g. 4-bits */ +typedef uint8_t cap_version_t; + typedef CAP_cc(cap_t) cap_register_t; typedef CAP_cc(offset_t) cap_offset_t; typedef CAP_cc(length_t) cap_length_t; @@ -170,6 +180,8 @@ typedef enum CheriTbFlags { TB_FLAG_CHERI_PCC_FULL_AS = TB_FLAG_CHERI_PCC_BASE_ZERO | TB_FLAG_CHERI_PCC_TOP_MAX, TB_FLAG_CHERI_PCC_READABLE = (1 << 9), + /* DDC is tagged, unsealed and unversioned */ + TB_FLAG_CHERI_DDC_UNVERSIONED = (1 << 10), /* Useful for CHERI-specific flags on various platforms if the normal flags overflowed */ diff --git a/target/cheri-common/cheri_tagmem.c b/target/cheri-common/cheri_tagmem.c index ca4f822b5c2..ede48bafeb4 100644 --- a/target/cheri-common/cheri_tagmem.c +++ b/target/cheri-common/cheri_tagmem.c @@ -4,6 +4,7 @@ * Copyright (c) 2015-2016 Stacey Son * Copyright (c) 2016-2018 Alfredo Mazzinghi * Copyright (c) 2016-2018 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -123,9 +124,14 @@ static inline size_t num_tagblocks(RAMBlock* ram) typedef struct CheriTagBlock { DECLARE_BITMAP(tag_bitmap, CAP_TAGBLK_SIZE); + cap_version_t mem_versions[CAP_TAGBLK_SIZE]; } CheriTagBlock; +/** + * Creates a new zero initialised CheriTagBlock for given ram and tagidx and + * sets the pointer in the ram->cheri_tags first-level array. + */ static CheriTagBlock *cheri_tag_new_tagblk(RAMBlock *ram, uint64_t tagidx) { CheriTagBlock *tagblk, *old; @@ -151,6 +157,10 @@ static CheriTagBlock *cheri_tag_new_tagblk(RAMBlock *ram, uint64_t tagidx) } } +/** + * Returns the CheriTagBlock for given tag_index in RAMBlock. May return NULL if + * not yet allocated. + */ static inline QEMU_ALWAYS_INLINE CheriTagBlock *cheri_tag_block(size_t tag_index, RAMBlock *ram) { @@ -196,6 +206,18 @@ tagblock_set_tag_tagmem(void *tagmem, size_t block_index) qatomic_or(p, BIT_MASK(block_index)); } +static inline QEMU_ALWAYS_INLINE cap_version_t tagblock_get_version(CheriTagBlock *block, + size_t block_index) +{ + return block ? block->mem_versions[block_index] : CAP_VERSION_UNVERSIONED; +} + +static inline QEMU_ALWAYS_INLINE void tagblock_set_version(CheriTagBlock *block, + size_t block_index, cap_version_t v) +{ + block->mem_versions[block_index] = v; +} + static inline QEMU_ALWAYS_INLINE void tagblock_set_tag_many_tagmem(void *tagmem, size_t block_index, uint8_t tags) { @@ -231,6 +253,22 @@ static inline QEMU_ALWAYS_INLINE void tagblock_clear_tag(CheriTagBlock *block, tagblock_clear_tag_tagmem(block->tag_bitmap, block_index); } +static inline QEMU_ALWAYS_INLINE cap_version_t get_version(size_t index, RAMBlock *ram) +{ + return tagblock_get_version(cheri_tag_block(index, ram), CAP_TAGBLK_IDX(index)); +} + +static inline QEMU_ALWAYS_INLINE void set_version(size_t index, RAMBlock *ram, + bool *allocated, cap_version_t val) +{ + CheriTagBlock *block = cheri_tag_block(index, ram); + if (!block) { + block = cheri_tag_new_tagblk(ram, index); + *allocated = true; + } + tagblock_set_version(block, CAP_TAGBLK_IDX(index), val); +} + void cheri_tag_init(MemoryRegion *mr, uint64_t memory_size) { assert(memory_region_is_ram(mr)); @@ -255,13 +293,15 @@ void cheri_tag_init(MemoryRegion *mr, uint64_t memory_size) void *cheri_tagmem_for_addr(CPUArchState *env, target_ulong vaddr, RAMBlock *ram, ram_addr_t ram_offset, size_t size, - int *prot, bool tag_write) + int *prot, bool tag_write, void **vermem_out) { if (unlikely(!ram || !ram->cheri_tags)) { /* Tags stored here are effectively cleared (unless they should trap) */ if (!(*prot & PAGE_SC_TRAP)) { *prot |= PAGE_SC_CLEAR; + /* Attempting to set versions here is a guest bug -- trap*/ + *prot |= PAGE_SV_TRAP; } if (tag_write) { error_report("Attempting change tag bit on memory without tags:"); @@ -269,6 +309,7 @@ void *cheri_tagmem_for_addr(CPUArchState *env, target_ulong vaddr, (uintmax_t)vaddr, ram ? ram->idstr : NULL, (uintmax_t)ram_offset); } + *vermem_out = ALL_ZERO_VERMEM; return ALL_ZERO_TAGBLK; } @@ -298,6 +339,7 @@ void *cheri_tagmem_for_addr(CPUArchState *env, target_ulong vaddr, if (tagblk != NULL) { const size_t tagblk_index = CAP_TAGBLK_IDX(tag); + *vermem_out = &(tagblk->mem_versions[tagblk_index]); return tagblk->tag_bitmap + BIT_WORD(tagblk_index); } @@ -306,7 +348,9 @@ void *cheri_tagmem_for_addr(CPUArchState *env, target_ulong vaddr, // to this location. See the comment around TLBENTRYCAP_INVALID_WRITE_*. *prot |= PAGE_SC_TRAP; } - + // similarly trap any attempts to write zero version pages + *prot |= PAGE_SV_TRAP; + *vermem_out = ALL_ZERO_VERMEM; return ALL_ZERO_TAGBLK; } @@ -685,3 +729,155 @@ void cheri_tag_set_many(CPUArchState *env, uint32_t tags, target_ulong vaddr, tagblock_set_tag_many_tagmem(tagmem, page_vaddr_to_tag_offset(vaddr), tags); } + +/** + * Returns the version associated with given index in vermem. Vermem is the + * version memory cached in the iotlb and index is the tag index in page. Note + * we might use this abstraction to make version storage more efficient in future + * (e.g. one version per nibble iso one per byte) + */ +static inline QEMU_ALWAYS_INLINE cap_version_t vermem_get_ver(void *vermem, size_t index) +{ + if (vermem == ALL_ZERO_VERMEM) + return 0; + cap_version_t *varray = (cap_version_t *) vermem; + return varray[index]; +} + +/** + * As above vermem_get_ver but sets the version at index in vermem to v. + */ +static inline QEMU_ALWAYS_INLINE void +vermem_set_ver(void *vermem, size_t index, cap_version_t v) +{ + g_assert(vermem != ALL_ZERO_VERMEM); // should have allocated vermem before reaching here + cap_version_t *varray = (cap_version_t *) vermem; + varray[index] = v; +} + +static inline void *get_vermem_from_iotlb_entry(CPUArchState *env, + target_ulong vaddr, int mmu_idx, + uintptr_t *flags_out) +{ + /* XXXAR: see mte_helper.c */ + /* + * Find the iotlbentry for ptr. This *must* be present in the TLB + * because we just found the mapping. + * TODO: Perhaps there should be a cputlb helper that returns a + * matching tlb entry + iotlb entry. + */ +#ifdef CONFIG_DEBUG_TCG + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, vaddr); + g_assert(tlb_hit(isWrite ? tlb_addr_write(entry) : entry->addr_read, vaddr)); +#endif + CPUIOTLBEntry *iotlbentry = + &env_tlb(env)->d[mmu_idx].iotlb[tlb_index(env, mmu_idx, vaddr)]; + if (flags_out != NULL) + *flags_out = IOTLB_GET_VERMEM_FLAGS(iotlbentry); + return IOTLB_GET_VERMEM(iotlbentry); +} + +cap_version_t cheri_version_get_aligned(CPUArchState *env, target_ulong vaddr, int reg, + hwaddr *ret_paddr, int *prot, uintptr_t pc) +{ + const int mmu_idx = cpu_mmu_index(env, false); + /* XXX caller needs to ensure read MMUAccesType is apporpriate */ + void *host_addr = probe_read(env, vaddr, 1, mmu_idx, pc); + handle_paddr_return(read); /* XXX ret_paddr may not be neeeded. */ + + uintptr_t vermem_flags; + void *vermem = get_vermem_from_iotlb_entry(env, vaddr, mmu_idx, &vermem_flags); + + /* XXX prot is probably not needed either */ + if (prot) { + *prot = 0; + if (vermem_flags & TLBENTRYVER_TRAP) { + *prot |= PAGE_SV_TRAP; + } + } + + cap_version_t result = vermem_get_ver(vermem, page_vaddr_to_tag_offset(vaddr)); + + // XXX: Not atomic w.r.t. writes to tag memory + qemu_maybe_log_instr_extra( + env, " Cap Version Read [" TARGET_FMT_lx "/" RAM_ADDR_FMT "] -> %d\n", + vaddr, qemu_ram_addr_from_host(host_addr), result); + return result; +} + +void cheri_version_set_aligned(CPUArchState *env, target_ulong vaddr, int reg, hwaddr* ret_paddr, uintptr_t pc, cap_version_t val) +{ + const int mmu_idx = cpu_mmu_index(env, false); + store_capcause_reg(env, reg); + // XXX FIXME should be probe_ver_write but that results in page fault loop! + void *host_addr = probe_ver_write(env, vaddr, 1, mmu_idx, pc); + clear_capcause_reg(env); + + handle_paddr_return(write); /* is ret_paddr needed? */ + + if (unlikely(!host_addr)) { + /* Guest bug? Warn? */ + warn_report("Attempt to set version on non-RAM " + "via vaddr 0x" TARGET_FMT_lx "\r\n", vaddr); + return; + } + + uintptr_t vermem_flags; + void *vermem = get_vermem_from_iotlb_entry(env, vaddr, mmu_idx, &vermem_flags); + + if (vermem_flags & TLBENTRYVER_TRAP) { + raise_store_ver_exception(env, vaddr, reg, pc); + } + + /* + * probe_ver_write() should have ensured there was a tagmem for this + * location. A NULL ram should have been indicated via + * TLBENTRYVER_TRAP. + */ + cheri_debug_assert(vermem != ALL_ZERO_VERMEM); + + target_ulong tag_offset = page_vaddr_to_tag_offset(vaddr); + + qemu_maybe_log_instr_extra( + env, " Cap Version Write [" TARGET_FMT_lx "/" RAM_ADDR_FMT "] %d -> %d\n", + vaddr, qemu_ram_addr_from_host(host_addr), + vermem_get_ver(vermem, tag_offset), val); + + vermem_set_ver(vermem, tag_offset, val); +} + +static bool cheri_version_check_one(CPUArchState *env, target_ulong vaddr, + MMUAccessType rw, uintptr_t pc, cap_version_t expected) +{ + const int mmu_idx = cpu_mmu_index(env, false); + probe_access(env, vaddr, 1, rw, mmu_idx, pc); + + void *vermem = get_vermem_from_iotlb_entry(env, vaddr, mmu_idx, NULL); + target_ulong tag_offset = page_vaddr_to_tag_offset(vaddr); + cap_version_t result = vermem_get_ver(vermem, tag_offset); + return result == expected; +} + +bool cheri_version_check(CPUArchState *env, target_ulong vaddr, int32_t size, + MMUAccessType rw, uintptr_t pc, cap_version_t expected) +{ + cheri_debug_assert(size > 0); + target_ulong first_addr = vaddr; + target_ulong last_addr = (vaddr + size - 1); + TagOffset tag_start = addr_to_tag_offset(first_addr); + TagOffset tag_end = addr_to_tag_offset(last_addr); + if (likely(tag_start.value == tag_end.value)) { + // Common case, only one granule (i.e. aligned load / store) + return cheri_version_check_one(env, vaddr, rw, pc, expected); + } + // Unaligned -> can cross a capabiblity alignment boundary and + // therefore invalidate two tags. It can also cross pages + size_t ntags = tag_end.value - tag_start.value + 1; + assert(ntags == 2 && "Should check at most two version granules here"); + bool success = true; + for (target_ulong addr = tag_offset_to_addr(tag_start); + addr <= tag_offset_to_addr(tag_end); addr += CHERI_CAP_SIZE) { + success &= cheri_version_check_one(env, addr, rw, pc, expected); + } + return success; +} diff --git a/target/cheri-common/cheri_tagmem.h b/target/cheri-common/cheri_tagmem.h index f5930fe01be..56fbdd32b4d 100644 --- a/target/cheri-common/cheri_tagmem.h +++ b/target/cheri-common/cheri_tagmem.h @@ -4,6 +4,7 @@ * Copyright (c) 2015-2016 Stacey Son * Copyright (c) 2016-2018 Alfredo Mazzinghi * Copyright (c) 2016-2018 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -82,5 +83,29 @@ void *cheri_tag_set(CPUArchState *env, target_ulong vaddr, int reg, void *cheri_tagmem_for_addr(CPUArchState *env, target_ulong vaddr, RAMBlock *ram, ram_addr_t ram_offset, size_t size, - int *prot, bool tag_write); + int *prot, bool tag_write, void **vermem_out); + +/** + * Returns the version of the granule-aligned @vaddr + * @reg XXX is redundant on RISC-V + * @ret_paddr is used to return the physical address if non-NULL + * @prot returns protection attributes form translation. Must not be NULL. + * May cause translation faults as for data loads. + */ +cap_version_t cheri_version_get_aligned(CPUArchState *env, target_ulong vaddr, int reg, + hwaddr *ret_paddr, int *prot, uintptr_t pc); +/** + * Sets the version of the granule-aligned @vaddr to @val. + * @reg XXX is redundant on RISC-V + * @ret_paddr is used to return the physical address fr + * May cause translation faults as for data stores. + */ +void cheri_version_set_aligned(CPUArchState *env, target_ulong vaddr, int reg, hwaddr* ret_paddr, uintptr_t pc, cap_version_t val); +/** + * Returns true if the memory version matches the give @expected version for all + * granules in [vaddr, vaddr+size). May cause translation faults as per the + * access type rw. + */ +bool cheri_version_check(CPUArchState *env, target_ulong vaddr, int32_t size, + MMUAccessType rw, uintptr_t pc, cap_version_t expected); #endif /* TARGET_CHERI */ diff --git a/target/cheri-common/cheri_utils.h b/target/cheri-common/cheri_utils.h index fa42d2761fc..dd5277d8487 100644 --- a/target/cheri-common/cheri_utils.h +++ b/target/cheri-common/cheri_utils.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -97,6 +98,11 @@ static inline uint8_t cap_get_flags(const cap_register_t *c) return CAP_cc(get_flags)(c); } +static inline cap_version_t cap_get_version(const cap_register_t *c) +{ + return CAP_cc(get_version)(c); +} + static inline bool cap_has_reserved_bits_set(const cap_register_t *c) { return CAP_cc(get_reserved)(c) != 0; diff --git a/target/cheri-common/cpu_cheri.h b/target/cheri-common/cpu_cheri.h index 53943b02085..d092231bc04 100644 --- a/target/cheri-common/cpu_cheri.h +++ b/target/cheri-common/cpu_cheri.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -163,6 +164,8 @@ static inline void cheri_cpu_get_tb_cpu_state(const cap_register_t *pcc, *cheri_flags |= TB_FLAG_CHERI_DDC_CURSOR_ZERO; if (cap_get_top_full(ddc) == CAP_MAX_TOP) *cheri_flags |= TB_FLAG_CHERI_DDC_TOP_MAX; + if (cap_get_version(ddc) == CAP_VERSION_UNVERSIONED) + *cheri_flags |= TB_FLAG_CHERI_DDC_UNVERSIONED; } } diff --git a/target/cheri-common/op_helper_cheri_common.c b/target/cheri-common/op_helper_cheri_common.c index 78b28c22636..54c168ba7bb 100644 --- a/target/cheri-common/op_helper_cheri_common.c +++ b/target/cheri-common/op_helper_cheri_common.c @@ -4,6 +4,7 @@ * Copyright (c) 2015-2016 Stacey Son * Copyright (c) 2016-2018 Alfredo Mazzinghi * Copyright (c) 2016-2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -152,6 +153,20 @@ void CHERI_HELPER_IMPL(ddc_check_bounds(CPUArchState *env, target_ulong addr, "Should have been checked before bounds!"); check_cap(env, ddc, 0, addr, CHERI_EXC_REGNUM_DDC, num_bytes, /*instavail=*/true, GETPC()); + // We don't currently support versioned DDC because: + // 1) because we're not sure of the exact semantics we want, is it useful? + // 2) we would need to pass the memory access type to this helper so we get the + // correct MMU fault in cheri_version_check (not a big deal but not done + // right now) + // Instead, currently we just generate an unconditional exception if DDC is + // versioned (see _generate_ddc_checked_ptr). + // The check could look something like this: + // if (ddc->cr_version != CAP_VERSION_UNVERSIONED) { + // MMUAccessType rw = required_perms & CAP_PERM_STORE ? MMU_DATA_STORE : MMU_DATA_CAP_LOAD; + // if (!cheri_version_check(env, addr, len, rw, retpc, ddc->cr_version)) { + // raise_version_exception(env, addr, _host_return_address); + // } + // } } #ifdef TARGET_AARCH64 @@ -332,6 +347,130 @@ target_ulong CHERI_HELPER_IMPL(cgettype(CPUArchState *env, uint32_t cb)) return otype; } +target_ulong CHERI_HELPER_IMPL(cgetversion(CPUArchState *env, uint32_t cb)) +{ + /* + * CGetVersion: Move Version Field to a General-Purpose Register. + */ + const cap_register_t *cbp = get_readonly_capreg(env, cb); + return (target_ulong) cap_get_version(cbp); +} + +target_ulong CHERI_HELPER_IMPL(cloadversion(CPUArchState *env, uint32_t cb)) +{ + /* + * CLoadVersion: Load Version for granule to a General-Purpose Register. + * TODO reduce code duplication with cap_check_common? + */ + GET_HOST_RETPC(); + const cap_register_t *cbp = get_readonly_capreg(env, cb); + if (!cbp->cr_tag) { + raise_cheri_exception(env, CapEx_TagViolation, cb); + } else if (is_cap_sealed(cbp)) { + raise_cheri_exception(env, CapEx_SealViolation, cb); + } else if (cap_get_version(cbp) != CAP_VERSION_UNVERSIONED) { + raise_cheri_exception(env, CapEx_VersionViolation, cb); + } else if (!(cap_get_perms(cbp) & CAP_PERM_LOAD)) { + raise_cheri_exception(env, CapEx_PermitLoadViolation, cb); + } + const target_ulong addr = cap_get_cursor(cbp); + if (!cap_is_in_bounds(cbp, addr, CHERI_VERSION_GRANULE_SIZE)) { + qemu_log_instr_or_mask_msg(env, CPU_LOG_INT, + "Failed capability bounds check: cursor=" TARGET_FMT_plx "\n", addr); + raise_cheri_exception(env, CapEx_LengthViolation, cb); + } + if (!QEMU_IS_ALIGNED(addr, CHERI_VERSION_GRANULE_SIZE)) { + raise_unaligned_load_exception(env, addr, _host_return_address); + } + int prot; + cap_version_t ver = cheri_version_get_aligned(env, addr, cb, /*physaddr_out=*/NULL, &prot, _host_return_address); + /* XXX should we use prot? */ + (void) prot; + return (target_ulong) ver; +} + +void CHERI_HELPER_IMPL(cstoreversion(CPUArchState *env, uint32_t cb, + target_ulong version)) +{ + /* + * CStoreVersion: Set version of a granule in memory. + * TODO reduce code duplication with cap_check_common? + */ + GET_HOST_RETPC(); + const cap_register_t *cbp = get_readonly_capreg(env, cb); + if (!cbp->cr_tag) { + raise_cheri_exception(env, CapEx_TagViolation, cb); + } else if (is_cap_sealed(cbp)) { + raise_cheri_exception(env, CapEx_SealViolation, cb); + } else if (cap_get_version(cbp) != CAP_VERSION_UNVERSIONED) { + raise_cheri_exception(env, CapEx_VersionViolation, cb); + } else if (!(cap_get_perms(cbp) & CAP_PERM_STORE)) { + raise_cheri_exception(env, CapEx_PermitStoreViolation, cb); + } + const target_ulong addr = cap_get_cursor(cbp); + if (!cap_is_in_bounds(cbp, addr, CHERI_VERSION_GRANULE_SIZE)) { + qemu_log_instr_or_mask_msg(env, CPU_LOG_INT, + "Failed capability bounds check: cursor=" TARGET_FMT_plx "\n", addr); + raise_cheri_exception(env, CapEx_LengthViolation, cb); + } + if (!QEMU_IS_ALIGNED(addr, CHERI_VERSION_GRANULE_SIZE)) { + raise_unaligned_load_exception(env, addr, _host_return_address); + } + cheri_version_set_aligned(env, addr, cb, NULL, _host_return_address, version & CAP_MAX_VERSION); +} + +target_ulong CHERI_HELPER_IMPL(camocdecversion(CPUArchState *env, uint32_t cb, + uint32_t cs2)) +{ + /* + * CAmoCDecVersion: Atomically decrement version of a granule in memory, + * comparing with expected version in cs2. + * TODO reduce code duplication with cap_check_common? + * XXX should also check for PERMIT_LOAD? + */ + GET_HOST_RETPC(); + const cap_register_t *cbp = get_readonly_capreg(env, cb); + const cap_register_t *cs2p = get_readonly_capreg(env, cs2); + if (!cbp->cr_tag) { + raise_cheri_exception(env, CapEx_TagViolation, cb); + } else if (!cs2p->cr_tag) { + raise_cheri_exception(env, CapEx_TagViolation, cs2); + } else if (is_cap_sealed(cbp)) { + raise_cheri_exception(env, CapEx_SealViolation, cb); + } else if (is_cap_sealed(cs2p)) { + raise_cheri_exception(env, CapEx_SealViolation, cs2); + } else if (cap_get_version(cbp) != CAP_VERSION_UNVERSIONED) { + raise_cheri_exception(env, CapEx_VersionViolation, cb); + } else if (!(cap_get_perms(cbp) & CAP_PERM_STORE)) { + raise_cheri_exception(env, CapEx_PermitStoreViolation, cb); + } else if (!(cap_get_perms(cs2p) & CAP_PERM_STORE)) { + raise_cheri_exception(env, CapEx_PermitStoreViolation, cs2); + } + const target_ulong addr = cap_get_cursor(cbp); + if (!cap_is_in_bounds(cbp, addr, CHERI_VERSION_GRANULE_SIZE)) { + qemu_log_instr_or_mask_msg(env, CPU_LOG_INT, + "Failed capability bounds check: cursor=" TARGET_FMT_plx "\n", addr); + raise_cheri_exception(env, CapEx_LengthViolation, cb); + } + if (!QEMU_IS_ALIGNED(addr, CHERI_VERSION_GRANULE_SIZE)) { + raise_unaligned_load_exception(env, addr, _host_return_address); + } + + int prot; + cap_version_t expected_ver = cap_get_version(cs2p); + cap_version_t mem_ver = cheri_version_get_aligned(env, addr, cb, /*physaddr_out=*/NULL, &prot, _host_return_address); + /* XXX should we use prot? */ + (void) prot; + if (expected_ver == mem_ver) { + cap_version_t new_ver = (expected_ver == 0) ? CAP_MAX_VERSION : (expected_ver - 1); + cheri_version_set_aligned(env, addr, cb, NULL, _host_return_address, new_ver); + return new_ver == CAP_VERSION_UNVERSIONED ? -1 : 1; + } else { + /* Version mismatch -- return failure */ + return 0; + } +} + /// Two operands (both capabilities) void CHERI_HELPER_IMPL(ccleartag(CPUArchState *env, uint32_t cd, uint32_t cb)) @@ -1035,6 +1174,28 @@ void CHERI_HELPER_IMPL(csetflags(CPUArchState *env, uint32_t cd, uint32_t cb, } #endif +void CHERI_HELPER_IMPL(csetversion(CPUArchState *env, uint32_t cd, uint32_t cb, + target_ulong version)) +{ + const cap_register_t *cbp = get_readonly_capreg(env, cb); + GET_HOST_RETPC(); + /* + * CSetVersion: Set capability version + */ + if (!cbp->cr_tag) { + raise_cheri_exception(env, CapEx_TagViolation, cb); + } else if (!cap_is_unsealed(cbp)) { + raise_cheri_exception(env, CapEx_SealViolation, cb); + } else if (cap_get_version(cbp) != CAP_VERSION_UNVERSIONED) { + raise_cheri_exception(env, CapEx_VersionViolation, cb); + } + cap_register_t result = *cbp; + version &= CAP_MAX_VERSION; + _Static_assert(((CAP_MAX_VERSION + 1) & CAP_MAX_VERSION) == 0, "Expected power of two CAP_MAX_VERSION"); + CAP_cc(update_version)(&result, version); + update_capreg(env, cd, &result); +} + /// Three operands (int capability capability) // static inline bool cap_bounds_are_subset(const cap_register_t *first, const @@ -1528,7 +1689,10 @@ void CHERI_HELPER_IMPL(raise_exception_ddc_perms(CPUArchState *env, uint32_t required_perms)) { const cap_register_t *ddc = cheri_get_ddc(env); - + if (cap_get_version(ddc) != CAP_VERSION_UNVERSIONED) { + GET_HOST_RETPC(); + raise_cheri_exception(env, CapEx_VersionViolation, CHERI_EXC_REGNUM_DDC); + } cap_check_common_reg(required_perms, env, CHERI_EXC_REGNUM_DDC, addr, 1, GETPC(), ddc, 1, NULL); error_report("%s should not return! DDC= " PRINT_CAP_FMTSTR, __func__, diff --git a/target/mips/cheri-archspecific-early.h b/target/mips/cheri-archspecific-early.h index 76532bec1d2..7fba65715fa 100644 --- a/target/mips/cheri-archspecific-early.h +++ b/target/mips/cheri-archspecific-early.h @@ -92,6 +92,7 @@ typedef enum CheriCapExc { CapEx_AccessCCallIDCViolation = 0x1A, /* Access IDC in a CCall delay slot */ CapEx_PermitUnsealViolation = 0x1B, /* Permit_Unseal violation */ CapEx_PermitSetCIDViolation = 0x1C, /* Permit_SetCID violation */ + CapEx_VersionViolation = 0x1D, /* Version violation */ // 0x1d-0x1f Reserved } CheriCapExcCause; diff --git a/target/mips/cheri-archspecific.h b/target/mips/cheri-archspecific.h index 3c479635d29..9923d50b5a6 100644 --- a/target/mips/cheri-archspecific.h +++ b/target/mips/cheri-archspecific.h @@ -118,6 +118,15 @@ static inline void QEMU_NORETURN raise_store_tag_exception(CPUArchState *env, do_raise_c2_exception_impl(env, CapEx_TLBNoStoreCap, cb, retpc); } +static inline void QEMU_NORETURN raise_store_ver_exception(CPUArchState *env, + target_ulong va, + int cb, + uintptr_t retpc) +{ + env->CP0_BadVAddr = va; + do_raise_c2_exception_impl(env, CapEx_TLBNoStoreCap, cb, retpc); +} + static inline void QEMU_NORETURN raise_unaligned_load_exception( CPUArchState *env, target_ulong addr, uintptr_t retpc) { diff --git a/target/riscv/cheri-archspecific-early.h b/target/riscv/cheri-archspecific-early.h index 0f33cb0cef1..731809d2e38 100644 --- a/target/riscv/cheri-archspecific-early.h +++ b/target/riscv/cheri-archspecific-early.h @@ -73,6 +73,7 @@ typedef enum CheriCapExc { CapEx_AccessCCallIDCViolation = 0x1A, CapEx_PermitUnsealViolation = 0x1B, CapEx_PermitSetCIDViolation = 0x1C, + CapEx_VersionViolation = 0x1D, // 0x1d - 0x1f reserved } CheriCapExcCause; diff --git a/target/riscv/cheri-archspecific.h b/target/riscv/cheri-archspecific.h index 2c86a4e92da..897c2601fd6 100644 --- a/target/riscv/cheri-archspecific.h +++ b/target/riscv/cheri-archspecific.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -75,6 +76,19 @@ static inline void QEMU_NORETURN raise_store_tag_exception(CPUArchState *env, #endif } +static inline void QEMU_NORETURN raise_store_ver_exception(CPUArchState *env, + target_ulong va, + int reg, + uintptr_t retpc) +{ +#ifdef TARGET_RISCV32 + g_assert_not_reached(); +#else + env->badaddr = va; + riscv_raise_exception(env, RISCV_EXCP_STORE_AMO_VER_PAGE_FAULT, retpc); +#endif +} + static inline void QEMU_NORETURN raise_unaligned_load_exception( CPUArchState *env, target_ulong addr, uintptr_t retpc) { @@ -91,6 +105,13 @@ static inline void QEMU_NORETURN raise_unaligned_store_exception( } +static inline void QEMU_NORETURN raise_version_exception ( + CPUArchState *env, target_ulong addr, uintptr_t retpc) +{ + env->badaddr = addr; + riscv_raise_exception(env, RISCV_EXCP_VERSION, retpc); +} + static inline bool validate_jump_target(CPUArchState *env, const cap_register_t *cap, target_ulong addr, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 27c4dd32324..0229c691c75 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -106,7 +106,8 @@ const char * const riscv_excp_names[] = { [RISCV_EXCP_LOAD_CAP_PAGE_FAULT] = "load_cap_page_fault", [RISCV_EXCP_STORE_AMO_CAP_PAGE_FAULT] = "store_cap_page_fault", #endif - [RISCV_EXCP_CHERI] = "cheri_fault" + [RISCV_EXCP_CHERI] = "cheri_fault", + [RISCV_EXCP_VERSION] = "cheri_version_fault", #endif // 32–47 Reserved for future standard use // 48-63 Reserved for custom use diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 7e43fdadf3f..fa710f6e34e 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -91,6 +91,7 @@ enum { TRANSLATE_G_STAGE_FAIL, #if defined(TARGET_CHERI) && !defined(TARGET_RISCV32) TRANSLATE_CHERI_FAIL, + TRANSLATE_VERSION_FAIL, #endif }; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 0f75ae35e4e..df2e5922c96 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -532,6 +532,7 @@ #define PTE_D 0x080 /* Dirty */ #define PTE_SOFT 0x300 /* Reserved for Software */ #if defined(TARGET_CHERI) && !defined(TARGET_RISCV32) +#define PTE_CWV 0x0400000000000000 /* Version write */ #define PTE_CRG 0x0800000000000000 /* Cap Read Generation */ #define PTE_CRM 0x1000000000000000 /* Cap Read Modifier */ #define PTE_CD 0x2000000000000000 /* Cap Dirty */ @@ -575,6 +576,9 @@ #define RISCV_EXCP_STORE_AMO_CAP_PAGE_FAULT 0x1b #endif #define RISCV_EXCP_CHERI 0x1c +#define RISCV_EXCP_VERSION 0x1d +/* XXX we don't currently distinguish betwen version mismatch and page fault but probably should ... */ +#define RISCV_EXCP_STORE_AMO_VER_PAGE_FAULT RISCV_EXCP_VERSION #endif #define RISCV_EXCP_INT_FLAG 0x80000000 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index cfea792dcd8..2d1b7d1c167 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -619,7 +619,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, /* Read access check failed */ return TRANSLATE_FAIL; } else if ((access_type == MMU_DATA_STORE || - access_type == MMU_DATA_CAP_STORE) && !(pte & PTE_W)) { + access_type == MMU_DATA_CAP_STORE || + access_type == MMU_VERSION_STORE ) && !(pte & PTE_W)) { /* Write access check failed */ return TRANSLATE_FAIL; } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) { @@ -629,6 +630,9 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } else if (access_type == MMU_DATA_CAP_STORE && !(pte & PTE_CW)) { /* CW inhibited */ return TRANSLATE_CHERI_FAIL; + } else if (access_type == MMU_VERSION_STORE && !(pte & PTE_CWV)) { + /* XXX CWV inhibited: XXX should use special fault ? */ + return TRANSLATE_VERSION_FAIL; #endif #if RISCV_PTE_TRAPPY & PTE_A } else if (!(pte & PTE_A)) { @@ -642,8 +646,9 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, #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 */ + } else if ((access_type == MMU_DATA_CAP_STORE + access_type == MMU_VERSION_STORE ) && !(pte & PTE_D)) { + /* PTE not marked as dirty for cap / version store */ return TRANSLATE_FAIL; #endif #if RISCV_PTE_TRAPPY & PTE_CD @@ -660,6 +665,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, case MMU_DATA_CAP_STORE: updated_pte |= PTE_CD; /* FALLTHROUGH */ + case MMU_VERSION_STORE: #endif case MMU_DATA_STORE: updated_pte |= PTE_D; @@ -720,7 +726,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, so that we TLB miss on later writes to update the dirty bit */ if ((pte & PTE_W) && ((access_type == MMU_DATA_STORE) || - (access_type == MMU_DATA_CAP_STORE) || (pte & PTE_D))) { + (access_type == MMU_DATA_CAP_STORE) || + (access_type == MMU_VERSION_STORE) || (pte & PTE_D))) { *prot |= PAGE_WRITE; } #if defined(TARGET_CHERI) && !defined(TARGET_RISCV32) @@ -746,6 +753,9 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, if ((pte & PTE_CW) == 0) { *prot |= PAGE_SC_TRAP; } + if ((pte & PTE_CWV) == 0) { + *prot |= PAGE_SV_TRAP; + } #endif return TRANSLATE_SUCCESS; } @@ -756,12 +766,15 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, static void raise_mmu_exception(CPURISCVState *env, target_ulong address, MMUAccessType access_type, bool pmp_violation, #if defined(TARGET_CHERI) && !defined(TARGET_RISCV32) - bool cheri_violation, + int failure_reason, #endif bool first_stage, bool two_stage) { CPUState *cs = env_cpu(env); int page_fault_exceptions; +#if defined(TARGET_CHERI) && !defined(TARGET_RISCV32) + bool cheri_violation = failure_reason == TRANSLATE_CHERI_FAIL; +#endif if (first_stage) { page_fault_exceptions = get_field(env->satp, SATP_MODE) != VM_1_10_MBARE && @@ -783,6 +796,7 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, case MMU_DATA_LOAD: #if defined(TARGET_CHERI) case MMU_DATA_CAP_LOAD: + case MMU_VERSION_LOAD: #endif if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT; @@ -798,12 +812,15 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, case MMU_DATA_STORE: #if defined(TARGET_CHERI) case MMU_DATA_CAP_STORE: + case MMU_VERSION_STORE: #endif if (two_stage && !first_stage) { cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; #if defined(TARGET_CHERI) && !defined(TARGET_RISCV32) } else if (cheri_violation) { cs->exception_index = RISCV_EXCP_STORE_AMO_CAP_PAGE_FAULT; + } else if (failure_reason == TRANSLATE_VERSION_FAIL) { + cs->exception_index = RISCV_EXCP_VERSION; #endif } else { cs->exception_index = page_fault_exceptions ? @@ -1071,7 +1088,10 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TRANSLATE_SUCCESS) { MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; #ifdef TARGET_CHERI - attrs.tag_setting = access_type == MMU_DATA_CAP_STORE; + /* tag_setting is used to decide whether to allocate tag / version + storage. Fow now we allocate both if either is required. */ + attrs.tag_setting = (access_type == MMU_DATA_CAP_STORE) || + (access_type == MMU_VERSION_STORE); #endif if (pmp_is_range_in_tlb(env, pa & TARGET_PAGE_MASK, &tlb_size)) { tlb_set_page_with_attrs(cs, address & ~(tlb_size - 1), @@ -1088,7 +1108,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else { raise_mmu_exception(env, address, access_type, pmp_violation, #if defined(TARGET_CHERI) && !defined(TARGET_RISCV32) - ret == TRANSLATE_CHERI_FAIL, + ret, #endif first_stage_error, riscv_cpu_virt_enabled(env) || @@ -1189,6 +1209,9 @@ void riscv_cpu_do_interrupt(CPUState *cs) case RISCV_EXCP_LOAD_ACCESS_FAULT: case RISCV_EXCP_STORE_AMO_ACCESS_FAULT: case RISCV_EXCP_ILLEGAL_INST: +#ifdef TARGET_CHERI + case RISCV_EXCP_VERSION: +#endif write_tval = true; tval = env->badaddr; break; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index a4a5aa349a9..9155e5ad7dc 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -435,6 +435,7 @@ static const target_ulong delegable_excps = (1ULL << (RISCV_EXCP_STORE_AMO_CAP_PAGE_FAULT)) | #endif (1ULL << (RISCV_EXCP_CHERI)) | + (1ULL << (RISCV_EXCP_VERSION)) | #endif 0; static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | diff --git a/target/riscv/insn32-cheri.decode b/target/riscv/insn32-cheri.decode index 79bbc61aa6e..36420ca1016 100644 --- a/target/riscv/insn32-cheri.decode +++ b/target/riscv/insn32-cheri.decode @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2020 Alex Richardson +# Copyright (c) 2021 Microsoft # All rights reserved. # # This software was developed by SRI International and the University of @@ -37,7 +38,9 @@ ### Three operand instructions # 0000000 unused cspecialrw 0000001 ..... ..... 000 ..... 1011011 @r -# 0000010-0000111 unused +csetversion 0000010 ..... ..... 000 ..... 1011011 @r +camocdecversion 0000011 ..... ..... 000 ..... 1011011 @r +# 0000100-0000111 unused csetbounds 0001000 ..... ..... 000 ..... 1011011 @r csetboundsexact 0001001 ..... ..... 000 ..... 1011011 @r # 0001010 unused @@ -85,7 +88,10 @@ cgetaddr 1111111 01111 ..... 000 ..... 1011011 @r2 # fpclear 1111111 10000 ..... 000 ..... 1011011 @r2 csealentry 1111111 10001 ..... 000 ..... 1011011 @r2 cloadtags 1111111 10010 ..... 000 ..... 1011011 @r2 +cgetversion 1111111 10011 ..... 000 ..... 1011011 @r2 jalr_pcc 1111111 10100 ..... 000 ..... 1011011 @r2 +# cloadversions 1111111 10101 ..... 000 ..... 1011011 @r2 +cloadversion 1111111 10110 ..... 000 ..... 1011011 @r2 @@ -93,7 +99,8 @@ jalr_pcc 1111111 10100 ..... 000 ..... 1011011 @r2 @r_2source ....... ..... ..... ... ..... ....... %rs2 %rs1 ### Two operands (source1 and source2) -cinvoke 1111110 ..... ..... 000 00001 1011011 @r_2source +cinvoke 1111110 ..... ..... 000 00001 1011011 @r_2source +cstoreversion 1111110 ..... ..... 000 00010 1011011 @r_2source ### Instructions with 12-bit immediates: diff --git a/target/riscv/insn_trans/trans_cheri.c.inc b/target/riscv/insn_trans/trans_cheri.c.inc index dae1d785f21..a04989e2267 100644 --- a/target/riscv/insn_trans/trans_cheri.c.inc +++ b/target/riscv/insn_trans/trans_cheri.c.inc @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020 Alex Richardson + * Copyright (c) 2021 Microsoft * All rights reserved. * * This software was developed by SRI International and the University of @@ -165,6 +166,8 @@ TRANSLATE_INT_CAP(cgettype) TRANSLATE_INT_CAP(cgetsealed) TRANSLATE_INT_CAP(cloadtags) +TRANSLATE_INT_CAP(cgetversion) +TRANSLATE_INT_CAP(cloadversion) // Two operand (int int) static inline bool trans_crrl(DisasContext *ctx, arg_crrl *a) @@ -214,12 +217,14 @@ TRANSLATE_CAP_CAP_INT(csetbounds) TRANSLATE_CAP_CAP_INT(csetboundsexact) TRANSLATE_CAP_CAP_INT(csetflags) TRANSLATE_CAP_CAP_INT(csetoffset) +TRANSLATE_CAP_CAP_INT(csetversion) // Three operand (int cap cap) TRANSLATE_INT_CAP_CAP(csub) TRANSLATE_INT_CAP_CAP(ctestsubset) TRANSLATE_INT_CAP_CAP(cseqx) TRANSLATE_INT_CAP_CAP(ctoptr) +TRANSLATE_INT_CAP_CAP(camocdecversion) // CIncOffsetImm/CSetBoundsImm: typedef void(cheri_cap_cap_imm_helper)(TCGv_env, TCGv_i32, TCGv_i32, TCGv); @@ -315,6 +320,17 @@ static inline bool trans_cinvoke(DisasContext *ctx, arg_cinvoke *a) return true; } +static inline bool trans_cstoreversion(DisasContext *ctx, arg_cstoreversion *a) +{ + TCGv_i32 base_regnum = tcg_const_i32(a->rs1); + TCGv gpr_value = tcg_temp_new(); + gen_get_gpr(gpr_value, a->rs2); + gen_helper_cstoreversion(cpu_env, base_regnum, gpr_value); + tcg_temp_free_i32(base_regnum); + tcg_temp_free(gpr_value); + return true; +} + // Loads static bool gen_ddc_load(DisasContext *ctx, int rd, int rs1, MemOp memop) { @@ -411,7 +427,7 @@ static bool gen_ddc_store(DisasContext *ctx, int rs1, int rs2, MemOp memop) return true; } -/* Load Via Capability Register */ +/* Store Via Capability Register */ static inline bool gen_cap_store_mem_idx(DisasContext *ctx, int32_t addr_regnum, int32_t val_regnum, target_long offset, int mem_idx, MemOp op) diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index 7b6d452f4d0..bf7659228a5 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -37,6 +37,8 @@ static inline pmp_priv_t access_type_to_pmp_priv(MMUAccessType at) case MMU_DATA_LOAD: return PMP_READ; case MMU_DATA_STORE: return PMP_WRITE; case MMU_INST_FETCH: return PMP_EXEC; + case MMU_VERSION_LOAD: return PMP_READ; + case MMU_VERSION_STORE: return PMP_WRITE; } abort(); }