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