|
| 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