; Copyright 2009 Castle Technology Ltd ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. ; You may obtain a copy of the License at ; ; http://www.apache.org/licenses/LICENSE-2.0 ; ; Unless required by applicable law or agreed to in writing, software ; distributed under the License is distributed on an "AS IS" BASIS, ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ; See the License for the specific language governing permissions and ; limitations under the License. ; ; > VMSAv6 GBLL DebugAborts DebugAborts SETL {FALSE} ; MMU interface file - VMSAv6 version ; Created from s.ARM600 by JL 18-Feb-09 ; Make sure we aren't being compiled against a CPU that can't possibly support a VMSAv6 MMU ASSERT :LNOT: NoARMv6 KEEP ; Convert given page flags to the equivalent temp uncacheable L2PT flags MACRO GetTempUncache $out, $pageflags, $pcbtrans, $temp ASSERT DynAreaFlags_CPBits = 7*XCB_P :SHL: 10 ASSERT DynAreaFlags_NotCacheable = XCB_NC :SHL: 4 ASSERT DynAreaFlags_NotBufferable = XCB_NB :SHL: 4 AND $out, $pageflags, #DynAreaFlags_NotCacheable + DynAreaFlags_NotBufferable AND $temp, $pageflags, #DynAreaFlags_CPBits ORR $out, $out, #XCB_TU<<4 ; treat as temp uncacheable ORR $out, $out, $temp, LSR #10-4 LDRB $out, [$pcbtrans, $out, LSR #4] ; convert to X, C and B bits for this CPU MEND TempUncache_L2PTMask * L2_B+L2_C+L2_TEX ; **************** CAM manipulation utility routines *********************************** ; ************************************************************************************** ; ; BangCamUpdate - Update CAM, MMU for page move, coping with page currently mapped in ; ; mjs Oct 2000 ; reworked to use generic ARM ops (vectored to appropriate routines during boot) ; ; First look in the CamEntries table to find the logical address L this physical page is ; currently allocated to. Then check in the Level 2 page tables to see if page L is currently ; at page R2. If it is, then map page L to be inaccessible, otherwise leave page L alone. ; Then map logical page R3 to physical page R2. ; ; in: r2 = physical page number ; r3 = logical address (2nd copy if doubly mapped area) ; r9 = offset from 1st to 2nd copy of doubly mapped area (either source or dest, but not both) ; r11 = PPL + CB bits ; ; out: r0, r1, r4, r6 corrupted ; r2, r3, r5, r7-r12 preserved ; ; NB Use of stack is allowed in this routine BangCamUpdate ROUT TST r11, #DynAreaFlags_DoublyMapped ; if moving page to doubly mapped area SUBNE r3, r3, r9 ; then CAM soft copy holds ptr to 1st copy LDR r1, =ZeroPage LDR r1, [r1, #CamEntriesPointer] ADD r1, r1, r2, LSL #CAM_EntrySizeLog2 ; point at cam entry (logaddr, PPL) ASSERT CAM_LogAddr=0 ASSERT CAM_PageFlags=4 LDMIA r1, {r0, r6} ; r0 = current logaddress, r6 = current PPL BIC r4, r11, #PageFlags_Unsafe STMIA r1, {r3, r4} ; store new address, PPL Push "r0, r6" ; save old logical address, PPL LDR r1, =ZeroPage+PhysRamTable ; go through phys RAM table MOV r6, r2 ; make copy of r2 (since that must be preserved) 10 LDMIA r1!, {r0, r4} ; load next address, size SUBS r6, r6, r4, LSR #12 ; subtract off that many pages BCS %BT10 ; if more than that, go onto next bank ADD r6, r6, r4, LSR #12 ; put back the ones which were too many ADD r0, r0, r6, LSL #12 ; move on address by the number of pages left LDR r6, [sp] ; reload old logical address ; now we have r6 = old logical address, r2 = physical page number, r0 = physical address TEQ r6, r3 ; TMD 19-Jan-94: if old logaddr = new logaddr, then BEQ %FT20 ; don't remove page from where it is, to avoid window ; where page is nowhere. LDR r1, =L2PT ADD r6, r1, r6, LSR #10 ; r6 -> L2PT entry for old log.addr MOV r4, r6, LSR #12 ; r4 = word offset into L2 for address r6 LDR r4, [r1, r4, LSL #2] ; r4 = L2PT entry for L2PT entry for old log.addr TST r4, #3 ; if page not there BEQ %FT20 ; then no point in trying to remove it LDR r4, [r6] ; r4 = L2PT entry for old log.addr MOV r4, r4, LSR #12 ; r4 = physical address for old log.addr TEQ r4, r0, LSR #12 ; if equal to physical address of page being moved BNE %FT20 ; if not there, then just put in new page AND r4, r11, #PageFlags_Unsafe Push "r0, r3, r11, r14" ; save phys.addr, new log.addr, new PPL, lr ADD r3, sp, #4*4 LDMIA r3, {r3, r11} ; reload old logical address, old PPL LDR r0, =DuffEntry ; Nothing to do if wasn't mapped in ORR r11, r11, r4 TEQ r3, r0 MOV r0, #0 ; cause translation fault BLNE BangL2PT ; map page out Pull "r0, r3, r11, r14" 20 ADD sp, sp, #8 ; junk old logical address, PPL B BangCamAltEntry ; and branch into BangCam code ; ************************************************************************************** ; ; BangCam - Update CAM, MMU for page move, assuming page currently mapped out ; ; This routine maps a physical page to a given logical address ; It is assumed that the physical page is currently not mapped anywhere else ; ; in: r2 = physical page number ; r3 = logical address (2nd copy if doubly mapped) ; r9 = offset from 1st to 2nd copy of doubly mapped area (either source or dest, but not both) ; r11 = PPL ; ; out: r0, r1, r4, r6 corrupted ; r2, r3, r5, r7-r12 preserved ; ; NB Can't use stack - there might not be one! ; ; NB Also - the physical page number MUST be in range. ; This routine must work in 32-bit mode BangCam ROUT TST r11, #DynAreaFlags_DoublyMapped ; if area doubly mapped SUBNE r3, r3, r9 ; then move ptr to 1st copy LDR r1, =ZeroPage+PhysRamTable ; go through phys RAM table MOV r6, r2 ; make copy of r2 (since that must be preserved) 10 LDMIA r1!, {r0, r4} ; load next address, size SUBS r6, r6, r4, LSR #12 ; subtract off that many pages BCS %BT10 ; if more than that, go onto next bank ADD r6, r6, r4, LSR #12 ; put back the ones which were too many ADD r0, r0, r6, LSL #12 ; move on address by the number of pages left BangCamAltEntry LDR r4, =DuffEntry ; check for requests to map a page to nowhere ADR r1, PPLTrans TEQ r4, r3 ; don't actually map anything to nowhere MOVEQ pc, lr AND r4, r11, #3 ; first use PPL bits LDR r1, [r1, r4, LSL #2] ; get PPL bits and SmallPage indicator ASSERT DynAreaFlags_CPBits = 7*XCB_P :SHL: 10 ASSERT DynAreaFlags_NotCacheable = XCB_NC :SHL: 4 ASSERT DynAreaFlags_NotBufferable = XCB_NB :SHL: 4 ORR r0, r0, r1 LDR r6, =ZeroPage LDR r6, [r6, #MMU_PCBTrans] TST r11, #PageFlags_TempUncacheableBits AND r1, r11, #DynAreaFlags_NotCacheable + DynAreaFlags_NotBufferable AND r4, r11, #DynAreaFlags_CPBits ORRNE r1, r1, #XCB_TU<<4 ; if temp uncache, set TU bit ORR r1, r1, r4, LSR #10-4 LDRB r1, [r6, r1, LSR #4] ; convert to X, C and B bits for this CPU ORR r0, r0, r1 LDR r1, =L2PT ; point to level 2 page tables ;fall through to BangL2PT ;internal entry point for updating L2PT entry ; ; entry: r0 = new L2PT value, r1 -> L2PT, r3 = logical address (4k aligned), r11 = PPL ; ; exit: r0,r1,r4,r6 corrupted ; BangL2PT ; internal entry point used only by BangCamUpdate Push "lr" MOV r6, r0 TST r11, #PageFlags_Unsafe BNE BangL2PT_unsafe TST r11, #DynAreaFlags_DoublyMapped BNE BangL2PT_sledgehammer ;if doubly mapped, don't try to be clever ;In order to safely map out a cacheable page and remove it from the ;cache, we need to perform the following process: ;* Make the page uncacheable ;* Flush TLB ;* Clean+invalidate cache ;* Write new mapping (r6) ;* Flush TLB ;For uncacheable pages we can just do the last two steps ; TEQ r6, #0 ;EQ if mapping out TSTEQ r11, #DynAreaFlags_NotCacheable ;EQ if also cacheable (overcautious for temp uncache+illegal PCB combos) LDR r4, =ZeroPage BNE %FT20 ; Potentially we could just map as strongly-ordered + XN here ; But for safety just go for temp uncacheable (will retain memory type + shareability) LDR lr, [r4, #MMU_PCBTrans] GetTempUncache r0, r11, lr, r4 LDR lr, [r1, r3, LSR #10] ;get current L2PT entry LDR r4, =TempUncache_L2PTMask BIC lr, lr, r4 ;remove current attributes ORR lr, lr, r0 STR lr, [r1, r3, LSR #10] ;Make uncacheable LDR r4, =ZeroPage MOV r0, r3 ARMop MMU_ChangingUncachedEntry,,, r4 ; TLB flush MOV r0, r3 ADD r1, r3, #4096 ARMop Cache_CleanInvalidateRange,,, r4 ; Cache flush LDR r1, =L2PT 20 STR r6, [r1, r3, LSR #10] ;update L2PT entry Pull "lr" MOV r0, r3 ARMop MMU_ChangingUncachedEntry,,tailcall,r4 BangL2PT_sledgehammer ;sledgehammer is super cautious and does cache/TLB coherency on a global basis ;should only be used for awkward cases ; TEQ r6, #0 ;EQ if mapping out TSTEQ r11, #DynAreaFlags_NotCacheable ;EQ if also cacheable (overcautious for temp uncache+illegal PCB combos) ADR lr, %FT30 LDR r4, =ZeroPage ARMop MMU_Changing, EQ, tailcall, r4 ARMop MMU_ChangingUncached, NE, tailcall, r4 30 STR r6, [r1, r3, LSR #10]! ; update level 2 page table (and update pointer so we can use bank-to-bank offset TST r11, #DynAreaFlags_DoublyMapped ; if area doubly mapped STRNE r6, [r1, r9, LSR #10] ; then store entry for 2nd copy as well ADDNE r3, r3, r9 ; and point logical address back at 2nd copy ; In order to guarantee that the result of a page table write is ; visible, the ARMv6+ memory order model requires us to perform TLB ; maintenance (equivalent to the MMU_ChangingUncached ARMop) after we've ; performed the write. Performing the maintenance beforehand (as we've ; done traditionally) will work most of the time, but not always. Pull "lr" ARMop MMU_ChangingUncached,,tailcall,r4 BangL2PT_unsafe STR r6, [r1, r3, LSR #10]! ; update level 2 page table (and update pointer so we can use bank-to-bank offset TST r11, #DynAreaFlags_DoublyMapped ; if area doubly mapped STRNE r6, [r1, r9, LSR #10] ; then store entry for 2nd copy as well ADDNE r3, r3, r9 ; and point logical address back at 2nd copy Pull "pc" PPLTransL1 & (AP_Full * L1_APMult) + L1_Section ; R any W any & (AP_Read * L1_APMult) + L1_Section ; R any W sup & (AP_None * L1_APMult) + L1_Section ; R sup W sup & (AP_ROM * L1_APMult) + L1_Section ; R any W none PPLTrans & (AP_Full * L2X_APMult) + L2_ExtPage ; R any W any & (AP_Read * L2X_APMult) + L2_ExtPage ; R any W sup & (AP_None * L2X_APMult) + L2_ExtPage ; R sup W sup & (AP_ROM * L2X_APMult) + L2_ExtPage ; R any W none PPLTransLarge & (AP_Full * L2_APMult) + L2_LargePage ; R any W any & (AP_Read * L2_APMult) + L2_LargePage ; R any W sup & (AP_None * L2_APMult) + L2_LargePage ; R sup W sup & (AP_ROM * L2_APMult) + L2_LargePage ; R any W none PageShifts = 12, 13, 0, 14 ; 1 2 3 4 = 0, 0, 0, 15 ; 5 6 7 8 LTORG ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ; "VMSAv6"-specific OS_MMUControl code ; ; Make current stack page(s) temporarily uncacheable to make cache disable operations safer ; In: R0 = OS_Memory 0 flags ModifyStackCacheability Entry "r1-r2", 24 ; Make up to two pages uncacheable ADD lr, sp, #24+12 ; Get original SP STR lr, [sp, #4] ; Make current page uncacheable ASSERT (SVCStackAddress :AND: ((1<<20)-1)) = 0 ; Assume MB aligned stack TST lr, #(1<<20)-4096 ; Zero if this is the last stack page SUBNE lr, lr, #4096 STRNE lr, [sp, #12+4] ; Make next page uncacheable MOVNE r2, #2 MOV r1, sp MOVEQ r2, #1 BL MemoryConvertNoFIQCheck ; Bypass FIQ disable logic within OS_Memory (we've already claimed the FIQ vector) EXIT ; in: r0 = 0 (reason code 0, for modify control register) ; r1 = EOR mask ; r2 = AND mask ; ; new control = ((old control AND r2) EOR r1) ; ; out: r1 = old value ; r2 = new value MMUControl_ModifyControl ROUT Push "r0,r3,r4,r5" CMP r1,#0 CMPEQ r2,#&FFFFFFFF BEQ MMUC_modcon_readonly MOV r3, r1 MOV r1, #Service_ClaimFIQ SWI XOS_ServiceCall ; stop FIQs for safety MOV r1, r3 LDR r3,=ZeroPage MRS r4, CPSR CPSID if ; disable IRQs while we modify soft copy (and possibly switch caches off/on) ; We're ARMv6+, just read the real control reg and ignore the soft copy ARM_read_control lr AND r2, r2, lr EOR r2, r2, r1 MOV r1, lr LDR r5, [r3, #ProcessorFlags] TST r5, #CPUFlag_SplitCache BEQ %FT05 05 STR r2, [r3, #MMUControlSoftCopy] BIC lr, r2, r1 ; lr = bits going from 0->1 TST lr, #MMUC_C ; if cache turning on then flush cache before we do it TSTEQ lr, #MMUC_I BEQ %FT10 ARMop Cache_InvalidateAll,,,r3 10 ; If I+D currently enabled, and at least one is turning off, turn off ; HAL L2 cache TST r1, #MMUC_C TSTNE r1, #MMUC_I BEQ %FT11 TST r2, #MMUC_C TSTNE r2, #MMUC_I BNE %FT11 LDR r0, [r3, #Cache_HALDevice] TEQ r0, #0 BEQ %FT11 Push "r1-r3,r12" MOV lr, pc LDR pc, [r0, #HALDevice_Deactivate] Pull "r1-r3,r12" 11 BIC lr, r1, r2 ; lr = bits going from 1->0 TST lr, #MMUC_C ; if cache turning off then clean data cache first BEQ %FT15 ; When disabling the data cache we have the problem that modern ARMs generally ignore unexpected cache hits, so any stack usage between us disabling the cache and finishing the clean + invalidate is very unsafe ; Solve this problem by making the current pages of the SVC stack temporarily uncacheable for the duration of the dangerous bit ; (n.b. making the current stack page uncacheable has the same problems as turning off the cache globally, but OS_Memory 0 has its own workaround for that) MOV r0, #(1<<9)+(2<<14) BL ModifyStackCacheability ARMop Cache_CleanAll,,,r3 15 ARM_write_control r2 myISB ,lr ; Must be running on >=ARMv6, so perform ISB to ensure CP15 write is complete BIC lr, r1, r2 ; lr = bits going from 1->0 TST lr, #MMUC_C ; if cache turning off then flush cache afterwards TSTNE lr, #MMUC_I BEQ %FT20 LDR r3,=ZeroPage ARMop Cache_InvalidateAll,,,r3 ; Undo any stack uncaching we performed above BIC lr, r1, r2 TST lr, #MMUC_C MOVNE r0, #(1<<9)+(3<<14) BLNE ModifyStackCacheability 20 ; If either I+D was disabled, and now both are turned on, turn on HAL ; L2 cache TST r1, #MMUC_C TSTNE r1, #MMUC_I BNE %FT30 TST r2, #MMUC_C TSTNE r2, #MMUC_I BEQ %FT30 LDR r0, [r3, #Cache_HALDevice] TEQ r0, #0 BEQ %FT30 Push "r1-r3,r12" MOV lr, pc LDR pc, [r0, #HALDevice_Activate] Pull "r1-r3,r12" 30 MSR CPSR_c, r4 ; restore IRQ state MOV r3, r1 MOV r1, #Service_ReleaseFIQ SWI XOS_ServiceCall ; allow FIQs again MOV r1, r3 CLRV Pull "r0,r3,r4,r5,pc" MMUC_modcon_readonly LDR r3, =ZeroPage ; We're ARMv6+, just read the real control reg and ignore the soft copy ARM_read_control r1 STR r1, [r3, #MMUControlSoftCopy] MOV r2, r1 Pull "r0,r3,r4,r5,pc" END