table_walker.cc revision 7437:5853fbdfba9b
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 47using namespace ArmISA; 48 49TableWalker::TableWalker(const Params *p) 50 : MemObject(p), port(NULL), tlb(NULL), tc(NULL), req(NULL), 51 doL1DescEvent(this), doL2DescEvent(this) 52{} 53 54TableWalker::~TableWalker() 55{ 56 ; 57} 58 59 60unsigned int 61drain(Event *de) 62{ 63 panic("Not implemented\n"); 64} 65 66Port* 67TableWalker::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 82Fault 83TableWalker::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 mode = _mode; 99 100 /** @todo These should be cached or grabbed from cached copies in 101 the TLB, all these miscreg reads are expensive */ 102 vaddr = req->getVaddr() & ~PcModeMask; 103 sctlr = tc->readMiscReg(MISCREG_SCTLR); 104 cpsr = tc->readMiscReg(MISCREG_CPSR); 105 N = tc->readMiscReg(MISCREG_TTBCR); 106 Addr ttbr = 0; 107 108 isFetch = (mode == TLB::Execute); 109 isWrite = (mode == TLB::Write); 110 isPriv = (cpsr.mode != MODE_USER); 111 112 // If translation isn't enabled, we shouldn't be here 113 assert(sctlr.m); 114 115 DPRINTF(TLB, "Begining table walk for address %#x, TTBCR: %#x, bits:%#x\n", 116 vaddr, N, mbits(vaddr, 31, 32-N)); 117 118 if (N == 0 || !mbits(vaddr, 31, 32-N)) { 119 DPRINTF(TLB, " - Selecting TTBR0\n"); 120 ttbr = tc->readMiscReg(MISCREG_TTBR0); 121 } else { 122 DPRINTF(TLB, " - Selecting TTBR1\n"); 123 ttbr = tc->readMiscReg(MISCREG_TTBR1); 124 N = 0; 125 } 126 127 Addr l1desc_addr = mbits(ttbr, 31, 14-N) | (bits(vaddr,31-N,20) << 2); 128 DPRINTF(TLB, " - Descriptor at address %#x\n", l1desc_addr); 129 130 131 // Trickbox address check 132 fault = tlb->walkTrickBoxCheck(l1desc_addr, vaddr, sizeof(uint32_t), 133 isFetch, isWrite, 0, true); 134 if (fault) { 135 tc = NULL; 136 req = NULL; 137 return fault; 138 } 139 140 if (timing) { 141 port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t), 142 &doL1DescEvent, (uint8_t*)&l1Desc.data, (Tick)0); 143 } else { 144 port->dmaAction(MemCmd::ReadReq, l1desc_addr, sizeof(uint32_t), 145 NULL, (uint8_t*)&l1Desc.data, (Tick)0); 146 doL1Descriptor(); 147 } 148 149 return fault; 150} 151 152void 153TableWalker::memAttrs(TlbEntry &te, uint8_t texcb, bool s) 154{ 155 DPRINTF(TLBVerbose, "memAttrs texcb:%d s:%d\n", texcb, s); 156 te.shareable = false; // default value 157 bool outer_shareable = false; 158 if (sctlr.tre == 0) { 159 switch(texcb) { 160 case 0: // Stongly-ordered 161 te.nonCacheable = true; 162 te.mtype = TlbEntry::StronglyOrdered; 163 te.shareable = true; 164 te.innerAttrs = 1; 165 te.outerAttrs = 0; 166 break; 167 case 1: // Shareable Device 168 te.nonCacheable = true; 169 te.mtype = TlbEntry::Device; 170 te.shareable = true; 171 te.innerAttrs = 3; 172 te.outerAttrs = 0; 173 break; 174 case 2: // Outer and Inner Write-Through, no Write-Allocate 175 te.mtype = TlbEntry::Normal; 176 te.shareable = s; 177 te.innerAttrs = 6; 178 te.outerAttrs = bits(texcb, 1, 0); 179 break; 180 case 3: // Outer and Inner Write-Back, no Write-Allocate 181 te.mtype = TlbEntry::Normal; 182 te.shareable = s; 183 te.innerAttrs = 7; 184 te.outerAttrs = bits(texcb, 1, 0); 185 break; 186 case 4: // Outer and Inner Non-cacheable 187 te.nonCacheable = true; 188 te.mtype = TlbEntry::Normal; 189 te.shareable = s; 190 te.innerAttrs = 0; 191 te.outerAttrs = bits(texcb, 1, 0); 192 break; 193 case 5: // Reserved 194 break; 195 case 6: // Implementation Defined 196 break; 197 case 7: // Outer and Inner Write-Back, Write-Allocate 198 te.mtype = TlbEntry::Normal; 199 te.shareable = s; 200 te.innerAttrs = 5; 201 te.outerAttrs = 1; 202 break; 203 case 8: // Non-shareable Device 204 te.nonCacheable = true; 205 te.mtype = TlbEntry::Device; 206 te.shareable = false; 207 te.innerAttrs = 3; 208 te.outerAttrs = 0; 209 break; 210 case 9 ... 15: // Reserved 211 break; 212 case 16 ... 31: // Cacheable Memory 213 te.mtype = TlbEntry::Normal; 214 te.shareable = s; 215 if (bits(texcb, 1,0) == 0 || bits(texcb, 3,2) == 0) 216 te.nonCacheable = true; 217 te.innerAttrs = bits(texcb, 1, 0); 218 te.outerAttrs = bits(texcb, 3, 2); 219 break; 220 default: 221 panic("More than 32 states for 5 bits?\n"); 222 } 223 } else { 224 PRRR prrr = tc->readMiscReg(MISCREG_PRRR); 225 NMRR nmrr = tc->readMiscReg(MISCREG_NMRR); 226 DPRINTF(TLBVerbose, "memAttrs PRRR:%08x NMRR:%08x\n", prrr, nmrr); 227 uint8_t curr_tr, curr_ir, curr_or; 228 switch(bits(texcb, 2,0)) { 229 case 0: 230 curr_tr = prrr.tr0; 231 curr_ir = nmrr.ir0; 232 curr_or = nmrr.or0; 233 outer_shareable = (prrr.nos0 == 0); 234 break; 235 case 1: 236 curr_tr = prrr.tr1; 237 curr_ir = nmrr.ir1; 238 curr_or = nmrr.or1; 239 outer_shareable = (prrr.nos1 == 0); 240 break; 241 case 2: 242 curr_tr = prrr.tr2; 243 curr_ir = nmrr.ir2; 244 curr_or = nmrr.or2; 245 outer_shareable = (prrr.nos2 == 0); 246 break; 247 case 3: 248 curr_tr = prrr.tr3; 249 curr_ir = nmrr.ir3; 250 curr_or = nmrr.or3; 251 outer_shareable = (prrr.nos3 == 0); 252 break; 253 case 4: 254 curr_tr = prrr.tr4; 255 curr_ir = nmrr.ir4; 256 curr_or = nmrr.or4; 257 outer_shareable = (prrr.nos4 == 0); 258 break; 259 case 5: 260 curr_tr = prrr.tr5; 261 curr_ir = nmrr.ir5; 262 curr_or = nmrr.or5; 263 outer_shareable = (prrr.nos5 == 0); 264 break; 265 case 6: 266 panic("Imp defined type\n"); 267 case 7: 268 curr_tr = prrr.tr7; 269 curr_ir = nmrr.ir7; 270 curr_or = nmrr.or7; 271 outer_shareable = (prrr.nos7 == 0); 272 break; 273 } 274 275 switch(curr_tr) { 276 case 0: 277 DPRINTF(TLBVerbose, "StronglyOrdered\n"); 278 te.mtype = TlbEntry::StronglyOrdered; 279 te.nonCacheable = true; 280 te.innerAttrs = 1; 281 te.outerAttrs = 0; 282 te.shareable = true; 283 break; 284 case 1: 285 DPRINTF(TLBVerbose, "Device ds1:%d ds0:%d s:%d\n", 286 prrr.ds1, prrr.ds0, s); 287 te.mtype = TlbEntry::Device; 288 te.nonCacheable = true; 289 te.innerAttrs = 3; 290 te.outerAttrs = 0; 291 if (prrr.ds1 && s) 292 te.shareable = true; 293 if (prrr.ds0 && !s) 294 te.shareable = true; 295 break; 296 case 2: 297 DPRINTF(TLBVerbose, "Normal ns1:%d ns0:%d s:%d\n", 298 prrr.ns1, prrr.ns0, s); 299 te.mtype = TlbEntry::Normal; 300 if (prrr.ns1 && s) 301 te.shareable = true; 302 if (prrr.ns0 && !s) 303 te.shareable = true; 304 //te.shareable = outer_shareable; 305 break; 306 case 3: 307 panic("Reserved type"); 308 } 309 310 if (te.mtype == TlbEntry::Normal){ 311 switch(curr_ir) { 312 case 0: 313 te.nonCacheable = true; 314 te.innerAttrs = 0; 315 break; 316 case 1: 317 te.innerAttrs = 5; 318 break; 319 case 2: 320 te.innerAttrs = 6; 321 break; 322 case 3: 323 te.innerAttrs = 7; 324 break; 325 } 326 327 switch(curr_or) { 328 case 0: 329 te.nonCacheable = true; 330 te.outerAttrs = 0; 331 break; 332 case 1: 333 te.outerAttrs = 1; 334 break; 335 case 2: 336 te.outerAttrs = 2; 337 break; 338 case 3: 339 te.outerAttrs = 3; 340 break; 341 } 342 } 343 } 344 345 /** Formatting for Physical Address Register (PAR) 346 * Only including lower bits (TLB info here) 347 * PAR: 348 * PA [31:12] 349 * Reserved [11] 350 * TLB info [10:1] 351 * NOS [10] (Not Outer Sharable) 352 * NS [9] (Non-Secure) 353 * -- [8] (Implementation Defined) 354 * SH [7] (Sharable) 355 * Inner[6:4](Inner memory attributes) 356 * Outer[3:2](Outer memory attributes) 357 * SS [1] (SuperSection) 358 * F [0] (Fault, Fault Status in [6:1] if faulted) 359 */ 360 te.attributes = ( 361 ((outer_shareable ? 0:1) << 10) | 362 // TODO: NS Bit 363 ((te.shareable ? 1:0) << 7) | 364 (te.innerAttrs << 4) | 365 (te.outerAttrs << 2) 366 // TODO: Supersection bit 367 // TODO: Fault bit 368 ); 369 370 371} 372 373void 374TableWalker::doL1Descriptor() 375{ 376 DPRINTF(TLB, "L1 descriptor for %#x is %#x\n", vaddr, l1Desc.data); 377 TlbEntry te; 378 379 switch (l1Desc.type()) { 380 case L1Descriptor::Ignore: 381 case L1Descriptor::Reserved: 382 if (!delayed) { 383 tc = NULL; 384 req = NULL; 385 } 386 DPRINTF(TLB, "L1 Descriptor Reserved/Ignore, causing fault\n"); 387 if (isFetch) 388 fault = new PrefetchAbort(vaddr, ArmFault::Translation0); 389 else 390 fault = new DataAbort(vaddr, NULL, isWrite, 391 ArmFault::Translation0); 392 return; 393 case L1Descriptor::Section: 394 if (sctlr.afe && bits(l1Desc.ap(), 0) == 0) { 395 /** @todo: check sctlr.ha (bit[17]) if Hardware Access Flag is 396 * enabled if set, do l1.Desc.setAp0() instead of generating 397 * AccessFlag0 398 */ 399 400 fault = new DataAbort(vaddr, NULL, isWrite, 401 ArmFault::AccessFlag0); 402 } 403 404 if (l1Desc.supersection()) { 405 panic("Haven't implemented supersections\n"); 406 } 407 te.N = 20; 408 te.pfn = l1Desc.pfn(); 409 te.size = (1<<te.N) - 1; 410 te.global = !l1Desc.global(); 411 te.valid = true; 412 te.vpn = vaddr >> te.N; 413 te.sNp = true; 414 te.xn = l1Desc.xn(); 415 te.ap = l1Desc.ap(); 416 te.domain = l1Desc.domain(); 417 te.asid = contextId; 418 memAttrs(te, l1Desc.texcb(), l1Desc.shareable()); 419 420 DPRINTF(TLB, "Inserting Section Descriptor into TLB\n"); 421 DPRINTF(TLB, " - N%d pfn:%#x size: %#x global:%d valid: %d\n", 422 te.N, te.pfn, te.size, te.global, te.valid); 423 DPRINTF(TLB, " - vpn:%#x sNp: %d xn:%d ap:%d domain: %d asid:%d\n", 424 te.vpn, te.sNp, te.xn, te.ap, te.domain, te.asid); 425 DPRINTF(TLB, " - domain from l1 desc: %d data: %#x bits:%d\n", 426 l1Desc.domain(), l1Desc.data, (l1Desc.data >> 5) & 0xF ); 427 428 if (!timing) { 429 tc = NULL; 430 req = NULL; 431 } 432 tlb->insert(vaddr, te); 433 434 return; 435 case L1Descriptor::PageTable: 436 Addr l2desc_addr; 437 l2desc_addr = l1Desc.l2Addr() | (bits(vaddr, 19,12) << 2); 438 DPRINTF(TLB, "L1 descriptor points to page table at: %#x\n", 439 l2desc_addr); 440 441 // Trickbox address check 442 fault = tlb->walkTrickBoxCheck(l2desc_addr, vaddr, sizeof(uint32_t), 443 isFetch, isWrite, l1Desc.domain(), false); 444 if (fault) { 445 if (!timing) { 446 tc = NULL; 447 req = NULL; 448 } 449 return; 450 } 451 452 453 if (timing) { 454 delayed = true; 455 port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t), 456 &doL2DescEvent, (uint8_t*)&l2Desc.data, 0); 457 } else { 458 port->dmaAction(MemCmd::ReadReq, l2desc_addr, sizeof(uint32_t), 459 NULL, (uint8_t*)&l2Desc.data, 0); 460 doL2Descriptor(); 461 } 462 return; 463 default: 464 panic("A new type in a 2 bit field?\n"); 465 } 466} 467 468void 469TableWalker::doL2Descriptor() 470{ 471 DPRINTF(TLB, "L2 descriptor for %#x is %#x\n", vaddr, l2Desc.data); 472 TlbEntry te; 473 474 if (l2Desc.invalid()) { 475 DPRINTF(TLB, "L2 descriptor invalid, causing fault\n"); 476 if (!delayed) { 477 tc = NULL; 478 req = NULL; 479 } 480 if (isFetch) 481 fault = new PrefetchAbort(vaddr, ArmFault::Translation1); 482 else 483 fault = new DataAbort(vaddr, l1Desc.domain(), isWrite, 484 ArmFault::Translation1); 485 return; 486 } 487 488 if (sctlr.afe && bits(l2Desc.ap(), 0) == 0) { 489 /** @todo: check sctlr.ha (bit[17]) if Hardware Access Flag is enabled 490 * if set, do l2.Desc.setAp0() instead of generating AccessFlag0 491 */ 492 493 fault = new DataAbort(vaddr, NULL, isWrite, ArmFault::AccessFlag1); 494 } 495 496 if (l2Desc.large()) { 497 te.N = 16; 498 te.pfn = l2Desc.pfn(); 499 } else { 500 te.N = 12; 501 te.pfn = l2Desc.pfn(); 502 } 503 504 te.valid = true; 505 te.size = (1 << te.N) - 1; 506 te.asid = contextId; 507 te.sNp = false; 508 te.vpn = vaddr >> te.N; 509 te.global = l2Desc.global(); 510 te.xn = l2Desc.xn(); 511 te.ap = l2Desc.ap(); 512 te.domain = l1Desc.domain(); 513 memAttrs(te, l2Desc.texcb(), l2Desc.shareable()); 514 515 if (!delayed) { 516 tc = NULL; 517 req = NULL; 518 } 519 tlb->insert(vaddr, te); 520} 521 522void 523TableWalker::doL1DescriptorWrapper() 524{ 525 delayed = false; 526 527 DPRINTF(TLBVerbose, "calling doL1Descriptor\n"); 528 doL1Descriptor(); 529 530 // Check if fault was generated 531 if (fault != NoFault) { 532 transState->finish(fault, req, tc, mode); 533 534 req = NULL; 535 tc = NULL; 536 delayed = false; 537 } 538 else if (!delayed) { 539 DPRINTF(TLBVerbose, "calling translateTiming again\n"); 540 fault = tlb->translateTiming(req, tc, transState, mode); 541 542 req = NULL; 543 tc = NULL; 544 delayed = false; 545 } 546} 547 548void 549TableWalker::doL2DescriptorWrapper() 550{ 551 assert(delayed); 552 553 DPRINTF(TLBVerbose, "calling doL2Descriptor\n"); 554 doL2Descriptor(); 555 556 // Check if fault was generated 557 if (fault != NoFault) { 558 transState->finish(fault, req, tc, mode); 559 } 560 else { 561 DPRINTF(TLBVerbose, "calling translateTiming again\n"); 562 fault = tlb->translateTiming(req, tc, transState, mode); 563 } 564 565 req = NULL; 566 tc = NULL; 567 delayed = false; 568} 569 570ArmISA::TableWalker * 571ArmTableWalkerParams::create() 572{ 573 return new ArmISA::TableWalker(this); 574} 575 576