~akkartik/mu-normie

mu-normie/linux/bootstrap/bootstrap.h -rw-r--r-- 9.0 KiB
fe92b2db — Kartik Agaram . a month ago
                                                                                
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
#include <assert.h>

#include <string>
using std::string;

#include <iostream>
using std::istream;
using std::ostream;
using std::iostream;
using std::cin;
using std::cout;
using std::cerr;
#include <fstream>
using std::ifstream;
using std::ofstream;
#include <sstream>
using std::istringstream;
using std::ostringstream;

#include <list>
using std::list;
#include <set>
using std::set;
#include <vector>
using std::vector;
#include <map>
using std::map;

#include <math.h>

// Standard names for 8- and 32-bit numbers.
#include <stdint.h>
#include <iomanip>
#define HEXBYTE  std::hex << std::setw(2) << std::setfill('0')
#define HEXWORD  std::hex << std::setw(8) << std::setfill('0')
// ugly that iostream doesn't print uint8_t as an integer
#define NUM(X) static_cast<int>(X)

//// Parsed SubX programs
// programs consist of segments, which consist of lines, which consist of words

struct word {
  string original;
  string data;
  vector<string> metadata;
};

struct line {
  vector<word> words;
  vector<string> metadata;
  string original;
};

struct segment {
  string name;
  uint32_t start;
  vector<line> lines;
  segment() {
    start = 0;
  }
};

struct program {
  uint32_t entry;
  vector<segment> segments;
  program() { entry = 0; }
};

//// Translating SubX programs

typedef void (*transform_fn)(program&);

enum expected_argument_type {
  // start from the least significant bit
  MODRM,  // more complex, may also involve disp8 or disp32
  SUBOP,
  DISP8,
  DISP16,
  DISP32,
  IMM8,
  IMM32,
  NUM_OPERAND_TYPES
};

//// x86 processor for the emulator

// 32-bit integer registers
enum {
  EAX,
  ECX,
  EDX,
  EBX,
  ESP,
  EBP,
  ESI,
  EDI,
  NUM_INT_REGISTERS,
};

union reg {
  int32_t i;
  uint32_t u;
};

extern reg Reg[NUM_INT_REGISTERS];
extern uint32_t EIP;  // program counter

// 32-bit floating-point registers
const int NUM_XMM_REGISTERS = 8;
extern float Xmm[NUM_XMM_REGISTERS];
extern const string Xname[NUM_XMM_REGISTERS];

// the subset of x86 flag registers we care about
extern bool SF;  // sign flag
extern bool ZF;  // zero flag
extern bool CF;  // carry flag
extern bool OF;  // overflow flag

//// ELF memory map for the emulator

// Very primitive/fixed/insecure ELF segments for now.
//   --- inaccessible:        0x00000000 -> 0x08047fff
//   code:                    0x09000000 -> 0x09ffffff (specified in ELF binary)
//   data:                    0x0a000000 -> 0x0affffff (specified in ELF binary)
//                      --- heap gets mmap'd somewhere here ---
//   stack:                   0xbdffffff -> 0xbd000000 (downward; not in ELF binary)
//   argv hack:               0xbf000000 -> 0xbfffffff (not in ELF binary)
//   --- reserved for kernel: 0xc0000000 -> ...
const uint32_t START_HEAP        = 0x0b000000;
const uint32_t END_HEAP          = 0xbd000000;
const uint32_t STACK_SEGMENT     = 0xbd000000;
const uint32_t AFTER_STACK       = 0xbe000000;
const uint32_t ARGV_DATA_SEGMENT = 0xbf000000;
// When updating the above memory map, don't forget to update `mmap`'s
// implementation in the 'syscalls' layer.

//// Some bits of the test harness.
// People should be able to create tests anywhere.

typedef void (*test_fn)(void);
extern bool Run_tests;
extern bool Passed;  // set this to false inside any test to indicate failure

#define CHECK(X) \
  if (Passed && !(X)) { \
    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << '\n'; \
    Passed = false; \
    return;  /* Currently we stop at the very first failure. */ \
  }

#define CHECK_EQ(X, Y) \
  if (Passed && (X) != (Y)) { \
    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << " == " << #Y << '\n'; \
    cerr << "  got " << (X) << '\n';  /* BEWARE: multiple eval */ \
    Passed = false; \
    return;  /* Currently we stop at the very first failure. */ \
  }

int run_tests();
void reset();

//// Some bits of the tracing framework.

const int Error_depth = 0;
const int Warn_depth = 1;
const int Max_depth = 9999;

struct trace_line {
  string contents;
  string label;
  int depth;  // 0 is 'sea level'; positive integers are progressively 'deeper' and lower level

  trace_line(string c, string l);
  trace_line(string c, string l, int d);
};

struct trace_stream {
  vector<trace_line> past_lines;
  // accumulator for current trace_line
  ostringstream* curr_stream;
  string curr_label;
  int curr_depth;
  // other stuff
  int collect_depth;  // avoid tracing lower levels for speed
  ofstream null_stream;  // never opened, so writes to it silently fail

  trace_stream();
  ostream& stream(string label);
  ostream& stream(int depth, string label);
  void newline();
  bool should_incrementally_print_trace();
  string readable_contents(string label);
};

extern trace_stream* Trace_stream;
extern ofstream Trace_file;
extern bool Dump_trace;

extern int Hide_errors;  // if set, don't print errors or warnings to screen
extern int Hide_warnings;  // if set, don't print warnings to screen
extern int Trace_errors;  // used only when Trace_stream is NULL

// No brackets around the expansion so that it prints nothing if Trace_stream
// isn't initialized.
#define trace(...)  !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__)

#define START_TRACING_UNTIL_END_OF_SCOPE  lease_tracer leased_tracer;
#define raise  (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error"))
#define warn (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Warn_depth, "warn"))

// If we aren't yet sure how to deal with some corner case, use assert_for_now
// to indicate that it isn't an inviolable invariant.
#define assert_for_now assert
#define raise_for_now raise

// Some helpers for white-box testing.

bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected);
int trace_count(string label);
int trace_count(string label, string line);
bool trace_contains_errors();

#define CHECK_TRACE_CONTENTS(...)  check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)

#define CHECK_TRACE_DOESNT_CONTAIN(...)  CHECK(trace_doesnt_contain(__VA_ARGS__))

#define CHECK_TRACE_COUNT(label, count) \
  if (Passed && trace_count(label) != (count)) { \
    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \
    cerr << "  got " << trace_count(label) << '\n';  /* multiple eval */ \
    DUMP(label); \
    Passed = false; \
    return;  /* Currently we stop at the very first failure. */ \
  }

#define CHECK_TRACE_CONTAINS_ERRORS()  CHECK(trace_contains_errors())
#define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
  if (Passed && trace_contains_errors()) { \
    cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
    DUMP("error"); \
    Passed = false; \
    return; \
  }

// Allow tests to ignore trace lines generated during setup.
#define CLEAR_TRACE  delete Trace_stream, Trace_stream = new trace_stream

//// Some helpers for debugging.

// To debug why a test is failing, dump its trace using '?'.
#define DUMP(label)  if (Trace_stream) cerr << Trace_stream->readable_contents(label);

// To add temporary prints to the trace, use 'dbg'.
// `git log` should never show any calls to 'dbg'.
#define dbg trace(0, "a")

void dump_trace_line(ostream& s, trace_line& t);

struct end {};
struct die {};
struct lease_tracer {
  lease_tracer();
  ~lease_tracer();
};

//// Miscellaneous

// Avoid using unsigned ints.
#define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size()))

string trim(const string& s);
vector<string> split(string s, string delim);
vector<string> split_first(string s, string delim);

// Be deliberate about introducing null values into maps.
// Any container that relies on get_or_insert should never call contains_key.
// from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map
template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) {
  typename T::iterator iter(map.find(key));
  if (iter == map.end()) {
    cerr << "get couldn't find key '" << key << "'\n";
    assert(iter != map.end());
  }
  return iter->second;
}

template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) {
  typename T::const_iterator iter(map.find(key));
  if (iter == map.end()) {
    cerr << "get couldn't find key '" << key << "'\n";
    assert(iter != map.end());
  }
  return iter->second;
}

template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) {
  map[key] = value;
  return map[key];
}

template<typename T> bool contains_key(T& map, typename T::key_type const& key) {
  return map.find(key) != map.end();
}

template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) {
  return map[key];
}

template<typename T> typename T::mapped_type const& put_new(T& map, typename T::key_type const& key, typename T::mapped_type const& value) {
  assert(map.find(key) == map.end());
  map[key] = value;
  return map[key];
}