Skip to content

Commit b7c298a

Browse files
author
Tim Hutt
authored
Add basic static PMA support (#866)
Add basic support for static PMAs specified in the config file. Support for the AMO level is not fully added yet because it needs to be added to `AccessType`. This also adds an implication operator `==>` which is useful for writing SMT properties.
1 parent 58de542 commit b7c298a

File tree

14 files changed

+426
-124
lines changed

14 files changed

+426
-124
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ For booting operating system images, see the information under the
131131
- Svinval extension for fine-grained address-translation cache invalidation, v1.0
132132
- Svrsw60t59b extension for PTE reserved-for-software bits 60-59, v1.0
133133
- Physical Memory Protection (PMP)
134+
- Static memory regions with some static PMAs (Physical Memory Attributes)
134135

135136
<!-- Uncomment the following section when unratified extensions are added
136137
The following unratified extensions are supported and can be enabled using the `--enable-experimental-extensions` flag:

c_emulator/riscv_sim.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -280,16 +280,7 @@ uint64_t load_sail(const std::string &filename, bool main_file)
280280

281281
void write_dtb_to_rom(const std::vector<uint8_t> &dtb)
282282
{
283-
uint64_t rom_base = get_config_uint64({"platform", "rom", "base"});
284-
uint64_t rom_size = get_config_uint64({"platform", "rom", "size"});
285-
286-
uint64_t addr = rom_base;
287-
288-
if (dtb.size() > rom_size) {
289-
throw std::runtime_error("DTB (" + std::to_string(dtb.size())
290-
+ " bytes) does not fit in platform.rom.size ("
291-
+ std::to_string(rom_size) + " bytes).");
292-
}
283+
uint64_t addr = get_config_uint64({"memory", "dtb_address"});
293284

294285
for (uint8_t d : dtb) {
295286
write_mem(addr++, d);

config/config.json.in

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
"na4_supported": true,
2828
"napot_supported": true
2929
},
30+
// This controls global support for misaligned access so it is
31+
// checked before address translation. "misaligned_fault" in
32+
// "regions" is checked after address translation.
3033
"misaligned": {
3134
"supported": true,
3235
"byte_by_byte": false,
@@ -35,23 +38,95 @@
3538
},
3639
"translation": {
3740
"dirty_update": false
38-
}
41+
},
42+
// Address to write DTB to (if provided).
43+
"dtb_address": {
44+
"len": 64,
45+
"value": "0x1000"
46+
},
47+
"regions": [
48+
// ROM
49+
{
50+
"base": {
51+
"len": 64,
52+
"value": "0x1000"
53+
},
54+
"size": {
55+
"len": 64,
56+
"value": "0x1000"
57+
},
58+
"attributes": {
59+
"cacheable": true,
60+
"coherent": true,
61+
"executable": false,
62+
"readable": true,
63+
"writable": false,
64+
"read_idempotent": true,
65+
"write_idempotent": true,
66+
"misaligned_fault": "NoFault",
67+
"reservability": "RsrvNone",
68+
"supports_cbo_zero": false
69+
},
70+
"include_in_device_tree": false
71+
},
72+
// MMIO
73+
{
74+
"base": {
75+
"len": 64,
76+
"value": "0x2000000"
77+
},
78+
"size": {
79+
"len": 64,
80+
"value": "0x2000000"
81+
},
82+
"attributes": {
83+
"cacheable": false,
84+
"coherent": true,
85+
"executable": false,
86+
"readable": true,
87+
"writable": true,
88+
"read_idempotent": false,
89+
"write_idempotent": false,
90+
"misaligned_fault": "AlignmentFault",
91+
"reservability": "RsrvNone",
92+
"supports_cbo_zero": false
93+
},
94+
"include_in_device_tree": false
95+
},
96+
// RAM
97+
{
98+
"base": {
99+
"len": 64,
100+
"value": "0x80000000"
101+
},
102+
"size": {
103+
"len": 64,
104+
"value": "0x80000000"
105+
},
106+
"attributes": {
107+
"cacheable": true,
108+
"coherent": true,
109+
"executable": true,
110+
"readable": true,
111+
"writable": true,
112+
"read_idempotent": true,
113+
"write_idempotent": true,
114+
"misaligned_fault": "NoFault",
115+
"reservability": "RsrvEventual",
116+
"supports_cbo_zero": true
117+
},
118+
"include_in_device_tree": true
119+
}
120+
]
39121
},
40122
"platform": {
41123
"vendorid": 0,
42124
"archid": 0,
43125
"impid": 0,
44126
"hartid": 0,
45127
"cache_block_size_exp": 6,
46-
"ram": {
47-
"base": 2147483648,
48-
"size": 2147483648
49-
},
50-
"rom": {
51-
"base": 4096,
52-
"size": 4096
53-
},
54128
"clint": {
129+
// This must be in a suitable memory region (see memory.regions).
55130
"base": 33554432,
56131
"size": 786432
57132
},

doc/ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636

3737
- Linux boot is now tested in CI.
3838

39+
- Support for specifying arbitrary memory regions and some basic
40+
static PMAs (Physical Memory Attributes) has been added.
41+
3942
# Release notes for the 0.8 release
4043

4144
This is a major release with some backwards-incompatible changes.

model/core/prelude.sail

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ function operator >_u (x, y) = unsigned(x) > unsigned(y)
160160
function operator <=_u (x, y) = unsigned(x) <= unsigned(y)
161161
function operator >=_u (x, y) = unsigned(x) >= unsigned(y)
162162

163+
// Implication. This has the lowest precedence.
164+
infix 1 ==>
165+
function operator ==> (x : bool, y : bool) -> bool = not(x) | y
166+
163167
infix 7 >>
164168
infix 7 <<
165169

model/core/range_util.sail

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
// Return true if the right-open range `a` is a subset of `b` (or equal to it).
10+
// Ranges wrap around the end. The 64 bit limit is just so Sail generates more
11+
// efficient C code.
12+
function range_subset forall 'n, 0 <= 'n <= 64 . (
13+
a_begin : bits('n),
14+
a_size : bits('n),
15+
b_begin : bits('n),
16+
b_size : bits('n),
17+
) -> bool = {
18+
// Rotate so that be starts at 0.
19+
let a_end = (a_begin + a_size) - b_begin;
20+
let b_end = (b_begin + b_size) - b_begin;
21+
let a_begin = a_begin - b_begin;
22+
a_begin <=_u b_end & a_end <=_u b_end & a_begin <=_u a_end
23+
}
24+
25+
$[test]
26+
function test_range_subset() -> unit = {
27+
assert(range_subset(0x0, 0x0, 0x0, 0x0));
28+
assert(range_subset(0x1, 0x0, 0x1, 0x0));
29+
assert(range_subset(0x0, 0x0, 0x0, 0x1));
30+
assert(range_subset(0x1, 0x0, 0x0, 0x1));
31+
assert(range_subset(0x8, 0xc, 0x8, 0xc));
32+
assert(not(range_subset(0x8, 0xc, 0x9, 0xc)));
33+
assert(not(range_subset(0x8, 0xc, 0x8, 0xb)));
34+
assert(not(range_subset(0x3e, 0xe0, 0xc1, 0x9f)));
35+
assert(not(range_subset(0xc1, 0x9f, 0x3e, 0xe0)));
36+
}
37+
38+
// Check if two ranges are subsets of each other they must be equal.
39+
$[property]
40+
function range_subset_equals(a_begin : bits(8), a_size : bits(8), b_begin : bits(8), b_size : bits(8)) -> bool =
41+
range_subset(a_begin, a_size, b_begin, b_size) & range_subset(b_begin, b_size, a_begin, a_size)
42+
==> (a_begin == b_begin & a_size == b_size)
43+
44+
// Check that if A is a subset of B, then all indices in A must be in B.
45+
// Also check that an index exists that is in A but not B then A cannot be a subset of B.
46+
$[property]
47+
function range_subset_precise(a_begin : bits(8), a_size : bits(8), b_begin : bits(8), b_size : bits(8), index : bits(8)) -> bool = {
48+
let index_in_a = (index - a_begin) <_u a_size;
49+
let index_in_b = (index - b_begin) <_u b_size;
50+
let is_subset = range_subset(a_begin, a_size, b_begin, b_size);
51+
52+
(is_subset & index_in_a ==> index_in_b) & ((index_in_a & not(index_in_b)) ==> not(is_subset))
53+
}

model/extensions/Zicbom/zicbom_insts.sail

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ function process_clean_inval(rs1, cbop) = {
9898
// the proper Cache access type when it is available. See
9999
// https://github.com/riscv/sail-riscv/pull/792
100100
let ep = effectivePrivilege(Read(Data), mstatus, cur_privilege);
101-
let exc_read = phys_access_check(Read(Data), ep, paddr, cache_block_size);
102-
let exc_write = phys_access_check(Write(Data), ep, paddr, cache_block_size);
101+
let exc_read = phys_access_check(Read(Data), ep, paddr, cache_block_size, false);
102+
let exc_write = phys_access_check(Write(Data), ep, paddr, cache_block_size, false);
103103
match (exc_read, exc_write) {
104104
// Access is permitted if read OR write are allowed. If neither
105105
// are allowed then we always report a store exception.

model/postlude/device_tree.sail

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,29 @@ function generate_isa_string(fmt : ISA_Format) -> string = {
9999

100100
// Generates the full Device-Tree configuration for the model.
101101

102+
function generate_dts_memories(pmas : list(PMA_Region)) -> string =
103+
match pmas {
104+
[||] => "",
105+
pma :: rest => {
106+
if pma.include_in_device_tree then {
107+
let base_hi = unsigned(pma.base >> 32);
108+
let base_lo = unsigned(pma.base[31 .. 0]);
109+
let size_hi = unsigned(pma.size >> 32);
110+
let size_lo = unsigned(pma.size[31 .. 0]);
111+
112+
" memory@" ^ string_drop(hex_str(unsigned(pma.base)), 2) ^ " {\n"
113+
^ " device_type = \"memory\";\n"
114+
^ " reg = <" ^ hex_str(base_hi) ^ " " ^ hex_str(base_lo)
115+
^ " " ^ hex_str(size_hi) ^ " " ^ hex_str(size_lo) ^ ">;\n"
116+
^ " };\n"
117+
^ generate_dts_memories(rest)
118+
} else generate_dts_memories(rest)
119+
}
120+
}
121+
102122
function generate_dts() -> string = {
103123
let clock_freq : nat = config platform.clock_frequency;
104-
let ram_base_hi = unsigned(plat_ram_base >> 32);
105-
let ram_base_lo = unsigned(plat_ram_base[31 .. 0]);
106-
let ram_size_hi = unsigned(plat_ram_size >> 32);
107-
let ram_size_lo = unsigned(plat_ram_size[31 .. 0]);
124+
108125
let clint_base_hi = unsigned(plat_clint_base >> 32);
109126
let clint_base_lo = unsigned(plat_clint_base[31 .. 0]);
110127
let clint_size_hi = unsigned(plat_clint_size >> 32);
@@ -148,11 +165,8 @@ function generate_dts() -> string = {
148165
^ " };\n"
149166
^ " };\n"
150167
^ " };\n"
151-
^ " memory@" ^ string_drop(hex_str(unsigned(plat_ram_base)), 2) ^ " {\n"
152-
^ " device_type = \"memory\";\n"
153-
^ " reg = <" ^ hex_str(ram_base_hi) ^ " " ^ hex_str(ram_base_lo)
154-
^ " " ^ hex_str(ram_size_hi) ^ " " ^ hex_str(ram_size_lo) ^ ">;\n"
155-
^ " };\n"
168+
^ generate_dts_memories(pma_regions)
169+
156170
^ " soc {\n"
157171
^ " #address-cells = <2>;\n"
158172
^ " #size-cells = <2>;\n"

model/postlude/model.sail

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ function init_model(config_filename : string) -> unit = {
3636
// See https://github.com/torvalds/linux/blob/master/Documentation/arch/riscv/boot.rst
3737
function init_boot_requirements() -> unit = {
3838
X(Regno(10)) = mhartid;
39-
X(Regno(11)) = to_bits_checked(config platform.rom.base : int);
39+
X(Regno(11)) = trunc(config memory.dtb_address : bits(64));
4040
}

model/postlude/validate_config.sail

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -177,39 +177,27 @@ function check_vext_config() -> bool = {
177177
valid;
178178
}
179179

180-
function has_overlap(a_lo : nat, a_hi : nat, b_lo : nat, b_hi : nat) -> bool = {
181-
not((a_lo < b_lo & a_hi < b_lo) | (b_lo < a_lo & b_hi < a_lo))
182-
}
183-
184-
// This will change when we have a more general memory layout.
185-
function check_mem_layout() -> bool = {
186-
var valid : bool = true;
187-
// Memory regions should not overlap. There are currently only three: ram, rom and clint.
188-
let ram_lo = unsigned(plat_ram_base);
189-
let ram_hi = unsigned(plat_ram_base) + unsigned(plat_ram_size);
190-
let rom_lo = unsigned(plat_rom_base);
191-
let rom_hi = unsigned(plat_rom_base) + unsigned(plat_rom_size);
192-
let clint_lo = unsigned(plat_clint_base);
193-
let clint_hi = unsigned(plat_clint_base) + unsigned(plat_clint_size);
194-
if has_overlap(rom_lo, rom_hi, ram_lo, ram_hi)
195-
then {
196-
valid = false;
197-
print_endline("The RAM and ROM regions overlap.");
198-
};
199-
if has_overlap(clint_lo, clint_hi, rom_lo, rom_hi)
200-
then {
201-
valid = false;
202-
print_endline("The Clint and ROM regions overlap.");
203-
};
204-
if has_overlap(clint_lo, clint_hi, ram_lo, ram_hi)
205-
then {
206-
valid = false;
207-
print_endline("The Clint and RAM regions overlap.");
208-
};
180+
// Return true if a list of PMA regions are sorted and don't overlap.
181+
function check_pma_regions(pmas : list(PMA_Region), prev_base : bits(64), prev_size : bits(64)) -> bool =
182+
match pmas {
183+
[||] => true,
184+
pma :: rest => {
185+
if pma.base <_u prev_base + prev_size then {
186+
print_endline("Memory region starting at " ^ bits_str(pma.base) ^
187+
" is not above the end of the previous region starting at " ^
188+
bits_str(prev_base) ^ " and ending at " ^ bits_str(prev_base + prev_size) ^ ".");
189+
return false;
190+
};
191+
check_pma_regions(rest, pma.base, pma.size)
192+
},
193+
}
209194

210-
// Should memory regions also be 4K aligned?
211-
valid
212-
}
195+
// Check that all memory regions are sorted and don't overlap.
196+
function check_mem_layout() -> bool =
197+
if pma_regions == [||] then {
198+
print_endline("No memory regions specified.");
199+
false
200+
} else check_pma_regions(pma_regions, zeros(), zeros())
213201

214202
function check_pmp() -> bool = {
215203
var valid : bool = true;

0 commit comments

Comments
 (0)