[sim] initial support for virtual memory
[riscv-isa-sim.git] / riscv / mmu.h
1 #include "decode.h"
2 #include "trap.h"
3 #include "icsim.h"
4 #include <assert.h>
5
6 class processor_t;
7
8 const reg_t LEVELS = 4;
9 const reg_t PGSHIFT = 12;
10 const reg_t PGSIZE = 1 << PGSHIFT;
11 const reg_t PPN_BITS = 8*sizeof(reg_t) - PGSHIFT;
12
13 struct pte_t
14 {
15 reg_t v : 1;
16 reg_t e : 1;
17 reg_t r : 1;
18 reg_t d : 1;
19 reg_t ux : 1;
20 reg_t ur : 1;
21 reg_t uw : 1;
22 reg_t sx : 1;
23 reg_t sr : 1;
24 reg_t sw : 1;
25 reg_t unused1 : 2;
26 reg_t ppn : PPN_BITS;
27 };
28
29 class mmu_t
30 {
31 public:
32 mmu_t(char* _mem, size_t _memsz)
33 : mem(_mem), memsz(_memsz), badvaddr(0),
34 ptbr(0), supervisor(true), vm_enabled(false),
35 icsim(NULL), dcsim(NULL), itlbsim(NULL), dtlbsim(NULL)
36 {
37 }
38
39 void set_icsim(icsim_t* _icsim) { icsim = _icsim; }
40 void set_dcsim(icsim_t* _dcsim) { dcsim = _dcsim; }
41 void set_itlbsim(icsim_t* _itlbsim) { itlbsim = _itlbsim; }
42 void set_dtlbsim(icsim_t* _dtlbsim) { dtlbsim = _dtlbsim; }
43
44 #ifdef RISCV_ENABLE_ICSIM
45 # define dcsim_tick(dcsim, dtlbsim, addr, size, st) \
46 do { if(dcsim) (dcsim)->tick(addr, size, st); \
47 if(dtlbsim) (dtlbsim)->tick(addr, sizeof(reg_t), false); } while(0)
48 #else
49 # define dcsim_tick(dcsim, dtlbsim, addr, size, st)
50 #endif
51
52 #define load_func(type) \
53 type##_t load_##type(reg_t addr) { \
54 check_align(addr, sizeof(type##_t), false, false); \
55 addr = translate(addr, false, false); \
56 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), false); \
57 return *(type##_t*)(mem+addr); \
58 }
59
60 #define store_func(type) \
61 void store_##type(reg_t addr, type##_t val) { \
62 check_align(addr, sizeof(type##_t), true, false); \
63 addr = translate(addr, true, false); \
64 dcsim_tick(dcsim, dtlbsim, addr, sizeof(type##_t), true); \
65 *(type##_t*)(mem+addr) = val; \
66 }
67
68 insn_t load_insn(reg_t addr, bool rvc)
69 {
70 insn_t insn;
71
72 #ifdef RISCV_ENABLE_RVC
73 check_align(addr, rvc ? 2 : 4, false, true);
74
75 reg_t paddr_lo = translate(addr, false, true);
76 insn.bits = *(uint16_t*)(mem+paddr_lo);
77
78 if(!INSN_IS_RVC(insn.bits))
79 {
80 reg_t paddr_hi = translate(addr+2, false, true);
81 insn.bits |= (uint32_t)*(uint16_t*)(mem+paddr_hi) << 16;
82 }
83 #else
84 check_align(addr, 4, false, true);
85 reg_t paddr = translate(addr, false, true);
86 insn = *(insn_t*)(mem+paddr);
87 #endif
88
89 #ifdef RISCV_ENABLE_ICSIM
90 if(icsim)
91 icsim->tick(addr, insn_length(insn), false);
92 if(itlbsim)
93 itlbsim->tick(addr, sizeof(reg_t), false);
94 #endif
95
96 return insn;
97 }
98
99 load_func(uint8)
100 load_func(uint16)
101 load_func(uint32)
102 load_func(uint64)
103
104 load_func(int8)
105 load_func(int16)
106 load_func(int32)
107 load_func(int64)
108
109 store_func(uint8)
110 store_func(uint16)
111 store_func(uint32)
112 store_func(uint64)
113
114 reg_t get_badvaddr() { return badvaddr; }
115 void set_supervisor(bool sup) { supervisor = sup; }
116 void set_vm_enabled(bool en) { vm_enabled = en; }
117 void set_ptbr(reg_t addr) { ptbr = addr & ~(PGSIZE-1); }
118
119 private:
120 char* mem;
121 size_t memsz;
122 reg_t badvaddr;
123
124 reg_t ptbr;
125 bool supervisor;
126 bool vm_enabled;
127
128 static const reg_t TLB_ENTRIES = 32;
129 pte_t tlb_data[TLB_ENTRIES];
130 reg_t tlb_tag[TLB_ENTRIES];
131
132 icsim_t* icsim;
133 icsim_t* dcsim;
134 icsim_t* itlbsim;
135 icsim_t* dtlbsim;
136
137 void check_align(reg_t addr, int size, bool store, bool fetch)
138 {
139 if(addr & (size-1))
140 {
141 badvaddr = addr;
142 if(fetch)
143 throw trap_instruction_address_misaligned;
144 if(store)
145 throw trap_store_address_misaligned;
146 throw trap_load_address_misaligned;
147 }
148 }
149
150 reg_t translate(reg_t addr, bool store, bool fetch)
151 {
152 reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
153 pte_t pte = tlb_data[idx];
154 reg_t tag = tlb_tag[idx];
155
156 trap_t trap = store ? trap_store_access_fault
157 : fetch ? trap_instruction_access_fault
158 : trap_load_access_fault;
159
160 if(!pte.v || tag != (addr >> PGSHIFT))
161 {
162 pte = walk(addr);
163 if(!pte.v)
164 throw trap;
165
166 tlb_data[idx] = pte;
167 tlb_tag[idx] = addr >> PGSHIFT;
168 }
169
170 if(store && !(supervisor ? pte.sw : pte.uw) ||
171 !store && !fetch && !(supervisor ? pte.sr : pte.ur) ||
172 !store && !fetch && !(supervisor ? pte.sr : pte.ur))
173 throw trap;
174
175 return (addr % PGSIZE) | (pte.ppn << PGSHIFT);
176 }
177
178 pte_t walk(reg_t addr)
179 {
180 pte_t pte;
181
182 if(!vm_enabled)
183 {
184 pte.v = addr < memsz;
185 pte.e = 1;
186 pte.r = pte.d = 0;
187 pte.ur = pte.uw = pte.ux = pte.sr = pte.sw = pte.sx = 1;
188 pte.ppn = addr >> PGSHIFT;
189 }
190 else
191 {
192 pte.v = 0;
193
194 int lg_ptesz = sizeof(pte_t) == 4 ? 2
195 : sizeof(pte_t) == 8 ? 3
196 : 0;
197 assert(lg_ptesz);
198
199 reg_t base = ptbr;
200
201 for(int i = LEVELS-1; i >= 0; i++)
202 {
203 reg_t idx = addr >> (PGSHIFT + i*(PGSHIFT - lg_ptesz));
204 idx &= (1<<(PGSHIFT - lg_ptesz)) - 1;
205
206 reg_t pte_addr = base + idx*sizeof(pte_t);
207 if(pte_addr >= memsz)
208 break;
209
210 pte = *(pte_t*)(mem+pte_addr);
211 if(!pte.v || pte.e)
212 break;
213
214 base = pte.ppn << PGSHIFT;
215 }
216 pte.v &= pte.e;
217 }
218
219 return pte;
220 }
221
222 void check_bounds(reg_t addr, int size, bool store, bool fetch)
223 {
224 if(addr >= memsz || addr + size > memsz)
225 {
226 badvaddr = addr;
227 if(fetch)
228 throw trap_instruction_access_fault;
229 throw store ? trap_store_access_fault : trap_load_access_fault;
230 }
231 }
232
233 friend class processor_t;
234 };