e3ecb7ddd6d95ea0a25f506fa848b0928c5a949c
[gem5.git] / src / arch / arm / table_walker.cc
1 /*
2 * Copyright (c) 2010 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Ali Saidi
38 */
39
40 #include "arch/arm/faults.hh"
41 #include "arch/arm/table_walker.hh"
42 #include "arch/arm/tlb.hh"
43 #include "dev/io_device.hh"
44 #include "cpu/thread_context.hh"
45
46
47 using namespace ArmISA;
48
49 TableWalker::TableWalker(const Params *p)
50 : MemObject(p), port(NULL), tlb(NULL), tc(NULL), req(NULL),
51 doL1DescEvent(this), doL2DescEvent(this)
52 {}
53
54 TableWalker::~TableWalker()
55 {
56 ;
57 }
58
59
60 unsigned int
61 drain(Event *de)
62 {
63 panic("Not implemented\n");
64 }
65
66 Port*
67 TableWalker::getPort(const std::string &if_name, int idx)
68 {
69 if (if_name == "port") {
70 if (port != NULL)
71 fatal("%s: port already connected to %s",
72 name(), port->getPeer()->name());
73 System *sys = params()->sys;
74 Tick minb = params()->min_backoff;
75 Tick maxb = params()->max_backoff;
76 port = new DmaPort(this, sys, minb, maxb);
77 return port;
78 }
79 return NULL;
80 }
81
82 Fault
83 TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint8_t _cid, TLB::Mode mode,
84 TLB::Translation *_trans, bool _timing)
85 {
86 // Right now 1 CPU == 1 TLB == 1 TLB walker
87 // In the future we might want to change this as multiple
88 // threads/contexts could share a walker and/or a TLB
89 if (tc || req)
90 panic("Overlapping TLB walks attempted\n");
91
92 tc = _tc;
93 transState = _trans;
94 req = _req;
95 fault = NoFault;
96 contextId = _cid;
97 timing = _timing;
98
99 // XXX These should be cached or grabbed from cached copies in
100 // the TLB, all these miscreg reads are expensive
101 vaddr = req->getVaddr() & ~PcModeMask;
102 sctlr = tc->readMiscReg(MISCREG_SCTLR);
103 cpsr = tc->readMiscReg(MISCREG_CPSR);
104 N = tc->readMiscReg(MISCREG_TTBCR);
105 Addr ttbr = 0;
106
107 isFetch = (mode == TLB::Execute);
108 isWrite = (mode == TLB::Write);
109 isPriv = (cpsr.mode != MODE_USER);
110
111 // If translation isn't enabled, we shouldn't be here
112 assert(sctlr.m);
113
114 DPRINTF(TLB, "Begining table walk for address %#x, TTBCR: %#x, bits:%#x\n",
115 vaddr, N, mbits(vaddr, 31, 32-N));
116
117 if (N == 0 || !mbits(vaddr, 31, 32-N)) {
118 DPRINTF(TLB, " - Selecting TTBR0\n");
119 ttbr = tc->readMiscReg(MISCREG_TTBR0);
120 } else {
121 DPRINTF(TLB, " - Selecting TTBR1\n");
122 ttbr = tc->readMiscReg(MISCREG_TTBR1);
123 N = 0;
124 }
125
126 Addr l1desc_addr = mbits(ttbr, 31, 14-N) | (bits(vaddr,31-N,20) << 2);
127 DPRINTF(TLB, " - Descriptor at address %#x\n", l1desc_addr);
128
129
130 // Trickbox address check
131 fault = tlb->walkTrickBoxCheck(l1desc_addr, vaddr, sizeof(uint32_t),
132 isFetch, isWrite, 0, true);
133 if (fault) {
134 tc = NULL;
135 req = NULL;
136 return fault;
137 }
138
139 if (timing) {
140 port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t),
141 &doL1DescEvent, (uint8_t*)&l1Desc.data, (Tick)0);
142 } else {
143 port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t),
144 NULL, (uint8_t*)&l1Desc.data, (Tick)0);
145 doL1Descriptor();
146 }
147
148 return fault;
149 }
150
151 void
152 TableWalker::memAttrs(TlbEntry &te, uint8_t texcb)
153 {
154
155 if (sctlr.tre == 0) {
156 switch(texcb) {
157 case 0:
158 case 1:
159 case 4:
160 case 8:
161 te.nonCacheable = true;
162 break;
163 case 16:
164 if (bits(texcb, 1,0) == 0 || bits(texcb, 3,2) == 0)
165 te.nonCacheable = true;
166 break;
167 }
168 } else {
169 PRRR prrr = tc->readMiscReg(MISCREG_PRRR);
170 NMRR nmrr = tc->readMiscReg(MISCREG_NMRR);
171 switch(bits(texcb, 2,0)) {
172 case 0:
173 if (nmrr.ir0 == 0 || nmrr.or0 == 0 || prrr.tr0 != 0x2)
174 te.nonCacheable = true;
175 break;
176 case 1:
177 if (nmrr.ir1 == 0 || nmrr.or1 == 0 || prrr.tr1 != 0x2)
178 te.nonCacheable = true;
179 break;
180 case 2:
181 if (nmrr.ir2 == 0 || nmrr.or2 == 0 || prrr.tr2 != 0x2)
182 te.nonCacheable = true;
183 break;
184 case 3:
185 if (nmrr.ir3 == 0 || nmrr.or3 == 0 || prrr.tr3 != 0x2)
186 te.nonCacheable = true;
187 break;
188 case 4:
189 if (nmrr.ir4 == 0 || nmrr.or4 == 0 || prrr.tr4 != 0x2)
190 te.nonCacheable = true;
191 break;
192 case 5:
193 if (nmrr.ir5 == 0 || nmrr.or5 == 0 || prrr.tr5 != 0x2)
194 te.nonCacheable = true;
195 break;
196 case 6:
197 panic("Imp defined type\n");
198 case 7:
199 if (nmrr.ir7 == 0 || nmrr.or7 == 0 || prrr.tr7 != 0x2)
200 te.nonCacheable = true;
201 break;
202 }
203 }
204 }
205
206 void
207 TableWalker::doL1Descriptor()
208 {
209 DPRINTF(TLB, "L1 descriptor for %#x is %#x\n", vaddr, l1Desc.data);
210 TlbEntry te;
211
212 switch (l1Desc.type()) {
213 case L1Descriptor::Ignore:
214 case L1Descriptor::Reserved:
215 tc = NULL;
216 req = NULL;
217 DPRINTF(TLB, "L1 Descriptor Reserved/Ignore, causing fault\n");
218 if (isFetch)
219 fault = new PrefetchAbort(vaddr, ArmFault::Translation0);
220 else
221 fault = new DataAbort(vaddr, NULL, isWrite, ArmFault::Translation0);
222 return;
223 case L1Descriptor::Section:
224 if (sctlr.afe && bits(l1Desc.ap(), 0) == 0)
225 panic("Haven't implemented AFE\n");
226
227 if (l1Desc.supersection()) {
228 panic("Haven't implemented supersections\n");
229 }
230 te.N = 20;
231 te.pfn = l1Desc.pfn();
232 te.size = (1<<te.N) - 1;
233 te.global = !l1Desc.global();
234 te.valid = true;
235 te.vpn = vaddr >> te.N;
236 te.sNp = true;
237 te.xn = l1Desc.xn();
238 te.ap = l1Desc.ap();
239 te.domain = l1Desc.domain();
240 te.asid = contextId;
241 memAttrs(te, l1Desc.texcb());
242
243 DPRINTF(TLB, "Inserting Section Descriptor into TLB\n");
244 DPRINTF(TLB, " - N%d pfn:%#x size: %#x global:%d valid: %d\n",
245 te.N, te.pfn, te.size, te.global, te.valid);
246 DPRINTF(TLB, " - vpn:%#x sNp: %d xn:%d ap:%d domain: %d asid:%d\n",
247 te.vpn, te.sNp, te.xn, te.ap, te.domain, te.asid);
248 DPRINTF(TLB, " - domain from l1 desc: %d data: %#x bits:%d\n",
249 l1Desc.domain(), l1Desc.data, (l1Desc.data >> 5) & 0xF );
250
251 tc = NULL;
252 req = NULL;
253 tlb->insert(vaddr, te);
254
255 return;
256 case L1Descriptor::PageTable:
257 Addr l2desc_addr;
258 l2desc_addr = l1Desc.l2Addr() | (bits(vaddr, 19,12) << 2);
259 DPRINTF(TLB, "L1 descriptor points to page table at: %#x\n", l2desc_addr);
260
261 // Trickbox address check
262 fault = tlb->walkTrickBoxCheck(l2desc_addr, vaddr, sizeof(uint32_t),
263 isFetch, isWrite, l1Desc.domain(), false);
264 if (fault) {
265 tc = NULL;
266 req = NULL;
267 return;
268 }
269
270
271 if (timing) {
272 port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t),
273 &doL2DescEvent, (uint8_t*)&l2Desc.data, 0);
274 } else {
275 port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t),
276 NULL, (uint8_t*)&l2Desc.data, 0);
277 doL2Descriptor();
278 }
279 return;
280 default:
281 panic("A new type in a 2 bit field?\n");
282 }
283 }
284
285 void
286 TableWalker::doL2Descriptor()
287 {
288 DPRINTF(TLB, "L2 descriptor for %#x is %#x\n", vaddr, l2Desc.data);
289 TlbEntry te;
290
291 if (sctlr.afe && bits(l1Desc.ap(), 0) == 0)
292 panic("Haven't implemented AFE\n");
293
294 if (l2Desc.invalid()) {
295 DPRINTF(TLB, "L2 descriptor invalid, causing fault\n");
296 tc = NULL;
297 req = NULL;
298 if (isFetch)
299 fault = new PrefetchAbort(vaddr, ArmFault::Translation1);
300 else
301 fault = new DataAbort(vaddr, l1Desc.domain(), isWrite, ArmFault::Translation1);
302 return;
303 }
304
305 if (l2Desc.large()) {
306 te.N = 16;
307 te.pfn = l2Desc.pfn();
308 } else {
309 te.N = 12;
310 te.pfn = l2Desc.pfn();
311 }
312
313 te.valid = true;
314 te.size = (1 << te.N) - 1;
315 te.asid = contextId;
316 te.sNp = false;
317 te.vpn = vaddr >> te.N;
318 te.global = l2Desc.global();
319 te.xn = l2Desc.xn();
320 te.ap = l2Desc.ap();
321 te.domain = l1Desc.domain();
322 memAttrs(te, l2Desc.texcb());
323
324 tc = NULL;
325 req = NULL;
326 tlb->insert(vaddr, te);
327 }
328
329 ArmISA::TableWalker *
330 ArmTableWalkerParams::create()
331 {
332 return new ArmISA::TableWalker(this);
333 }
334