Skip to content

Commit c0729fb

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.
1 parent 2078d87 commit c0729fb

18 files changed

+308
-3
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ SAIL_DEFAULT_INST += riscv_insts_vext_vm.sail
6060
SAIL_DEFAULT_INST += riscv_insts_vext_fp_vm.sail
6161
SAIL_DEFAULT_INST += riscv_insts_vext_red.sail
6262
SAIL_DEFAULT_INST += riscv_insts_vext_fp_red.sail
63+
SAIL_DEFAULT_INST += riscv_insts_zicbom.sail
64+
SAIL_DEFAULT_INST += riscv_insts_zicboz.sail
6365

6466
SAIL_SEQ_INST = $(SAIL_DEFAULT_INST) riscv_jalr_seq.sail
6567
SAIL_RMEM_INST = $(SAIL_DEFAULT_INST) riscv_jalr_rmem.sail riscv_insts_rmem.sail

c_emulator/riscv_platform.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ bool sys_enable_bext(unit u)
6262
return rv_enable_bext;
6363
}
6464

65+
bool sys_enable_zicbom(unit u)
66+
{
67+
return rv_enable_zicbom;
68+
}
69+
70+
bool sys_enable_zicboz(unit u)
71+
{
72+
return rv_enable_zicboz;
73+
}
74+
6575
uint64_t sys_pmp_count(unit u)
6676
{
6777
return rv_pmp_count;
@@ -112,6 +122,11 @@ mach_bits plat_rom_size(unit u)
112122
return rv_rom_size;
113123
}
114124

125+
mach_bits plat_cache_block_size_exp()
126+
{
127+
return rv_cache_block_size_exp;
128+
}
129+
115130
// Provides entropy for the scalar cryptography extension.
116131
mach_bits plat_get_16_random_bits(unit u)
117132
{

c_emulator/riscv_platform.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ bool sys_enable_writable_misa(unit);
1111
bool sys_enable_writable_fiom(unit);
1212
bool sys_enable_vext(unit);
1313
bool sys_enable_bext(unit);
14+
bool sys_enable_zicbom(unit);
15+
bool sys_enable_zicboz(unit);
1416

1517
uint64_t sys_pmp_count(unit);
1618
uint64_t sys_pmp_grain(unit);
@@ -26,6 +28,8 @@ bool within_phys_mem(mach_bits, sail_int);
2628
mach_bits plat_rom_base(unit);
2729
mach_bits plat_rom_size(unit);
2830

31+
mach_bits plat_cache_block_size_exp(unit);
32+
2933
// Provides entropy for the scalar cryptography extension.
3034
mach_bits plat_get_16_random_bits(unit);
3135

c_emulator/riscv_platform_impl.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ bool rv_enable_writable_misa = true;
1515
bool rv_enable_fdext = true;
1616
bool rv_enable_vext = true;
1717
bool rv_enable_bext = false;
18+
bool rv_enable_zicbom = false;
19+
bool rv_enable_zicboz = false;
1820

1921
bool rv_enable_dirty_update = false;
2022
bool rv_enable_misaligned = false;
@@ -27,6 +29,9 @@ uint64_t rv_ram_size = UINT64_C(0x4000000);
2729
uint64_t rv_rom_base = UINT64_C(0x1000);
2830
uint64_t rv_rom_size = UINT64_C(0x100);
2931

32+
// Default 64, which is mandated by RVA22.
33+
uint64_t rv_cache_block_size_exp = UINT64_C(6);
34+
3035
// Provides entropy for the scalar cryptography extension.
3136
uint64_t rv_16_random_bits(void)
3237
{

c_emulator/riscv_platform_impl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ extern bool rv_enable_next;
1919
extern bool rv_enable_fdext;
2020
extern bool rv_enable_vext;
2121
extern bool rv_enable_bext;
22+
extern bool rv_enable_zicbom;
23+
extern bool rv_enable_zicboz;
2224
extern bool rv_enable_writable_misa;
2325
extern bool rv_enable_dirty_update;
2426
extern bool rv_enable_misaligned;
@@ -31,6 +33,8 @@ extern uint64_t rv_ram_size;
3133
extern uint64_t rv_rom_base;
3234
extern uint64_t rv_rom_size;
3335

36+
extern uint64_t rv_cache_block_size_exp;
37+
3438
// Provides entropy for the scalar cryptography extension.
3539
extern uint64_t rv_16_random_bits(void);
3640

c_emulator/riscv_sim.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ enum {
5656
OPT_PMP_GRAIN,
5757
OPT_ENABLE_SVINVAL,
5858
OPT_ENABLE_ZCB,
59+
OPT_ENABLE_ZICBOM,
60+
OPT_ENABLE_ZICBOZ,
61+
OPT_CACHE_BLOCK_SIZE,
5962
};
6063

6164
static bool do_dump_dts = false;
@@ -151,6 +154,9 @@ static struct option options[] = {
151154
{"enable-writable-fiom", no_argument, 0, OPT_ENABLE_WRITABLE_FIOM},
152155
{"enable-svinval", no_argument, 0, OPT_ENABLE_SVINVAL },
153156
{"enable-zcb", no_argument, 0, OPT_ENABLE_ZCB },
157+
{"enable-zicbom", no_argument, 0, OPT_ENABLE_ZICBOM },
158+
{"enable-zicboz", no_argument, 0, OPT_ENABLE_ZICBOZ },
159+
{"cache-block-size", required_argument, 0, OPT_CACHE_BLOCK_SIZE },
154160
#ifdef SAILCOV
155161
{"sailcov-file", required_argument, 0, 'c' },
156162
#endif
@@ -234,6 +240,17 @@ static void read_dtb(const char *path)
234240
fprintf(stdout, "Read %zd bytes of DTB from %s.\n", dtb_len, path);
235241
}
236242

243+
// Return log2(x), or -1 if x is not a power of 2.
244+
static int ilog2(uint64_t x)
245+
{
246+
for (unsigned i = 0; i < sizeof(x) * 8; ++i) {
247+
if ((1ull << i) == x) {
248+
return i;
249+
}
250+
}
251+
return -1;
252+
}
253+
237254
/**
238255
* Parses the command line arguments and returns the argv index for the first
239256
* ELF file that should be loaded. As getopt transforms the argv array, all
@@ -247,6 +264,7 @@ static int process_args(int argc, char **argv)
247264
uint64_t ram_size = 0;
248265
uint64_t pmp_count = 0;
249266
uint64_t pmp_grain = 0;
267+
uint64_t block_size_exp = 0;
250268
while (true) {
251269
c = getopt_long(argc, argv,
252270
"a"
@@ -401,6 +419,26 @@ static int process_args(int argc, char **argv)
401419
fprintf(stderr, "enabling Zcb extension.\n");
402420
rv_enable_zcb = true;
403421
break;
422+
case OPT_ENABLE_ZICBOM:
423+
fprintf(stderr, "enabling Zicbom extension.\n");
424+
rv_enable_zicbom = true;
425+
break;
426+
case OPT_ENABLE_ZICBOZ:
427+
fprintf(stderr, "enabling Zicboz extension.\n");
428+
rv_enable_zicboz = true;
429+
break;
430+
case OPT_CACHE_BLOCK_SIZE:
431+
block_size_exp = ilog2(atol(optarg));
432+
433+
if (block_size_exp < 0 || block_size_exp > 12) {
434+
fprintf(stderr, "invalid cache-block-size '%s' provided.\n", optarg);
435+
exit(1);
436+
}
437+
438+
fprintf(stderr, "setting cache-block-size to 2^%" PRIu64 " = %u B\n",
439+
block_size_exp, 1 << block_size_exp);
440+
rv_cache_block_size_exp = block_size_exp;
441+
break;
404442
case 'x':
405443
fprintf(stderr, "enabling Zfinx support.\n");
406444
rv_enable_zfinx = true;
@@ -414,7 +452,6 @@ static int process_args(int argc, char **argv)
414452
case OPT_TRACE_OUTPUT:
415453
trace_log_path = optarg;
416454
fprintf(stderr, "using %s for trace output.\n", trace_log_path);
417-
break;
418455
case '?':
419456
print_usage(argv[0], 1);
420457
break;

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)