summaryrefslogtreecommitdiff
path: root/mips/run_embed.ml
blob: 42e36a6e91d4cec1bbe86dfc29589082c789fd35 (plain)
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
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
(**************************************************************************)
(*     Sail                                                               *)
(*                                                                        *)
(*  Copyright (c) 2013-2017                                               *)
(*    Kathyrn Gray                                                        *)
(*    Shaked Flur                                                         *)
(*    Stephen Kell                                                        *)
(*    Gabriel Kerneis                                                     *)
(*    Robert Norton-Wright                                                *)
(*    Christopher Pulte                                                   *)
(*    Peter Sewell                                                        *)
(*                                                                        *)
(*  All rights reserved.                                                  *)
(*                                                                        *)
(*  This software was developed by the University of Cambridge Computer   *)
(*  Laboratory as part of the Rigorous Engineering of Mainstream Systems  *)
(*  (REMS) project, funded by EPSRC grant EP/K008528/1.                   *)
(*                                                                        *)
(*  Redistribution and use in source and binary forms, with or without    *)
(*  modification, are permitted provided that the following conditions    *)
(*  are met:                                                              *)
(*  1. Redistributions of source code must retain the above copyright     *)
(*     notice, this list of conditions and the following disclaimer.      *)
(*  2. Redistributions in binary form must reproduce the above copyright  *)
(*     notice, this list of conditions and the following disclaimer in    *)
(*     the documentation and/or other materials provided with the         *)
(*     distribution.                                                      *)
(*                                                                        *)
(*  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''    *)
(*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED     *)
(*  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A       *)
(*  PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR   *)
(*  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,          *)
(*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      *)
(*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF      *)
(*  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND   *)
(*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,    *)
(*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT    *)
(*  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF    *)
(*  SUCH DAMAGE.                                                          *)
(**************************************************************************)

open Printf ;;
open Big_int_Z ;;
open Sail_values;;
open Mips_extras_ml;;
(* The linksem ELF interface *)
open Sail_interface ;;

module Mips_model=Mips_embed;;

let interact_print = ref true
let result_print = ref true
let error_print = ref true
let interactf : ('a, out_channel, unit) format -> 'a =
  function f -> if !interact_print then printf f else ifprintf stderr f
let errorf : ('a, out_channel, unit) format -> 'a =
  function f -> if !error_print then printf f else ifprintf stderr f
let resultf : ('a, out_channel, unit) format -> 'a =
  function f -> if !result_print then printf f else ifprintf stderr f

let rec foldli f acc ?(i=0) = function
  | [] -> acc
  | x::xs -> foldli f (f i acc x) ~i:(i+1) xs;;

let hex_to_big_int s = big_int_of_int64 (Int64.of_string s) ;;
let big_int_to_hex i = 
  (* annoyingly Uint64.to_string_hex prefixes the string with 0x UNLESS it is zero... *) 
  if i = zero_big_int then
    "0"
  else
    let s = Uint64.to_string_hex (Uint64.of_string (string_of_big_int i)) in
    let len = String.length s in
    String.sub s 2 (len - 2)

let big_int_to_hex64 i = 
  let hex = big_int_to_hex i in
  let len = String.length hex in
  if (len < 16) then
    (String.make (16-len) '0') ^ hex
  else
    hex
    
let input_buf = (ref [] : int list ref);;

let add_mem byte addr mem = begin
  assert(byte >= 0 && byte < 256);
  (*printf "MEM [%s] <- %x\n" (big_int_to_hex addr) byte;*)
  mem := Mem.add addr byte !mem
end
(*  
let rec load_memory_segment (segment: Elf_interpreted_segment.elf64_interpreted_segment) mem =
  let (Byte_sequence.Sequence bytes) = segment.Elf_interpreted_segment.elf64_segment_body in
  let addr = segment.Elf_interpreted_segment.elf64_segment_paddr in
  load_memory_segment' bytes addr mem

let rec load_memory_segments segments =
  begin match segments with
    | [] -> ()
    | segment::segments' ->
      let (x,w,r) = segment.Elf_interpreted_segment.elf64_segment_flags in
      begin
        load_memory_segment segment mips_mem;
        load_memory_segments segments'
      end    
  end

let initial_system_state_of_elf_file name = 

  (* call ELF analyser on file *)
  match Sail_interface.populate_and_obtain_global_symbol_init_info name with
  | Error.Fail s -> failwith ("populate_and_obtain_global_symbol_init_info: " ^ s)
  | Error.Success 
      (_, (elf_epi: Sail_interface.executable_process_image),
       (symbol_map: Elf_file.global_symbol_init_info))
    ->
    let (segments, e_entry, e_machine) =
      begin match elf_epi with
        | ELF_Class_32 _ -> failwith "cannot handle ELF_Class_32"
        | ELF_Class_64 (segments,e_entry,e_machine) ->
          (* remove all the auto generated segments (they contain only 0s) *)
          let segments =
            Lem_list.mapMaybe
              (fun (seg, prov) -> if prov = Elf_file.FromELF then Some seg else None)
              segments
          in
          (segments,e_entry,e_machine)
      end
    in
    (* construct program memory and start address *)
    begin
      load_memory_segments segments;
      (*
      debugf "prog_mem\n";
      Mem.iter (fun k v -> debugf "%s\n" (Mem.to_string k v)) !prog_mem;
      debugf "data_mem\n";
      Mem.iter (fun k v -> debugf "%s\n" (Mem.to_string k v)) !data_mem;
      *)
      let (isa_defs, isa_memory_access, isa_externs, isa_model, model_reg_d, startaddr,
           initial_stack_data, initial_register_abi_data, register_data_all) =
        match Nat_big_num.to_int e_machine with
        | 8 (* EM_MIPS *) ->
          let startaddr =
            let e_entry = Uint64.of_string (Nat_big_num.to_string e_entry) in
            match Abi_mips64.abi_mips64_compute_program_entry_point segments e_entry with
            | Error.Fail s -> failwith "Failed computing entry point"
            | Error.Success s -> s
          in
          let (initial_stack_data, initial_register_abi_data) =
            initial_stack_and_reg_data_of_MIPS_elf_file e_entry !data_mem in
          
          (Mips.defs,
           (Mips_extras.read_memory_functions,
            Mips_extras.memory_writes,
            Mips_extras.memory_eas,
            Mips_extras.memory_vals,
            Mips_extras.barrier_functions),
           [],
           MIPS,
           D_decreasing,
           startaddr,
           initial_stack_data,
           initial_register_abi_data,
           mips_register_data_all)

        | _ -> failwith (Printf.sprintf "Sail sequential interpreter can't handle the e_machine value %s, only EM_MIPS is supported." (Nat_big_num.to_string e_machine))
      in
      
      (* pull the object symbols from the symbol table *)
      let symbol_table : (string * Nat_big_num.num * int * word8 list (*their bytes*)) list =
        let rec convert_symbol_table symbol_map =
          begin match symbol_map with
          | [] -> []
          | ((name: string),
             ((typ: Nat_big_num.num),
              (size: Nat_big_num.num (*number of bytes*)),
              (address: Nat_big_num.num),
              (mb: Byte_sequence.byte_sequence option (*present iff type=stt_object*)),
              (binding: Nat_big_num.num)))
	    (*              (mb: Byte_sequence_wrapper.t option (*present iff type=stt_object*)) )) *)
            ::symbol_map' ->
            if Nat_big_num.equal typ Elf_symbol_table.stt_object && not (Nat_big_num.equal size (Nat_big_num.of_int 0))
            then
              (,
                (* an object symbol - map *)
                (*Printf.printf "*** size %d ***\n" (Nat_big_num.to_int size);*)
                let bytes =
                  (match mb with
                   | None -> raise (Failure "this cannot happen")
                   | Some (Sequence bytes) ->
                     List.map (fun (c:char) -> Char.code c) bytes) in
                 (name, address, List.length bytes, bytes):: convert_symbol_table symbol_map'
              )
              else
                (* not an object symbol or of zero size - ignore *)
                convert_symbol_table symbol_map'
          end
        in
        (List.map (fun (n,a,bs) -> (n,a,List.length bs,bs)) initial_stack_data) @ convert_symbol_table symbol_map
      in

      (* invert the symbol table to use for pp *)
      let symbol_table_pp : ((Sail_impl_base.address * int) * string) list =
        (* map symbol to (bindings, footprint),
           if a symbol appears more then onece keep the one with higher
           precedence (stb_global > stb_weak > stb_local) *)
        let map =
          List.fold_left
            (fun map (name, (typ, size, address, mb, binding)) ->
               if String.length name <> 0 &&
                  (if String.length name = 1 then Char.code (String.get name 0) <> 0 else true) &&
                  not (Nat_big_num.equal address (Nat_big_num.of_int 0))
               then
                 try
                   let (binding', _) = StringMap.find name map in
                   if  Nat_big_num.equal binding' Elf_symbol_table.stb_local ||
                       Nat_big_num.equal binding Elf_symbol_table.stb_global
                   then
                     StringMap.add name (binding,
                                         (Sail_impl_base.address_of_integer address, Nat_big_num.to_int size)) map
                   else map
                 with Not_found ->
                   StringMap.add name (binding,
                                       (Sail_impl_base.address_of_integer address, Nat_big_num.to_int size)) map
                     
               else map
            )
            StringMap.empty
            symbol_map
        in

        List.map (fun (name, (binding, fp)) -> (fp, name)) (StringMap.bindings map)
      in


      (* Now we examine the rest of the data memory,
         removing the footprint of the symbols and chunking it into aligned chunks *)
      
      let rec remove_symbols_from_data_memory data_mem symbols =
        match symbols with
        | [] -> data_mem
        | (name,address,size,bs)::symbols' ->
          let data_mem' =
            Mem.filter
              (fun a v ->
                 not (Nat_big_num.greater_equal a address &&
                      Nat_big_num.less a (Nat_big_num.add (Nat_big_num.of_int (List.length bs)) address)))
              data_mem in
          remove_symbols_from_data_memory data_mem' symbols' in

      let trimmed_data_memory : (Nat_big_num.num * memory_byte) list =
        Mem.bindings (remove_symbols_from_data_memory !data_mem symbol_table) in

      (* make sure that's ordered increasingly.... *)
      let trimmed_data_memory =
        List.sort (fun (a,b) (a',b') -> Nat_big_num.compare a a') trimmed_data_memory in

      let aligned a n =  (* a mod n = 0 *)
        let n_big = Nat_big_num.of_int n in
        Nat_big_num.equal (Nat_big_num.modulus a n_big) ((Nat_big_num.of_int 0)) in

      let isplus a' a n =   (* a' = a+n *)
        Nat_big_num.equal a' (Nat_big_num.add (Nat_big_num.of_int n) a) in


      let initial_register_state =
        fun rbn ->
          try
            List.assoc rbn initial_register_abi_data
          with
            Not_found ->
              (register_state_zero register_data_all) rbn
      in

      begin
        (initial_reg_file register_data_all initial_register_state);
        
        (* construct initial system state *)
        let initial_system_state =
          (isa_defs,
	   isa_memory_access,
	   isa_externs,
           isa_model,
           model_reg_d,
           startaddr,
           (Sail_impl_base.address_of_integer startaddr))
        in
        
        (initial_system_state, symbol_table_pp)
      end
    end
 *)

let max_cut_off = ref false
let max_instr = ref 0
let raw_file = ref ""
let raw_at   = ref ""
let elf_file = ref ""

let args = [
  ("--file", Arg.Set_string elf_file, "filename of elf binary to load in memory");
  ("--max_instruction", Arg.Int (fun i -> max_cut_off := true; max_instr := i), "only run i instructions, then stop");
  ("--raw", Arg.Set_string raw_file, "filename of raw file to load in memory");
  ("--at", Arg.Set_string raw_at, "address to load raw file in memory");
]

let time_it action arg =
  let start_time = Sys.time () in
  let ret = action arg in
  let finish_time = Sys.time () in
  (finish_time -. start_time, ret)

let rec debug_print_gprs start stop =
  resultf "DEBUG MIPS REG %.2d 0x%s\n" start (big_int_to_hex64 (unsigned_big(vector_access Mips_model._GPR (big_int_of_int start))));
  if start < stop
  then debug_print_gprs (start + 1) stop
  else ()

let get_opcode pc_a = Mips_model._reverse_endianness(_MEMr (to_vec_dec_big (big_int_of_int 64, pc_a), big_int_of_int 4))

let get_pc () = begin
    try
      Some (unsigned_big(Mips_model._TranslatePC(Mips_model._PC)))
    with Sail_exit -> None
  end


let rec fde_loop count =
  if !max_cut_off && count = !max_instr
  then begin
      resultf "\nEnding evaluation due to reaching cut off point of %d instructions\n" count;
      count
    end
  else begin
    let pc_vaddr = unsigned_big(Mips_model._PC) in
    interactf "\n**** instruction %d from address %s ****\n"
      count (big_int_to_hex64 pc_vaddr);
    let m_paddr = get_pc () in
    match m_paddr with
      | Some pc ->
          let inBranchDelay = Some(unsigned_int(Mips_model._inBranchDelay)) in
          (match inBranchDelay with
          | Some 0 -> 
            let npc_addr = add_int_big_int 4 pc_vaddr in
            let npc_vec = to_vec_dec_big (big_int_of_int 64, npc_addr) in
            set_register Mips_model._nextPC npc_vec;
          | Some 1 ->
            set_register Mips_model._nextPC Mips_model._delayedPC;
          | _ -> failwith "invalid value of inBranchDelay");
          let opcode = get_opcode pc in
          let _ = resultf "decode: 0x%x\n" (unsigned_int(opcode)) in
          let instruction = Mips_model._decode opcode in
          let i = match instruction with
            | Some (i) -> i
            | _ ->  begin
                errorf "\n**** Decode error ****\n"; 
                exit 1;
              end
          in
          if (i == Mips_model.HCF)
          then 
            begin
              resultf "DEBUG MIPS PC 0x%s\n" (big_int_to_hex pc_vaddr);
              debug_print_gprs 0 31;
              resultf "\nSUCCESS program terminated after %d instructions\n" count;
              count
            end 
          else
            begin
              (try
                Mips_model._execute(i)
              with Sail_exit -> ());
              set_register Mips_model._inBranchDelay Mips_model._branchPending;
              set_register Mips_model._branchPending (to_vec_dec_int (1, 0));
              set_register Mips_model._PC Mips_model._nextPC;
              fde_loop (count + 1)
            end
      | None -> begin (* Exception during PC translation *)
          set_register Mips_model._inBranchDelay Mips_model._branchPending;
          set_register Mips_model._branchPending (to_vec_dec_int (1, 0));
          set_register Mips_model._PC Mips_model._nextPC;
          fde_loop (count + 1)
        end
  end


 (*
 (try
   let (pending, _, _)  = (Unix.select [(Unix.stdin)] [] [] 0.0) in
   (if (pending != []) then
     let char = (input_byte stdin) in (
     errorf "Input %x\n" char;
     input_buf := (!input_buf) @ [char]));
 with
 | _ -> ());

 let uart_rvalid = option_int_of_reg "UART_RVALID" in
 (match uart_rvalid with
 | Some 0 ->
   (match !input_buf with
   | x :: xs -> (
       reg := Reg.add "UART_RDATA" (register_value_of_integer 8 7 Sail_impl_base.D_decreasing (Nat_big_num.of_int x)) !reg;
       reg := Reg.add "UART_RVALID" (register_value_of_integer 1 0 Sail_impl_base.D_decreasing (Nat_big_num.of_int 1)) !reg;
       input_buf := xs;
     )
   | [] -> ())
 | _-> ());

 let uart_written = option_int_of_reg "UART_WRITTEN" in
 (match uart_written with 
 | Some 1 ->
   (let uart_data = option_int_of_reg "UART_WDATA" in
   match uart_data with
   | Some b -> (printf "%c" (Char.chr b); printf "%!")
   | None   -> (errorf "UART_WDATA was undef" ; exit 1))
 | _      -> ());
 reg := Reg.add "UART_WRITTEN" (register_value_of_integer 1 0 Sail_impl_base.D_decreasing Nat_big_num.zero) !reg;*)

let rec load_raw_file' mem addr chan =
  let byte = input_byte chan in
  (add_mem byte addr mem;
  load_raw_file' mem (Big_int_Z.add_int_big_int 1 addr) chan)

let rec load_raw_file mem addr chan =
  try 
    load_raw_file' mem addr chan
   with
  | End_of_file -> ()


let run () =
  Arg.parse args (fun _ -> raise (Arg.Bad "anonymous parameter")) "" ;
  (*if !elf_file = "" then begin
    Arg.usage args "";
    exit 1;
  end;*)
(*
  let ((isa_defs,
       (isa_m0, isa_m1, isa_m2, isa_m3,isa_m4),
        isa_externs,
        isa_model,
        model_reg_d,
        startaddr,
        startaddr_internal), pp_symbol_map) = initial_system_state_of_elf_file !file in
 *)
  if String.length(!raw_file) != 0 then
    load_raw_file mips_mem (big_int_of_string !raw_at) (open_in_bin !raw_file);
  set_register_field_bit Mips_model._CP0Status "BEV" Vone;
  printf "CP0Status: %s\n" (string_of_value Mips_model._CP0Status);
  let start_addr = (to_vec_dec_big (big_int_of_int 64, hex_to_big_int "0x9000000040000000")) in
  set_register Mips_model._PC start_addr;
  let name = Filename.basename !raw_file in
  let (t, count) = time_it fde_loop 0 in
  resultf "Execution time for file %s: %f seconds %f IPS \n" name t (float(count) /. t);;

(* Turn off line-buffering of standard input to allow responsive console input 
if Unix.isatty (Unix.stdin) then begin
  let tattrs = Unix.tcgetattr (Unix.stdin) in
  Unix.tcsetattr (Unix.stdin) (Unix.TCSANOW) ({tattrs with c_icanon=false})
end ;;
*)

run () ;;