Generate instruction decoder dynamically
[riscv-isa-sim.git] / riscv / mmu.cc
1 // See LICENSE for license details.
2
3 #include "mmu.h"
4 #include "sim.h"
5 #include "processor.h"
6
7 mmu_t::mmu_t(char* _mem, size_t _memsz)
8 : mem(_mem), memsz(_memsz), badvaddr(0),
9 ptbr(0), proc(NULL)
10 {
11 flush_tlb();
12 }
13
14 mmu_t::~mmu_t()
15 {
16 }
17
18 void mmu_t::flush_icache()
19 {
20 memset(icache_tag, -1, sizeof(icache_tag));
21 }
22
23 void mmu_t::flush_tlb()
24 {
25 memset(tlb_insn_tag, -1, sizeof(tlb_insn_tag));
26 memset(tlb_load_tag, -1, sizeof(tlb_load_tag));
27 memset(tlb_store_tag, -1, sizeof(tlb_store_tag));
28
29 flush_icache();
30 yield_load_reservation();
31 }
32
33 reg_t mmu_t::refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch)
34 {
35 reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
36 reg_t expected_tag = addr & ~(PGSIZE-1);
37
38 reg_t pte = walk(addr);
39
40 reg_t pte_perm = pte & PTE_PERM;
41 if (proc == NULL || (proc->sr & SR_S))
42 pte_perm = (pte_perm/(PTE_SX/PTE_UX)) & PTE_PERM;
43 pte_perm |= pte & PTE_E;
44
45 reg_t perm = (fetch ? PTE_UX : store ? PTE_UW : PTE_UR) | PTE_E;
46 if(unlikely((pte_perm & perm) != perm))
47 {
48 if (fetch)
49 throw trap_instruction_access_fault;
50
51 badvaddr = addr;
52 throw store ? trap_store_access_fault : trap_load_access_fault;
53 }
54
55 reg_t pgoff = addr & (PGSIZE-1);
56 reg_t pgbase = pte >> PTE_PPN_SHIFT << PGSHIFT;
57 reg_t paddr = pgbase + pgoff;
58
59 if (unlikely(tracer.interested_in_range(pgbase, pgbase + PGSIZE, store, fetch)))
60 tracer.trace(paddr, bytes, store, fetch);
61 else
62 {
63 tlb_load_tag[idx] = (pte_perm & PTE_UR) ? expected_tag : -1;
64 tlb_store_tag[idx] = (pte_perm & PTE_UW) ? expected_tag : -1;
65 tlb_insn_tag[idx] = (pte_perm & PTE_UX) ? expected_tag : -1;
66 tlb_data[idx] = pgbase;
67 }
68
69 return paddr;
70 }
71
72 pte_t mmu_t::walk(reg_t addr)
73 {
74 pte_t pte = 0;
75
76 // the address must be a canonical sign-extended VA_BITS-bit number
77 int shift = 8*sizeof(reg_t) - VA_BITS;
78 if (((sreg_t)addr << shift >> shift) != (sreg_t)addr)
79 ;
80 else if (proc == NULL || !(proc->sr & SR_VM))
81 {
82 if(addr < memsz)
83 pte = PTE_E | PTE_PERM | ((addr >> PGSHIFT) << PTE_PPN_SHIFT);
84 }
85 else
86 {
87 reg_t base = ptbr;
88 reg_t ptd;
89
90 int ptshift = (LEVELS-1)*PTIDXBITS;
91 for(reg_t i = 0; i < LEVELS; i++, ptshift -= PTIDXBITS)
92 {
93 reg_t idx = (addr >> (PGSHIFT+ptshift)) & ((1<<PTIDXBITS)-1);
94
95 reg_t pte_addr = base + idx*sizeof(pte_t);
96 if(pte_addr >= memsz)
97 break;
98
99 ptd = *(pte_t*)(mem+pte_addr);
100 if(ptd & PTE_E)
101 {
102 // if this PTE is from a larger PT, fake a leaf
103 // PTE so the TLB will work right
104 reg_t vpn = addr >> PGSHIFT;
105 ptd |= (vpn & ((1<<(ptshift))-1)) << PTE_PPN_SHIFT;
106
107 // fault if physical addr is invalid
108 reg_t ppn = ptd >> PTE_PPN_SHIFT;
109 if((ppn << PGSHIFT) + (addr & (PGSIZE-1)) < memsz)
110 pte = ptd;
111 break;
112 }
113 else if(!(ptd & PTE_T))
114 break;
115
116 base = (ptd >> PTE_PPN_SHIFT) << PGSHIFT;
117 }
118 }
119
120 return pte;
121 }
122
123 void mmu_t::register_memtracer(memtracer_t* t)
124 {
125 flush_tlb();
126 tracer.hook(t);
127 }