1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
|
open import Pervasives_extra
open import Vector
open import Arch
let compose f g x = f (g x)
type end_flag =
| E_big_endian
| E_little_endian
type direction =
| D_increasing
| D_decreasing
type register_value = <|
rv_bits: list bit (* MSB first, smallest index number *);
rv_dir: direction;
rv_start: nat ;
rv_start_internal: nat;
(*when dir is increasing, rv_start = rv_start_internal.
Otherwise, tells interpreter how to reconstruct a proper decreasing value*)
|>
type byte = list bit (* of length 8 *) (*MSB first everywhere*)
type address = integer
type address_lifted = address
type memory_value = list byte
(* the list is of length >=1 *)
(* for both big-endian (Power) and little-endian (ARM), the head of the
list is the byte stored at the lowest address *)
(* for big-endian Power the head of the list is the most-significant
byte, in both the interpreter and machineDef* code. *)
(* For little-endian ARM, the head of the list is the
least-significant byte in machineDef* code and the
most-significant byte in interpreter code, with the switch over
(a list-reverse) being done just inside the interpreter interface*)
(* In other words, in the machineDef* code the lowest-address byte is first,
and in the interpreter code the most-significant byte is first *)
type opcode = Opcode of list byte (* of length 4 *)
type read_kind =
(* common reads *)
| Read_plain
(* Power reads *)
| Read_reserve
(* AArch64 reads *)
| Read_acquire | Read_exclusive | Read_exclusive_acquire | Read_stream
type write_kind =
(* common writes *)
| Write_plain
(* Power writes *)
| Write_conditional
(* AArch64 writes *)
| Write_release | Write_exclusive | Write_exclusive_release
type barrier_kind =
(* Power barriers *)
| Sync | LwSync | Eieio | Isync
(* AArch64 barriers *)
| DMB | DMB_ST | DMB_LD | DSB | DSB_ST | DSB_LD | ISB
type slice = (nat * nat)
type reg_name =
(* Name of the register, accessing the entire register, the start and size of this register,
* and its direction *)
| Reg of string * nat * nat * direction
(* Name of the register, accessing from the bit indexed by the first
* to the bit indexed by the second integer of the slice, inclusive. For
* machineDef* the first is a smaller number or equal to the second. *)
| Reg_slice of string * nat * direction * slice
(* Name of the register, start and direction, and name of the field of the register
* accessed. The slice specifies where this field is in the register*)
| Reg_field of string * nat * direction * string * slice
(* The first four components are as in Reg_field; the final slice
* specifies a part of the field, indexed w.r.t. the register as a whole *)
| Reg_f_slice of string * nat * direction * string * slice * slice
type outcome 'e 'a =
(* Request to read memory, value is location to read followed by registers that location depended
* on when mode.track_values, integer is size to read, followed by registers that were used in
* computing that size *)
(* address_lifted should go away: doesn't make sense to allow for undefined bits in addresses *)
| Read_mem of read_kind * address_lifted * nat * maybe (list reg_name) *
(memory_value -> outcome 'a)
(* Request to write memory, first value and dependent registers is location, second is the value
* to write *)
| Write_mem of write_kind * address_lifted * nat * maybe (list reg_name) * memory_value *
maybe (list reg_name) * (bool -> outcome 'a)
(* Tell the system a write is imminent, at address lifted tanted by register list, of size nat *)
| Write_ea of write_kind * address_lifted * nat * maybe (list reg_name) * outcome 'a
(* Request to write memory at last signaled address. Memory value should be 8* the size given in
* ea signal *)
| Write_memv of memory_value * maybe (list reg_name) * (bool -> outcome 'a)
(* Request a memory barrier *)
(* outcome 'a used to be (unit -> outcome 'a), but since the code is purely functional we don't
* need that *)
| Barrier of barrier_kind * outcome 'a
(* Tell the system to dynamically recalculate dependency footprint *)
| Footprint of outcome 'a
(* Request to read register *)
| Read_reg of reg_name * (register_value -> outcome 'a)
(* Request to write register *)
| Write_reg of reg_name * register_value * outcome 'a
(* List of instruction states to be run in parrallel, any order permitted.
Expects concurrency model to choose an order: a permutation of the original list *)
| Nondet_choice of list (outcome unit) * (list (outcome unit) -> outcome 'a)
(* Escape the current instruction, for traps, some sys calls, interrupts, etc. Can optionally
* provide a handler. *)
| Escape 'e (* currently without handlers *) (* of maybe (unit -> outcome 'a) *)
(* Stop for incremental stepping, function can be used to display function call data *)
| Done of 'a
type M 'e 'a = outcome 'e 'a
val return : forall 'e 'a. 'a -> M 'e 'a
let return a = Done a
val (>>=) : forall 'a 'b. M 'a -> ('a -> M 'b) -> M 'b
val (>>>) : forall 'a 'b 'c. ('a -> M 'b) -> ('b -> M 'c) -> ('a -> M 'c)
val (>>) : forall 'b. M unit -> (unit -> M 'b) -> M 'b
let (>>) m n = m >>= fun _ -> n
let rec (>>=) m f = match m with
| Done a -> f a
| Read_mem rk addr sz rs k -> Read_mem rk addr sz rs (fun v -> (k v) >>= f)
| Write_mem wk addr sz rs v rs2 k -> Write_mem wk addr sz rs v rs2 (fun b -> (k b) >>= f)
| Write_ea wk addr sz rs k -> Write_ea wk addr sz rs (k >>= f)
| Write_memv v rs k -> Write_memv v rs (fun b -> (k b) >>= f)
| Barrier bk k -> Barrier bk (k >>= f)
| Footprint k -> Footprint (k >>= f)
| Read_reg reg k -> Read_reg reg (fun v -> (k v) >>= f)
| Write_reg reg v k -> Write_reg reg v (k >>= f)
| Nondet_choice actions k -> Nondet_choice actions (fun c -> (k c) >>= f)
| Escape -> Escape
end
val exit : forall 'a 'e. 'e -> M 'a
let exit _ = Escape
val byte_chunks : forall 'a. nat -> list 'a -> list (list 'a)
let rec byte_chunks n list = match (n,list) with
| (0,_) -> []
| (n+1, a::b::c::d::e::f::g::h::rest) -> [a;b;c;d;e;f;g;h] :: byte_chunks n rest
| _ -> failwith "byte_chunks not given enough bits"
end
val bitvFromBytes : list byte -> vector bit
let bitvFromBytes v = V (foldl (++) [] v) 0 defaultDir
let dir is_inc = if is_inc then D_increasing else D_decreasing
val bitvFromRegisterValue : register_value -> vector bit
let bitvFromRegisterValue v =
V (v.rv_bits) (integerFromNat (v.rv_start)) (v.rv_dir = D_increasing)
val registerValueFromBitv : vector bit -> register -> register_value
let registerValueFromBitv (V bits start is_inc) reg =
let start = natFromInteger start in
<| rv_bits = bits;
rv_dir = dir is_inc;
rv_start_internal = start;
rv_start = start+1 - (natFromInteger (length_reg reg)) |>
val read_memory : read_kind -> integer -> integer -> M (vector bit)
let read_memory rk addr sz =
let sz = natFromInteger sz in
Read_mem rk addr sz Nothing (compose Done bitvFromBytes)
val write_memory : write_kind -> integer -> integer -> vector bit -> M bool
let write_memory wk addr sz (V v _ _) =
let sz = natFromInteger sz in
Write_mem wk addr sz Nothing (byte_chunks sz v) Nothing Done
val read_reg_range : register -> (integer * integer) -> M (vector bit)
let read_reg_range reg (i,j) =
let (i,j) = (natFromInteger i,natFromInteger j) in
let start = natFromInteger (start_index_reg reg) in
let reg = Reg_slice (register_to_string reg) start (dir defaultDir) (i,j) in
Read_reg reg (compose Done bitvFromRegisterValue)
val write_reg_range : register -> (integer * integer) -> vector bit -> M unit
let write_reg_range reg (i,j) v =
let rv = registerValueFromBitv v reg in
let start = natFromInteger (start_index_reg reg) in
let (i,j) = (natFromInteger i,natFromInteger j) in
let reg = Reg_slice (register_to_string reg) start (dir defaultDir) (i,j) in
Write_reg reg rv (Done ())
val read_reg_bit : register -> integer -> M bit
let read_reg_bit reg i =
let i = natFromInteger i in
let start = natFromInteger (start_index_reg reg) in
let reg = Reg_slice (register_to_string reg) start (dir defaultDir) (i,i) in
Read_reg reg (fun v -> Done (access (bitvFromRegisterValue v) 1))
val write_reg_bit : register -> integer -> bit -> M unit
let write_reg_bit reg i bit = write_reg_range reg (i,i) (V [bit] 0 true)
val read_reg : register -> M (vector bit)
let read_reg reg =
let start = natFromInteger (start_index_reg reg) in
let sz = natFromInteger (length_reg reg) in
let reg = Reg (register_to_string reg) start sz (dir defaultDir) in
Read_reg reg (compose Done bitvFromRegisterValue)
val write_reg : register -> vector bit -> M unit
let write_reg reg v =
let rv = registerValueFromBitv v reg in
let start = natFromInteger (start_index_reg reg) in
let sz = natFromInteger (length_reg reg) in
let reg = Reg (register_to_string reg) start sz (dir defaultDir) in
Write_reg reg rv (Done ())
val read_reg_field : register -> register_field -> M (vector bit)
let read_reg_field reg rfield =
let (i,j) =
let (i,j) = field_indices rfield in
(natFromInteger i,natFromInteger j) in
let start = natFromInteger (start_index_reg reg) in
let reg = Reg_slice (register_to_string reg) start (dir defaultDir) (i,j) in
Read_reg reg (compose Done bitvFromRegisterValue)
val write_reg_field : register -> register_field -> vector bit -> M unit
let write_reg_field reg rfield = write_reg_range reg (field_indices rfield)
val read_reg_bitfield : register -> register_bitfield -> M bit
let read_reg_bitfield reg rbit = read_reg_bit reg (field_index_bit rbit)
val write_reg_bitfield : register -> register_bitfield -> bit -> M unit
let write_reg_bitfield reg rbit = write_reg_bit reg (field_index_bit rbit)
val foreach_inc : forall 'vars. (integer * integer * integer) (*(nat * nat * nat)*) -> 'vars ->
(integer (*nat*) -> 'vars -> (unit * 'vars)) -> (unit * 'vars)
let rec foreach_inc (i,stop,by) vars body =
if i <= stop
then
let (_,vars) = body i vars in
foreach_inc (i + by,stop,by) vars body
else ((),vars)
val foreach_dec : forall 'vars. (integer * integer * integer) (*(nat * nat * nat)*) -> 'vars ->
(integer (*nat*) -> 'vars -> (unit * 'vars)) -> (unit * 'vars)
let rec foreach_dec (i,stop,by) vars body =
if i >= stop
then
let (_,vars) = body i vars in
foreach_dec (i - by,stop,by) vars body
else ((),vars)
val foreachM_inc : forall 'vars. (nat * nat * nat) -> 'vars ->
(nat -> 'vars -> M (unit * 'vars)) -> M (unit * 'vars)
let rec foreachM_inc (i,stop,by) vars body =
if i <= stop
then
body i vars >>= fun (_,vars) ->
foreachM_inc (i + by,stop,by) vars body
else return ((),vars)
val foreachM_dec : forall 'vars. (nat * nat * nat) -> 'vars ->
(nat -> 'vars -> M (unit * 'vars)) -> M (unit * 'vars)
let rec foreachM_dec (i,stop,by) vars body =
if i >= stop
then
body i vars >>= fun (_,vars) ->
foreachM_dec (i - by,stop,by) vars body
else return ((),vars)
let length l = integerFromNat (length l)
let write_two_regs r1 r2 vec =
let size = length_reg r1 in
let start = get_start vec in
let vsize = length vec in
let r1_v = slice vec start ((if defaultDir then size - start else start - size) - 1) in
let r2_v =
(slice vec)
(if defaultDir then size - start else start - size)
(if defaultDir then vsize - start else start - vsize) in
write_reg r1 r1_v >> write_reg r2 r2_v
|