mmu.zig (17789B)
1 const system = @import("./system.zig"); 2 const mem = @import("./mem.zig"); 3 4 pub const LockdownIndex = struct { 5 pub inline fn get() u3 { 6 return asm volatile ("MRC p15, 5, %[result], c15, c4, 2" 7 : [result] "=r" (-> u3), 8 ); 9 } 10 11 pub inline fn set(index: u3) void { 12 return asm volatile ("MCR p15, 5, %[value], c15, c4, 2" 13 : 14 : [value] "r" (index), 15 ); 16 } 17 }; 18 19 pub const LockdownVA = packed struct(u32) { 20 pub const Scope = enum(u1) { 21 ApplicationSpecific = 0, 22 Global = 1, 23 }; 24 25 // address space ID 26 asid: u8, 27 _reserved_8: u1, 28 scope: Scope, 29 _reserved_10_11: u2, 30 virtual_address: u20, 31 32 pub inline fn get() @This() { 33 return asm volatile ("MRC p15, 5, %[result], c15, c5, 2" 34 : [result] "=r" (-> @This()), 35 ); 36 } 37 38 pub inline fn set(va: *const @This()) void { 39 return asm volatile ("MCR p15, 5, %[value], c15, c5, 2" 40 : 41 : [value] "r" (@as(u32, @bitCast(va.*))), 42 ); 43 } 44 }; 45 46 pub const AccessPermissions = enum(u2) { 47 TotalNoAccess = 0b00, 48 UserNoAccess = 0b01, 49 UserRead = 0b10, 50 UserReadWrite = 0b11, 51 }; 52 53 pub const AccessPermissionsExtended = enum(u1) { 54 SupervisorRW = 0, 55 SupervisorRO = 1, 56 }; 57 58 pub const LockdownPA = packed struct(u32) { 59 pub const Size = enum(u2) { 60 @"16MB" = 0b00, 61 @"4KB" = 0b01, 62 @"64KB" = 0b10, 63 @"1MB" = 0b11, 64 }; 65 pub const Security = enum(u1) { 66 Secure = 0, 67 NonSecure = 1, 68 }; 69 70 valid: bool, 71 ap0: AccessPermissions, 72 apx: AccessPermissionsExtended, 73 _reversed_5_4: u2, 74 size: Size, 75 nstid: Security, 76 nsa: Security, 77 _reserved_11_10: u2, 78 physical_address: u20, 79 80 pub inline fn get() @This() { 81 return asm volatile ("MRC p15, 5, %[result], c15, c6, 2" 82 : [result] "=r" (-> @This()), 83 ); 84 } 85 86 pub inline fn set(pa: *const @This()) void { 87 return asm volatile ("MCR p15, 5, %[value], c15, c6, 2" 88 : 89 : [value] "r" (@as(u32, @bitCast(pa.*))), 90 ); 91 } 92 }; 93 94 pub const LockdownAttributesRegister = packed struct(u32) { 95 pub const PageTableEncodingPreset = enum(u5) { 96 StronglyOrdered = 0b000_0_0, 97 SharedDevice = 0b000_0_1, 98 OuterInnerWriteThrough = 0b000_1_0, 99 OuterInnerWriteBackNoAllocOnWrite = 0b000_1_1, 100 OuterInnerNoncacheable = 0b001_0_0, 101 OuterInnerWriteBackAllocOnWrite = 0b001_1_1, 102 NonSharedDevice = 0b010_0_0, 103 }; 104 105 pub const PageTableEncodingCached = packed struct(u5) { 106 pub const CachePolicy = enum(u2) { 107 Noncacheable = 0b00, 108 WriteBackCachedWriteAlloc = 0b01, 109 WriteThroughCachedNoWriteAlloc = 0b10, 110 WriteBackCachedNoWriteAlloc = 0b11, 111 }; 112 113 always_one: u1 = 1, 114 outer_policy: CachePolicy, 115 inner_policy: CachePolicy, 116 }; 117 118 pub const PageTableEncodingManual = packed struct(u5) { 119 b: u1, 120 c: u1, 121 tex: u3, 122 }; 123 124 pub const PageTableEncoding = packed union { 125 preset: PageTableEncodingPreset, 126 cached: PageTableEncodingCached, 127 manual: PageTableEncodingManual, 128 }; 129 130 shared: bool, 131 page_table_encoding: PageTableEncoding, 132 execute_never: bool, 133 domain: u4, 134 _reserved_24_11: u14 = 0, 135 subpages_valid: bool, 136 ap1: AccessPermissions, 137 ap2: AccessPermissions, 138 ap3: AccessPermissions, 139 140 pub inline fn get() @This() { 141 return asm volatile ("MRC p15, 5, %[result], c15, c7, 2" 142 : [result] "=r" (-> @This()), 143 ); 144 } 145 146 pub inline fn set(attr: *const @This()) void { 147 return asm volatile ("MCR p15, 5, %[value], c15, c7, 2" 148 : 149 : [value] "r" (@as(u32, @bitCast(attr.*))), 150 ); 151 } 152 }; 153 154 pub const DomainAccessControlRegister = packed struct(u32) { 155 pub const DomainAccess = enum(u2) { 156 NoAccess = 0b00, 157 Client = 0b01, 158 Manager = 0b11, 159 }; 160 161 // Inconvenient but necessary to ensure ordering 162 d0: DomainAccess = .NoAccess, 163 d1: DomainAccess = .NoAccess, 164 d2: DomainAccess = .NoAccess, 165 d3: DomainAccess = .NoAccess, 166 d4: DomainAccess = .NoAccess, 167 d5: DomainAccess = .NoAccess, 168 d6: DomainAccess = .NoAccess, 169 d7: DomainAccess = .NoAccess, 170 d8: DomainAccess = .NoAccess, 171 d9: DomainAccess = .NoAccess, 172 d10: DomainAccess = .NoAccess, 173 d11: DomainAccess = .NoAccess, 174 d12: DomainAccess = .NoAccess, 175 d13: DomainAccess = .NoAccess, 176 d14: DomainAccess = .NoAccess, 177 d15: DomainAccess = .NoAccess, 178 179 pub inline fn get() @This() { 180 return asm volatile ("MRC p15, 0, %[result], c3, c0, 0" 181 : [result] "=r" (-> @This()), 182 ); 183 } 184 185 pub inline fn set(domains: *const @This()) void { 186 return asm volatile ("MCR p15, 0, %[value], c3, c0, 0" 187 : 188 : [value] "r" (@as(u32, @bitCast(domains.*))), 189 ); 190 } 191 192 pub inline fn set_all(access: DomainAccess) void { 193 const domains: @This() = .{ 194 .d0 = access, 195 .d1 = access, 196 .d2 = access, 197 .d3 = access, 198 .d4 = access, 199 .d5 = access, 200 .d6 = access, 201 .d7 = access, 202 .d8 = access, 203 .d9 = access, 204 .d10 = access, 205 .d11 = access, 206 .d12 = access, 207 .d13 = access, 208 .d14 = access, 209 .d15 = access, 210 }; 211 212 domains.set(); 213 mem.barrier(.Instruction); 214 } 215 }; 216 217 pub const TLBTypeRegister = packed struct(u32) { 218 unified: bool, 219 _reserved_7_1: u7, 220 data_lockable_size: u8, 221 instruction_lockable_size: u8, 222 _reserved_31_24: u8, 223 224 pub inline fn get() @This() { 225 return asm volatile ("MRC p15, 0, %[result], c0, c0, 3" 226 : [result] "=r" (-> @This()), 227 ); 228 } 229 230 pub inline fn set(domains: *const @This()) void { 231 return asm volatile ("MCR p15, 0, %[value], c0, c0, 3" 232 : 233 : [value] "r" (@as(u32, @bitCast(domains.*))), 234 ); 235 } 236 }; 237 238 pub const OuterCacheableAttr = enum(u2) { 239 OuterNoncacheable = 0b00, 240 WriteBackWriteAlloc = 0b01, 241 WriteThrough = 0b10, 242 WriteBackNoWriteAlloc = 0b11, 243 }; 244 245 pub const InnerCacheableAttr = enum(u3) { 246 Noncacheable = 0b000, 247 StronglyOrdered = 0b001, 248 Device = 0b011, 249 InnerWriteThrough = 0b110, 250 InnerWriteBack = 0b111, 251 }; 252 253 pub const TranslationTableBaseRegister0 = packed struct(u32) { 254 inner_cacheable: bool = false, 255 shared: bool = false, 256 ecc: bool = false, 257 outer_cacheable_attr: OuterCacheableAttr = .OuterNoncacheable, 258 translation_table_base: u27, 259 260 pub inline fn get() @This() { 261 return asm volatile ("MRC p15, 0, %[result], c2, c0, 0" 262 : [result] "=r" (-> @This()), 263 ); 264 } 265 266 pub inline fn set(ttbc: *const @This()) void { 267 return asm volatile ("MCR p15, 0, %[value], c2, c0, 0" 268 : 269 : [value] "r" (@as(u32, @bitCast(ttbc.*))), 270 ); 271 } 272 }; 273 274 pub const TranslationTableBaseRegister1 = packed struct(u32) { 275 inner_cacheable: bool, 276 shared: bool, 277 ecc: bool, 278 outer_cacheable_attr: OuterCacheableAttr, 279 // _reserved_13_5: u9, 280 translation_table_base: u27, 281 282 pub inline fn get() @This() { 283 return asm volatile ("MRC p15, 0, %[result], c2, c0, 1" 284 : [result] "=r" (-> @This()), 285 ); 286 } 287 288 pub inline fn clear() void { 289 const zero: @This() = @bitCast(@as(u32, 0)); 290 zero.set(); 291 } 292 293 pub inline fn set(ttbc: *const @This()) void { 294 return asm volatile ("MCR p15, 0, %[value], c2, c0, 1" 295 : 296 : [value] "r" (@as(u32, @bitCast(ttbc.*))), 297 ); 298 } 299 }; 300 301 // ARM ARM only defines boundary_size 302 // ARM1176JZF-S specifies more on top 303 pub const TranslationTableBaseControl = packed struct(u32) { 304 pub const BoundarySize = enum(u3) { 305 @"16KB" = 0b000, 306 @"8KB" = 0b001, 307 @"4KB" = 0b010, 308 @"2KB" = 0b011, 309 @"1KB" = 0b100, 310 @"512B" = 0b101, 311 @"256B" = 0b110, 312 @"128B" = 0b111, 313 }; 314 315 boundary_size: BoundarySize = .@"16KB", 316 _reserved_3: u1 = 0, 317 page_table_walk_0: u1 = 0, 318 page_table_walk_1: u1 = 0, 319 _reserved_31_6: u26 = 0, 320 321 pub inline fn get() @This() { 322 return asm volatile ("MRC p15, 0, %[result], c2, c0, 2" 323 : [result] "=r" (-> @This()), 324 ); 325 } 326 327 pub inline fn set(ttbc: *const @This()) void { 328 return asm volatile ("MCR p15, 0, %[value], c2, c0, 2" 329 : 330 : [value] "r" (@as(u32, @bitCast(ttbc.*))), 331 ); 332 } 333 }; 334 335 pub const ContextId = packed struct(u32) { 336 asid: u8 = 0, 337 pid: u24 = 0, 338 339 pub inline fn get() @This() { 340 return asm volatile ("MRC p15, 0, %[result], c13, c0, 1" 341 : [result] "=r" (-> @This()), 342 ); 343 } 344 345 pub inline fn set(ctx: *const @This()) void { 346 return asm volatile ("MCR p15, 0, %[value], c13, c0, 1" 347 : 348 : [value] "r" (@as(u32, @bitCast(ctx.*))), 349 ); 350 } 351 352 pub inline fn clear() void { 353 const ctx: ContextId = .{}; 354 ctx.set(); 355 } 356 }; 357 358 pub const FirstLevelDescriptor = packed struct(u32) { 359 pub const Fault = packed struct(u30) { 360 _ignore_31_2: u30 = 0, 361 }; 362 pub const Coarse = packed struct(u30) { 363 _reserved_0: u1 = 0, 364 not_secure: bool = false, 365 _reserved_1: u1 = 0, 366 domain: u4, 367 ecc: bool = false, 368 coarse_base_address: u22, 369 }; 370 pub const Section = packed struct(u30) { 371 b: u1, 372 c: u1, 373 never_execute: bool, 374 domain: u4, 375 ecc: bool = false, 376 ap: AccessPermissions, 377 tex: u3, 378 apx: AccessPermissionsExtended, 379 shared: bool, 380 not_global: bool, 381 is_supersection: bool = false, 382 not_secure: bool = false, 383 section_base_address: u12, 384 }; 385 pub const SuperSection = packed struct(u30) { 386 b: u1, 387 c: u1, 388 never_execute: bool, 389 base_address_39_36: u4, 390 ecc: bool = false, 391 ap: AccessPermissions, 392 tex: u3, 393 apx: AccessPermissionsExtended, 394 shared: bool, 395 not_global: bool, 396 is_supersection: bool = true, 397 not_secure: bool = false, 398 base_address_35_32: u4, 399 section_base_address: u8, 400 }; 401 402 pub const Type = enum(u2) { 403 Fault = 0b00, 404 CoarsePageTable = 0b01, 405 Section = 0b10, 406 }; 407 408 ty: Type, 409 descriptor: packed union { 410 fault: Fault, 411 coarse: Coarse, 412 section: Section, 413 supersection: SuperSection, 414 }, 415 }; 416 417 pub const SecondLevelDescriptor = packed struct(u32) { 418 pub const Fault = packed struct(u30) { 419 _ignore_31_2: u30 = 0, 420 }; 421 pub const LargePage = packed struct(u30) { 422 b: u1, 423 c: u1, 424 ap: AccessPermissions, 425 _reserved_8_6: u3 = 0, 426 apx: AccessPermissionsExtended, 427 shared: bool, 428 not_global: bool, 429 tex: u3, 430 never_execute: bool, 431 base_address: u16, 432 }; 433 pub const SmallPage = packed struct(u30) { 434 b: u1, 435 c: u1, 436 ap: AccessPermissions, 437 tex: u3, 438 apx: AccessPermissionsExtended, 439 shared: bool, 440 not_global: bool, 441 base_address: u20, 442 }; 443 444 pub const Type = enum(u2) { 445 Fault = 0b00, 446 LargePage = 0b01, 447 SmallPageNeverExecute = 0b11, 448 SmallPageExecutable = 0b10, 449 }; 450 ty: Type, 451 descriptor: packed union { 452 fault: Fault, 453 large_page: LargePage, 454 small_page: SmallPage, 455 }, 456 }; 457 458 pub const TranslationMode = enum { 459 PrivilegedRead, 460 PrivilegedWrite, 461 UserRead, 462 UserWrite, 463 }; 464 465 pub const TranslationResult = packed struct(u32) { 466 pub const TranslationSuccess = packed struct(u31) { 467 _reserved_1: u1, 468 outer: OuterCacheableAttr, 469 inner: InnerCacheableAttr, 470 shared: bool, 471 _unused_8: u1, 472 not_secure: bool, 473 address: u22, 474 }; 475 pub const TranslationAborted = packed struct(u31) { 476 fsr_bits: u6, 477 _reserved_7_31: u25, 478 }; 479 480 aborted: bool, 481 inner: packed union { 482 success: TranslationSuccess, 483 aborted: TranslationAborted, 484 }, 485 }; 486 487 // 3-82 488 // Write to p15, c7, c4, 0 with VA and read to get PA + extra 489 pub inline fn va_translation_cw(va: u32, comptime mode: TranslationMode) TranslationResult { 490 const mode_asm = switch (mode) { 491 .PrivilegedRead => "0", 492 .PrivilegedWrite => "1", 493 .UserRead => "2", 494 .UserWrite => "3", 495 }; 496 497 asm volatile ("MCR p15, 0, %[value], c7, c8, " ++ mode_asm 498 : 499 : [value] "r" (va), 500 ); 501 502 return asm volatile ("MRC p15, 0, %[result], c7, c4, 0" 503 : [result] "=r" (-> TranslationResult), 504 ); 505 } 506 507 pub fn init() void { 508 // Reset MMU 509 reset(); 510 511 // Read-Modify-Write XP in CP1 512 // Ensure MMU is disabled 513 var reg = system.SystemControlRegister.get(); 514 reg.extended_page_tables = true; 515 reg.mmu_enabled = false; 516 reg.set(); 517 } 518 519 // We want to ensure the processor is in a known state, so: 520 // 1. Invalidate all caches 521 // 2. B2 (memory) ordering operations 522 pub fn reset() void { 523 // Instruction Cache/Data Cache 524 system.invalidate_all_caches(); 525 // ITLB/DTLB 526 system.invalidate_TLB(); 527 528 // B2 ordering 529 // Ensure maintenance completes 530 mem.barrier(.Write); 531 532 // // Flush BTB as part of reset 533 // system.flush_btb(); 534 535 // // Ensure completion 536 // mem.barrier(.Write); 537 // // Required after BTB flush 538 // mem.barrier(.Instruction); 539 } 540 541 // 6-9: 6.4.1 - Enabling the MMU 542 // 1. Program all relevant CP15 registers 543 // 2. Program first-level and second-level page tables as required 544 // 3. Disable and invalidate instruction cache 545 // 4. Enable MMU by setting bit 0 in System Control Register 546 // 5. Optionally reenable instruction cache 547 pub noinline fn enable() void { 548 // 1. Program all relevant CP15 registers 549 // Should be done by reset/user 550 551 // 2. Program first-level and second-level page tables 552 // Should be done by user (via set_context_ttbr0) 553 554 // 3. Disable instruction cache 555 var reg = system.SystemControlRegister.get(); 556 reg.level_one_instruction_cache = false; 557 reg.set(); 558 559 // 3. Invalidate instruction cache 560 mem.barrier(.Instruction); 561 system.invalidate_icache(); 562 // Ensure completion of instruction cache maintenance 563 mem.barrier(.Write); 564 565 // 4. Enable MMU 566 reg.mmu_enabled = true; 567 reg.set(); 568 569 // Ensure completion of enabling 570 mem.barrier(.Instruction); 571 572 // Required after enabling MMU 573 system.flush_btb(); 574 // Required after BTB flush 575 mem.barrier(.Instruction); 576 } 577 578 // 6-9: 6.4.2 - Disabling the MMU 579 // 1. Clear bit 2 to 0 in System Control Register (level one data cache) 580 // 2. Clear bit 0 to 0 in System Control Register (MMU Enabled) 581 pub noinline fn disable() void { 582 // 0. Clear cache so entries are properly written 583 system.clear_entire_data_cache(); 584 mem.barrier(.Write); 585 586 var reg = system.SystemControlRegister.get(); 587 588 // 1. Clear bit 2 to 0 589 reg.level_one_data_cache = false; 590 reg.set(); 591 592 mem.barrier(.Instruction); 593 594 // Invalidate instruction cache 595 system.invalidate_icache(); 596 mem.barrier(.Write); 597 598 // 2. Clear bit 0 to 0 599 reg.mmu_enabled = false; 600 reg.set(); 601 602 mem.barrier(.Instruction); 603 604 // BTB is to be flushed after any change to MMU 605 system.flush_btb(); 606 // Prefetch flush is required after BTB flush 607 mem.barrier(.Instruction); 608 } 609 610 // 3-129 611 pub fn set_context(ctx: ContextId) void { 612 // "You must ensure that software performs a Data Synchronization 613 // Barrier operation before changes to this register." 614 mem.barrier(.Write); 615 616 ctx.set(); 617 618 // "You must execute an IMB instruction immediately after changes 619 // to the Context ID Register." 620 system.flush_btb(); 621 mem.barrier(.Instruction); 622 } 623 624 // B2-25 Synchronization of Changes of ASID and TTBR 625 // 1. Change ASID to 0 626 // 2. Prefetch Flush 627 // 3. Change TTRB 628 // 4. Prefetch Flush 629 // 5. Change ASID to new value 630 pub fn set_context_ttbr0(ctx: ContextId, ttrb0: TranslationTableBaseRegister0) void { 631 mem.barrier(.Write); 632 633 // 1. Change ASID to 0 634 const cleared: ContextId = .{}; 635 cleared.set(); 636 mem.barrier(.Instruction); 637 638 // 3. Set control N = 0 to use TTRB0 639 const control: TranslationTableBaseControl = .{ 640 // Specifies TTRB0 (3-62), N = 0 641 .boundary_size = .@"16KB", 642 }; 643 control.set(); 644 645 // 3. Set TTRB0 646 ttrb0.set(); 647 648 // 4. Prefetch flush 649 mem.barrier(.Instruction); 650 651 // 5. Change ASID to new value 652 ctx.set(); 653 654 // BTB flush required after change to ContextID 655 system.flush_btb(); 656 mem.barrier(.Instruction); 657 } 658 659 // B2.7.3 660 // Synchronize PTE modifications 661 // 662 // 1. Store PTE 663 // 2. Clean PTE line 664 // 3. DSB 665 // 4. Invalidate TLB 666 // 5. Invalidate BTB 667 // 6. DSB 668 // 7. Prefetch 669 pub fn sync_pte() void { 670 // 1. Store page table 671 // Should be done by user 672 673 // 2. Clean page table 674 // This ensures that the page table is fully committed to memory 675 system.clear_entire_data_cache(); 676 677 // 3. DSB 678 mem.barrier(.Write); 679 680 // 4. Invalidate TLB 681 system.invalidate_TLB(); 682 683 // 5. Invalidate BTB 684 system.flush_btb(); 685 686 // 6. DSB 687 mem.barrier(.Write); 688 689 // 7. Prefetch 690 mem.barrier(.Instruction); 691 } 692 693 pub fn set_domain(domain: DomainAccessControlRegister) void { 694 domain.set(); 695 mem.barrier(.Instruction); 696 } 697 698 // Higher Level Abstractions 699 700 pub fn is_enabled() bool { 701 return system.SystemControlRegister.get().mmu_enabled; 702 }