Skip to content

Commit 3a2210f

Browse files
authored
Merge pull request #7157 from tautschnig/feature/document-mmio
Document modelling of Memory-mapped I/O
2 parents f7f6eed + 845d238 commit 3a2210f

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

doc/cprover-manual/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* [Pointers](modeling/pointers/)
2626
* [Floating Point](modeling/floating-point/)
2727
* [Generating Environments](goto-harness/)
28+
* [Memory-mapped I/O](modeling/mmio/)
2829

2930
9. Build Systems
3031

doc/cprover-manual/modeling-mmio.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
[CPROVER Manual TOC](../../)
2+
3+
## Modeling Memory-mapped I/O
4+
5+
Input and output between CPU and devices is facilitated using
6+
[port-mapped or memory-mapped I/O](https://en.wikipedia.org/wiki/Memory-mapped_I/O).
7+
The former uses dedicated instructions, while the latter uses read or write
8+
access to specific locations in memory.
9+
In absence of any further modeling, CBMC would consider such accesses
10+
out-of-bounds (cf. [program properties](../../properties/)).
11+
Furthermore, reads or writes to these specific memory locations may result in
12+
the device exhibiting some behavior.
13+
Both situations require modeling to ensure that CBMC can perform verification
14+
that is faithful to the behavior exhibited in concrete execution.
15+
16+
### Declaring memory mapping
17+
18+
When low-level code uses memory-mapped I/O to access a device, registers of the
19+
device are mapped to specific locations in memory. Code reads or writes a
20+
register in the device by reading or writing a specific location in memory. For
21+
example, if the second bit in a configuration register is to be set, and if that
22+
configuration register is mapped to the byte at location 0x1000 in memory, then
23+
code sets the second bit of the byte at 0x1000. The problem posed by
24+
memory-mapped I/O is that there is no declaration or allocation in the source
25+
code specifying this location 0x1000 as a valid region of memory. Nevertheless
26+
accesses within this region are valid memory accesses, and should not be flagged
27+
as an out-of-bounds memory reference. This is an example of
28+
implementation-defined behavior that must be modeled to avoid reporting false
29+
positives.
30+
31+
CBMC uses the built-in function `__CPROVER_allocated_memory(address, size)` to
32+
mark ranges of memory as valid. Accesses within this region are exempt from the
33+
out-of-bounds assertion checking that CBMC would normally do. The function
34+
declares the half-open interval [address, address + size) as valid memory that
35+
can be read and written.
36+
37+
This function can be used anywhere in the source code, but is most commonly used
38+
in the verification harness. Note that there is no flow sensitivity or scope
39+
restriction: CBMC considers accesses to memory regions marked as above valid for
40+
read or write access even before the call to the built-in function is
41+
encountered.
42+
43+
### Device behavior
44+
45+
A memory-mapped I/O region is an interface to a device. Values returned by
46+
reading and writing this region of memory need not follow the semantics of
47+
ordinary read-write memory. Imagine a device that can generate unique
48+
identifiers. If the register returning the unique id is mapped to the byte at
49+
location 0x1000, then reading location 0x1000 will return a different value
50+
every time, even without intervening writes. These side effects have to be
51+
modeled. One easy approach is to ‘havoc’ the device, meaning that writes are
52+
ignored and reads return non-deterministic values. This is sound, but may lead
53+
to too many false positives. To model the device semantics more precisely, use
54+
one of the options described below.
55+
56+
*If the device has an API,* havoc the device by removing the implementation
57+
using `goto-instrument`'s `--remove-function-body` command-line parameter.
58+
Assume the API is called `device_access`. Using
59+
`goto-instrument --remove-function-body device_access` will drop the
60+
implementation of the function device_access from compiled object code.
61+
If there is no other definition of `device_access`, CBMC will model each
62+
invocation of `device_access` as returning an unconstrained value of the
63+
appropriate return type. Now, to havoc a device with an API that includes a read
64+
and write method, use this command-line option to remove their function bodies,
65+
and CBMC will model each invocation of read as returning an unconstrained value.
66+
At link time, if another object file, such as the test harness, provides a
67+
second definition of `device_access`, CBMC will use this definition in its
68+
place. Thus, to model device semantics more precisely, provide a device model in
69+
the verification harness by providing implementations of (or approximations for)
70+
the methods in the API.
71+
72+
*If the device has no API,* meaning that the code refers directly to the address
73+
in the memory-mapped I/O region for the device without reference to accessor
74+
functions, use
75+
```C
76+
__CPROVER_mm_io_r(address, size)
77+
__CPROVER_mm_io_w(address, size, value)
78+
```
79+
to model the reading or writing of an address at a fixed integer address. If the
80+
test harness provides implementations of these functions, CBMC will use these
81+
functions to model every read or write of memory. For example, defining
82+
```C
83+
char __CPROVER_mm_io_r(void *a, unsigned s) {
84+
if(a == 0x1000)
85+
return 2;
86+
else
87+
return nondet_char();
88+
}
89+
```
90+
will return the value 2 upon any access at address 0x1000, and return a
91+
non-deterministic value in all other cases.

0 commit comments

Comments
 (0)