Skip to content

Commit 15699af

Browse files
author
Tim Hutt
committed
Implement Zicbom, Zicboz (cbo.flush, cbo.inval, cbo.zero)
Note that Zicbop (prefetch hints) does not need to be implemented because all it does is label some existing base instructions as prefetch hints. Also I have not wired up the enable flags to the emulators because it is rather tedious (and will hopefully be replaced by riscv-config at some point).
1 parent db5f430 commit 15699af

17 files changed

+271
-0
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ SAIL_DEFAULT_INST += riscv_insts_vext_mem.sail
5757
SAIL_DEFAULT_INST += riscv_insts_vext_mask.sail
5858
SAIL_DEFAULT_INST += riscv_insts_vext_vm.sail
5959
SAIL_DEFAULT_INST += riscv_insts_vext_red.sail
60+
SAIL_DEFAULT_INST += riscv_insts_zicbom.sail
61+
SAIL_DEFAULT_INST += riscv_insts_zicboz.sail
6062

6163
SAIL_SEQ_INST = $(SAIL_DEFAULT_INST) riscv_jalr_seq.sail
6264
SAIL_RMEM_INST = $(SAIL_DEFAULT_INST) riscv_jalr_rmem.sail riscv_insts_rmem.sail

c_emulator/riscv_platform.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ mach_bits plat_rom_size(unit u)
112112
return rv_rom_size;
113113
}
114114

115+
mach_bits plat_cache_block_size_exp()
116+
{
117+
return rv_cache_block_size_exp;
118+
}
119+
115120
// Provides entropy for the scalar cryptography extension.
116121
mach_bits plat_get_16_random_bits(unit u)
117122
{

c_emulator/riscv_platform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ bool within_phys_mem(mach_bits, sail_int);
2626
mach_bits plat_rom_base(unit);
2727
mach_bits plat_rom_size(unit);
2828

29+
mach_bits plat_cache_block_size_exp(unit);
30+
2931
// Provides entropy for the scalar cryptography extension.
3032
mach_bits plat_get_16_random_bits(unit);
3133

c_emulator/riscv_platform_impl.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ uint64_t rv_ram_size = UINT64_C(0x4000000);
2727
uint64_t rv_rom_base = UINT64_C(0x1000);
2828
uint64_t rv_rom_size = UINT64_C(0x100);
2929

30+
// Default 64, which is mandated by RVA22.
31+
uint64_t rv_cache_block_size_exp = UINT64_C(6);
32+
3033
// Provides entropy for the scalar cryptography extension.
3134
uint64_t rv_16_random_bits(void)
3235
{

c_emulator/riscv_platform_impl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ extern uint64_t rv_ram_size;
3131
extern uint64_t rv_rom_base;
3232
extern uint64_t rv_rom_size;
3333

34+
extern uint64_t rv_cache_block_size_exp;
35+
3436
// Provides entropy for the scalar cryptography extension.
3537
extern uint64_t rv_16_random_bits(void);
3638

c_emulator/riscv_sim.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ enum {
5656
OPT_PMP_GRAIN,
5757
OPT_ENABLE_SVINVAL,
5858
OPT_ENABLE_ZCB,
59+
OPT_CACHE_BLOCK_SIZE,
5960
};
6061

6162
static bool do_dump_dts = false;
@@ -154,6 +155,7 @@ static struct option options[] = {
154155
#ifdef SAILCOV
155156
{"sailcov-file", required_argument, 0, 'c' },
156157
#endif
158+
{"cache-block-size", required_argument, 0, OPT_CACHE_BLOCK_SIZE },
157159
{0, 0, 0, 0 }
158160
};
159161

@@ -234,6 +236,17 @@ static void read_dtb(const char *path)
234236
fprintf(stdout, "Read %zd bytes of DTB from %s.\n", dtb_len, path);
235237
}
236238

239+
// Return log2(x), or -1 if x is not a power of 2.
240+
static int ilog2(uint64_t x)
241+
{
242+
for (unsigned i = 0; i < sizeof(x) * 8; ++i) {
243+
if ((1ull << i) == x) {
244+
return i;
245+
}
246+
}
247+
return -1;
248+
}
249+
237250
/**
238251
* Parses the command line arguments and returns the argv index for the first
239252
* ELF file that should be loaded. As getopt transforms the argv array, all
@@ -247,6 +260,7 @@ static int process_args(int argc, char **argv)
247260
uint64_t ram_size = 0;
248261
uint64_t pmp_count = 0;
249262
uint64_t pmp_grain = 0;
263+
uint64_t block_size_exp = 0;
250264
while (true) {
251265
c = getopt_long(argc, argv,
252266
"a"
@@ -414,6 +428,17 @@ static int process_args(int argc, char **argv)
414428
case OPT_TRACE_OUTPUT:
415429
trace_log_path = optarg;
416430
fprintf(stderr, "using %s for trace output.\n", trace_log_path);
431+
case OPT_CACHE_BLOCK_SIZE:
432+
block_size_exp = ilog2(atol(optarg));
433+
434+
if (block_size_exp < 0 || block_size_exp > 12) {
435+
fprintf(stderr, "invalid cache-block-size '%s' provided.\n", optarg);
436+
exit(1);
437+
}
438+
439+
fprintf(stderr, "setting cache-block-size to 2^%" PRIu64 " = %u B\n",
440+
block_size_exp, 1 << block_size_exp);
441+
rv_cache_block_size_exp = block_size_exp;
417442
break;
418443
case '?':
419444
print_usage(argv[0], 1);

handwritten_support/riscv_extras.lem

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ val plat_rom_size : unit -> bitvector
193193
let plat_rom_size () = []
194194
declare ocaml target_rep function plat_rom_size = `Platform.rom_size`
195195

196+
val plat_cache_block_size_exp : unit -> bitvector
197+
let plat_cache_block_size_exp () = []
198+
declare ocaml target_rep function plat_cache_block_size_exp = `Platform.cache_block_size_exp`
199+
196200
val plat_clint_base : unit -> bitvector
197201
let plat_clint_base () = []
198202
declare ocaml target_rep function plat_clint_base = `Platform.clint_base`

handwritten_support/riscv_extras_sequential.lem

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ val plat_rom_size : forall 'a. Size 'a => unit -> bitvector 'a
173173
let plat_rom_size () = wordFromInteger 0
174174
declare ocaml target_rep function plat_rom_size = `Platform.rom_size`
175175

176+
val plat_cache_block_size_exp : unit -> integer
177+
let plat_cache_block_size_exp () = wordFromInteger 0
178+
declare ocaml target_rep function plat_cache_block_size_exp = `Platform.cache_block_size_exp`
179+
176180
val plat_clint_base : forall 'a. Size 'a => unit -> bitvector 'a
177181
let plat_clint_base () = wordFromInteger 0
178182
declare ocaml target_rep function plat_clint_base = `Platform.clint_base`

model/riscv_insts_zicbom.sail

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*=======================================================================================*/
2+
/* This Sail RISC-V architecture model, comprising all files and */
3+
/* directories except where otherwise noted is subject the BSD */
4+
/* two-clause license in the LICENSE file. */
5+
/* */
6+
/* SPDX-License-Identifier: BSD-2-Clause */
7+
/*=======================================================================================*/
8+
9+
// Cache Block Operations - Management
10+
11+
function cbo_clean_flush_enabled(p : Privilege) -> bool = feature_enabled_for_priv(p, menvcfg[CBCFE][0], senvcfg[CBCFE][0])
12+
function cbo_inval_enabled(p : Privilege) -> bool = feature_enabled_for_priv(p, menvcfg[CBIE][0], senvcfg[CBIE][0])
13+
function cbo_inval_as_inval(p : Privilege) -> bool = feature_enabled_for_priv(p, menvcfg[CBIE][1], senvcfg[CBIE][1])
14+
15+
/* ****************************************************************** */
16+
union clause ast = RISCV_ZICBOM : (cbop_zicbom, regidx)
17+
18+
mapping encdec_cbop : cbop_zicbom <-> bits(12) = {
19+
CBO_CLEAN <-> 0b000000000001,
20+
CBO_FLUSH <-> 0b000000000010,
21+
CBO_INVAL <-> 0b000000000000,
22+
}
23+
24+
mapping clause encdec = RISCV_ZICBOM(cbop, rs1) if haveZicbom()
25+
<-> encdec_cbop(cbop) @ rs1 @ 0b010 @ 0b00000 @ 0b0001111 if haveZicbom()
26+
27+
mapping cbop_mnemonic : cbop_zicbom <-> string = {
28+
CBO_CLEAN <-> "cbo.clean",
29+
CBO_FLUSH <-> "cbo.flush",
30+
CBO_INVAL <-> "cbo.inval"
31+
}
32+
33+
mapping clause assembly = RISCV_ZICBOM(cbop, rs1)
34+
<-> cbop_mnemonic(cbop) ^ spc() ^ "(" ^ opt_spc() ^ reg_name(rs1) ^ opt_spc() ^ ")"
35+
36+
val process_clean_inval : (regidx, cbop_zicbom) -> Retired
37+
function process_clean_inval(rs1, cbop) = {
38+
let rs1_val = X(rs1);
39+
let cache_block_size_exp = plat_cache_block_size_exp();
40+
let cache_block_size = 2 ^ cache_block_size_exp;
41+
42+
// Offset from rs1 to the beginning of the cache block. This is 0 if rs1
43+
// is aligned to the cache block, or negative if rs1 is misaligned.
44+
let offset = (rs1_val & ~(zero_extend(ones(cache_block_size_exp)))) - rs1_val;
45+
46+
// TODO: This is incorrect since CHERI only requires at least one byte
47+
// to be in bounds here, whereas `ext_data_get_addr()` checks that all bytes
48+
// are in bounds. We will need to add a new function, parameter or access type.
49+
match ext_data_get_addr(rs1, offset, Read(Data), cache_block_size) {
50+
Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL },
51+
Ext_DataAddr_OK(vaddr) => {
52+
let res: option(ExceptionType) = match translateAddr(vaddr, Read(Data)) {
53+
TR_Address(paddr, _) => {
54+
// "A cache-block management instruction is permitted to access the
55+
// specified cache block whenever a load instruction or store instruction
56+
// is permitted to access the corresponding physical addresses. If
57+
// neither a load instruction nor store instruction is permitted to
58+
// access the physical addresses, but an instruction fetch is permitted
59+
// to access the physical addresses, whether a cache-block management
60+
// instruction is permitted to access the cache block is UNSPECIFIED."
61+
//
62+
// In this implementation we currently don't allow access for fetches.
63+
let exc_read = phys_access_check(Read(Data), cur_privilege, paddr, cache_block_size);
64+
let exc_write = phys_access_check(Write(Data), cur_privilege, paddr, cache_block_size);
65+
match (exc_read, exc_write) {
66+
// Access is permitted if read OR write are allowed. If neither
67+
// are allowed then we always report a store exception.
68+
(Some(exc_read), Some(exc_write)) => Some(exc_write),
69+
_ => None(),
70+
}
71+
},
72+
TR_Failure(e, _) => Some(e)
73+
};
74+
// "If access to the cache block is not permitted, a cache-block management
75+
// instruction raises a store page fault or store guest-page fault exception
76+
// if address translation does not permit any access or raises a store access
77+
// fault exception otherwise."
78+
match res {
79+
// The model has no caches so there's no action required.
80+
None() => RETIRE_SUCCESS,
81+
Some(e) => {
82+
let e : ExceptionType = match e {
83+
E_Load_Access_Fault() => E_SAMO_Access_Fault(),
84+
E_SAMO_Access_Fault() => E_SAMO_Access_Fault(),
85+
E_Load_Page_Fault() => E_SAMO_Page_Fault(),
86+
E_SAMO_Page_Fault() => E_SAMO_Page_Fault(),
87+
// No other exceptions should be generated since we're not checking
88+
// for fetch access and it's can't be misaligned.
89+
_ => internal_error(__FILE__, __LINE__, "unexpected exception for cmo.clean/inval"),
90+
};
91+
handle_mem_exception(vaddr, e);
92+
RETIRE_FAIL
93+
}
94+
}
95+
}
96+
}
97+
}
98+
99+
function clause execute(RISCV_ZICBOM(cbop, rs1)) =
100+
match cbop {
101+
CBO_CLEAN if cbo_clean_flush_enabled(cur_privilege) =>
102+
process_clean_inval(rs1, cbop),
103+
CBO_FLUSH if cbo_clean_flush_enabled(cur_privilege) =>
104+
process_clean_inval(rs1, cbop),
105+
CBO_INVAL if cbo_inval_enabled(cur_privilege) =>
106+
process_clean_inval(rs1, if cbo_inval_as_inval(cur_privilege) then CBO_INVAL else CBO_FLUSH),
107+
_ => {
108+
handle_illegal();
109+
RETIRE_FAIL
110+
},
111+
}

model/riscv_insts_zicboz.sail

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*=======================================================================================*/
2+
/* This Sail RISC-V architecture model, comprising all files and */
3+
/* directories except where otherwise noted is subject the BSD */
4+
/* two-clause license in the LICENSE file. */
5+
/* */
6+
/* SPDX-License-Identifier: BSD-2-Clause */
7+
/*=======================================================================================*/
8+
9+
// Cache Block Operations - Zero
10+
11+
function cbo_zero_enabled(p : Privilege) -> bool = feature_enabled_for_priv(p, menvcfg[CBZE][0], senvcfg[CBZE][0])
12+
13+
/* ****************************************************************** */
14+
union clause ast = RISCV_ZICBOZ : (regidx)
15+
16+
mapping clause encdec = RISCV_ZICBOZ(rs1) if haveZicboz()
17+
<-> 0b000000000100 @ rs1 @ 0b010 @ 0b00000 @ 0b0001111 if haveZicboz()
18+
19+
mapping clause assembly = RISCV_ZICBOZ(rs1)
20+
<-> "cbo.zero" ^ spc() ^ "(" ^ opt_spc() ^ reg_name(rs1) ^ opt_spc() ^ ")"
21+
22+
function clause execute(RISCV_ZICBOZ(rs1)) = {
23+
if cbo_zero_enabled(cur_privilege) then {
24+
let rs1_val = X(rs1);
25+
let cache_block_size_exp = plat_cache_block_size_exp();
26+
let cache_block_size = 2 ^ cache_block_size_exp;
27+
28+
// Offset from rs1 to the beginning of the cache block. This is 0 if rs1
29+
// is aligned to the cache block, or negative if rs1 is misaligned.
30+
let offset = (rs1_val & ~(zero_extend(ones(cache_block_size_exp)))) - rs1_val;
31+
32+
// TODO: This is not correct since CHERI treats CBO operations
33+
// differently to normal accesses with respect to bounds checks.
34+
match ext_data_get_addr(rs1, offset, Write(Data), cache_block_size) {
35+
Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL },
36+
Ext_DataAddr_OK(vaddr) => {
37+
// "An implementation may update the bytes in any order and with any granularity
38+
// and atomicity, including individual bytes."
39+
//
40+
// This implementation does a single atomic write.
41+
match translateAddr(vaddr, Write(Data)) {
42+
TR_Failure(e, _) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
43+
TR_Address(paddr, _) => {
44+
let eares : MemoryOpResult(unit) = mem_write_ea(paddr, cache_block_size, false, false, false);
45+
match (eares) {
46+
MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
47+
MemValue(_) => {
48+
let res : MemoryOpResult(bool) = mem_write_value(paddr, cache_block_size, zeros(), false, false, false);
49+
match (res) {
50+
MemValue(true) => RETIRE_SUCCESS,
51+
MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"),
52+
MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL }
53+
}
54+
}
55+
}
56+
}
57+
}
58+
},
59+
}
60+
} else {
61+
handle_illegal();
62+
RETIRE_FAIL
63+
}
64+
}

0 commit comments

Comments
 (0)