sylveos

Toy Operating System
Log | Files | Refs

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 }