LongDesc 31.1 KB
Newer Older
Jeffrey Lee's avatar
Jeffrey Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
; Copyright 1996 Acorn Computers Ltd
; Copyright 2016 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.
;

; Page table interaction - "long descriptor" format (ARMv7+ 8 bytes per entry)

; Note that to ensure page table reads & writes are seen as atomic by both the
; MMU and other CPU cores, we only use LDRD & STRD when reading/writing entries.

;----------------------------------------------------------------------------------------
; logical_to_physical
;
;       In:     r4 = logical address
;
;       Out:    r5 corrupt
;               CC => r8,r9 = physical address
;               CS => invalid logical address, r8,r9 corrupted
;
;       Convert logical address to physical address.
;
33
logical_to_physical_LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
        LDR     r9, =LL3PT
        MOV     r5, r4, LSR #12         ; r5 = logical page number
        ADD     r5, r9, r5, LSL #3      ; r5 -> L3PT entry for logical address
        MOV     r8, r5, LSR #12         ; r8 = page offset to L3PT entry for logical address
        ADD     r8, r9, r8, LSL #3      ; r8 -> L3PT entry for L3PT entry for logical address
        LDRD    r8, [r8]
        ASSERT  LL3_Page != 0
        ASSERT  LL_Fault = 0
        TST     r8, #LL3_Page           ; Check for valid (4K) page
        BEQ     meminfo_returncs
        LDRD    r8, [r5]
        TST     r8, #LL3_Page           ; Check for valid (4K) page
        BEQ     meminfo_returncs
        BFI     r8, r4, #0, #12         ; Valid, so apply offset within the page
        AND     r9, r9, #LL_HighAddr    ; And high address mask
        CLC
        MOV     pc, lr

 [ CacheablePageTables
53
MakePageTablesCacheable_LongDesc ROUT
Jeffrey Lee's avatar
Jeffrey Lee committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
        Entry   "r0,r4-r5,r8-r9"
        BL      GetPageFlagsForCacheablePageTables
        ; Update PageTable_PageFlags
        LDR     r1, =ZeroPage
        STR     r0, [r1, #PageTable_PageFlags]
        ; Adjust the logical mapping of the page tables to use the specified page flags
        LDR     r1, =LL1PT
        LDR     r2, =4096 ; Round up to page multiple
        BL      AdjustMemoryPageFlags
        LDR     r1, =LL2PT
        LDR     r2, =16*1024
        BL      AdjustMemoryPageFlags
        LDR     r1, =LL3PT
        LDR     r2, =8*1024*1024
        BL      AdjustMemoryPageFlags
        ; Update the TTBR
        LDR     r4, =LL1PT
71
        BL      logical_to_physical_LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
72 73
        MOV     r0, r8 ; Assume only 32bit address
        LDR     r1, =ZeroPage
74
        BL      SetTTBR_LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
75 76 77 78
        ; Perform a full TLB flush to make sure the new mappings are visible
        ARMop   TLB_InvalidateAll,,,r1
        EXIT

79
MakePageTablesNonCacheable_LongDesc ROUT
Jeffrey Lee's avatar
Jeffrey Lee committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
        Entry   "r0-r1,r4-r5,r8-r9"
        ; Flush the page tables from the cache, so that when we update the TTBR
        ; below we can be sure that the MMU will be seeing the current page
        ; tables
        LDR     r0, =LL1PT
        ADD     r1, r0, #4096
        LDR     r4, =ZeroPage
        ARMop   Cache_CleanRange,,,r4
        LDR     r0, =LL2PT
        ADD     r1, r0, #16*1024
        ARMop   Cache_CleanRange,,,r4
        LDR     r0, =LL3PT
        ADD     r1, r0, #8*1024*1024
        ARMop   Cache_CleanRange,,,r4
        ; Update the TTBR so the MMU performs non-cacheable accesses
        LDR     r0, =AreaFlags_PageTablesAccess :OR: DynAreaFlags_NotCacheable :OR: DynAreaFlags_NotBufferable
        STR     r0, [r4, #PageTable_PageFlags]
        LDR     r4, =LL1PT
98
        BL      logical_to_physical_LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
99 100
        MOV     r0, r8 ; Assume only 32bit address
        LDR     r1, =ZeroPage
101
        BL      SetTTBR_LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
        ; Perform a full TLB flush just in case
        ARMop   TLB_InvalidateAll,,,r1
        ; Now we can adjust the logical mapping of the page tables to be non-cacheable
        LDR     r0, [r1, #PageTable_PageFlags]
        LDR     r1, =LL1PT
        LDR     r2, =4096
        BL      AdjustMemoryPageFlags
        LDR     r1, =LL2PT
        LDR     r2, =16*1024
        BL      AdjustMemoryPageFlags
        LDR     r1, =LL3PT
        LDR     r2, =8*1024*1024
        BL      AdjustMemoryPageFlags
        EXIT
 ]

;**************************************************************************
;
;       AllocateBackingLevel2 - Allocate L3 pages for an area
;
;       Internal routine called by DynArea_Create
;
; in:   r3 = base address (will be page aligned)
;       r4 = area flags (NB if doubly mapped, then have to allocate for both halves)
;       r5 = size (of each half in doubly mapped areas)
;
; out:  If successfully allocated pages, then
;         All registers preserved
;         V=0
;       else
;         r0 -> error
;         V=1
;       endif

136
AllocateBackingLevel2_LongDesc Entry "r0-r8,r11"
Jeffrey Lee's avatar
Jeffrey Lee committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        TST     r4, #DynAreaFlags_DoublyMapped          ; if doubly mapped
        SUBNE   r3, r3, r5                              ; then area starts further back
        MOVNE   r5, r5, LSL #1                          ; and is twice the size

; NB no need to do sanity checks on addresses here, they've already been checked

; now round address range to 2M boundaries

        ADD     r5, r5, r3                              ; r5 -> end
        MOV     r0, #1 :SHL: 21
        SUB     r0, r0, #1
        BIC     r8, r3, r0                              ; round start address down (+ save for later)
        ADD     r5, r5, r0
        BIC     r5, r5, r0                              ; but round end address up

; first go through existing L3PT working out how much we need

        LDR     r7, =LL3PT
        ADD     r3, r7, r8, LSR #9                      ; r3 -> start of L3PT for area
        ADD     r5, r7, r5, LSR #9                      ; r5 -> end of L3PT for area +1

        ADD     r1, r7, r3, LSR #9                      ; r1 -> L3PT for r3
        ADD     r2, r7, r5, LSR #9                      ; r2 -> L3PT for r5

        TEQ     r1, r2                                  ; if no pages needed
        BEQ     %FT30

        MOV     r4, #0                                  ; number of backing pages needed
10
        LDRD    r6, [r1], #8                            ; get L3PT entry for L3PT
        TST     r6, #LL_TypeMask                        ; EQ if translation fault
        ADDEQ   r4, r4, #1                              ; if not there then 1 more page needed
        TEQ     r1, r2
        BNE     %BT10

; if no pages needed, then exit

        TEQ     r4, #0
        BEQ     %FT30

; now we need to claim r4 pages from the free pool, if possible; return error if not

        LDR     r1, =ZeroPage
        LDR     r6, [r1, #FreePoolDANode + DANode_PMPSize]
        SUBS    r6, r6, r4                              ; reduce free pool size by that many pages
        BCS     %FT14                                   ; if enough, skip next bit

; not enough pages in free pool currently, so try to grow it by the required amount

        Push    "r0, r1"
        MOV     r0, #ChangeDyn_FreePool
        RSB     r1, r6, #0                              ; size change we want (+ve)
        MOV     r1, r1, LSL #12
        SWI     XOS_ChangeDynamicArea
        Pull    "r0, r1"
        BVS     %FT90                                   ; didn't manage change, so report error

        MOV     r6, #0                                  ; will be no pages left in free pool after this
14
        STR     r6, [r1, #FreePoolDANode + DANode_PMPSize] ; if possible then update size

        LDR     r0, [r1, #FreePoolDANode + DANode_PMP]  ; r0 -> free pool page list
        ADD     r0, r0, r6, LSL #2                      ; r0 -> first page we're taking out of free pool

        LDR     lr, =LL2PT
        ADD     r8, lr, r8, LSR #18                     ; point r8 at start of L2 we may be updating
        LDR     r7, =LL3PT
        ADD     r1, r7, r3, LSR #9                      ; point r1 at L3PT for r3 again
        LDR     r11, =ZeroPage
        LDR     r11, [r11, #PageTable_PageFlags]        ; access privs (+CB bits)
20
        LDRD    r6, [r1], #8                            ; get L2PT entry again
        TST     r6, #LL_TypeMask                        ; if no fault
        BNE     %FT25                                   ; then skip

        Push    "r1-r2, r4"
        MOV     lr, #-1
        LDR     r2, [r0]                                ; get page number to use
        STR     lr, [r0], #4                            ; remove from PMP
        Push    "r0"
217
        BL      BangCamUpdate_LongDesc                  ; Map in to L3PT access window
Jeffrey Lee's avatar
Jeffrey Lee committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281

; now that the page is mapped in we can zero its contents (=> cause translation fault for area initially)
; L2PT won't know about the page yet, so mapping it in with garbage initially shouldn't cause any issues

        ADD     r0, r3, #4096
        MOV     r1, #0
        MOV     r2, #0
        MOV     r4, #0
        MOV     r6, #0
15
        STMDB   r0!, {r1,r2,r4,r6}                      ; store data
        TEQ     r0, r3
        BNE     %BT15

        ; Make sure the page is seen to be clear before we update L2PT to make
        ; it visible to the MMU
        PageTableSync

        Pull    "r0-r2, r4"

        LDR     lr, =ZeroPage
        LDR     r6, [lr, #LxPTUsed]
        ADD     r6, r6, #4096
        STR     r6, [lr, #LxPTUsed]

; now update 1 entry in L2PT (corresponding to 2M of address space which is covered by the 4K of L3)
; and point it at the physical page we've just allocated (r1!-8 will already hold physical address+bits now!)

        LDRD    r6, [r1, #-8]                           ; r6,r7 = L3PT entry describing our logical mapping of the new page
        BFC     r6, #0, #LL_LowAddr_Start               ; Low phys addr
        AND     r7, r7, #LL_HighAddr                    ; High phys addr
        ORR     r6, r6, #LL12_Table                     ; It's a table ptr
        STRD    r6, [r8]                                ; Store as L2PT entry
25
        ADD     r3, r3, #4096                           ; advance L3PT logical address
        ADD     r8, r8, #8                              ; move onto L2 for next 2M

        TEQ     r1, r2
        BNE     %BT20
        PageTableSync
30
        CLRV
        EXIT

; Come here if not enough space in free pool to allocate level3

90
        ADRL    r0, ErrorBlock_CantAllocateLevel2
  [ International
        BL      TranslateError
  |
        SETV
  ]
        STR     r0, [sp]
        EXIT

;**************************************************************************
;
;       UpdateL1PTForPageReplacement
;
; Updates L2PT to point to the right place, if a physical L3PT page has been
; replaced with a substitute.
;
; In: r0 = log addr of page being replaced
282
;     r1,r2 = phys addr of replacement page
Jeffrey Lee's avatar
Jeffrey Lee committed
283 284 285
;
; Out: r0-r4, r7-r12 can be corrupted
;
286
UpdateL1PTForPageReplacement_LongDesc ROUT
Jeffrey Lee's avatar
Jeffrey Lee committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
        LDR     r3, =LL3PT
        SUBS    r0, r0, r3
        MOVCC   pc, lr                          ; address is below L3PT
        CMP     r0, #8*1024*1024
        MOVCS   pc, lr                          ; address is above L3PT

        LDR     r3, =LL2PT
        ADD     r0, r3, r0, LSR #(12-3)         ; address in L2 of entry to update
        LDRD    r8, [r0]                        ; load L2PT entry
        MOV     r1, r1, LSR #LL_LowAddr_Start
        BFI     r8, r1, #LL_LowAddr_Start, #LL_LowAddr_Size ; Update low phys addr
        ASSERT  LL_HighAddr_Start = 0
        BFI     r9, r2, #0, #LL_HighAddr_Size   ; Update high phys addr
        STRD    r8, [r0]

        ; 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.
        LDR     r3, =ZeroPage
        ARMop   MMU_ChangingUncached,,tailcall,r3

;
; ----------------------------------------------------------------------------------
;
;convert page number in $pnum to L3PT entry (physical address+protection bits),
;using cached PhysRamTable entries for speed
;
;entry: $ptable -> PhysRamTable, $pbits,$pbits2 = protection bits
;       $cache0, $cache1, $cache2 = PhysRamTable cache
;exit:  $cache0, $cache1, $cache2 updated
;

        MACRO
        PageNumToL3PT $pnum,$pnum2,$ptable,$cache0,$cache1,$cache2,$pbits,$pbits2
323
        MOV     $pnum2,$pbits2        ; Save $pbits2 so it can be used as cache func in/out
Jeffrey Lee's avatar
Jeffrey Lee committed
324 325 326
        SUB     $pbits2,$pnum,$cache0 ; no. pages into block
        CMP     $pbits2,$cache2
        BLHS    PageNumToL3PTCache_$ptable._$cache0._$cache1._$cache2._$pbits2
327
        ADD     $pnum,$cache1,$pbits2 ; physical address of page (in page units)
Jeffrey Lee's avatar
Jeffrey Lee committed
328
        MOV     $pbits2,$pnum2
329 330
        ORR     $pnum2,$pnum2,$pnum,LSR #20 ; High attr + high addr
        ORR     $pnum,$pbits,$pnum,LSL #12 ; Low attr + low addr
Jeffrey Lee's avatar
Jeffrey Lee committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
        MEND

        MACRO
        PageNumToL3PTInit $ptable,$cache0,$cache1,$cache2
        ASSERT  $cache2 > $cache1
        LDR     $ptable,=ZeroPage+PhysRamTable
        MOV     $cache0,#0
        LDMIA   $ptable,{$cache1,$cache2}
        MOV     $cache2,$cache2,LSR #12
        MEND

PageNumToL3PTCache_r4_r5_r6_r7_r12 ROUT
        Entry   "r4"
        ADD     r12,r12,r5 ; Restore page number
        MOV     r5,#0
10
        LDMIA   r4!,{r6,r7} ; Get PhysRamTable entry
        MOV     r7,r7,LSR #12
        CMP     r12,r7
        SUBHS   r12,r12,r7
        ADDHS   r5,r5,r7
        BHS     %BT10
        EXIT    ; r5-r7 = cache entry, r12 = offset into entry

; ----------------------------------------------------------------------------------
;
;AMB_movepagesin_L2PT
;
;updates L3PT for new logical page positions, does not update CAM
;
; entry:
;       r3  =  new logical address of 1st page
;       r8  =  number of pages
;       r9  =  page flags
;       r10 -> page list
;
367
AMB_movepagesin_L2PT_LongDesc ROUT
Jeffrey Lee's avatar
Jeffrey Lee committed
368 369 370
        Entry   "r0-r12"

        MOV     r0, #0
371
        GetPTE  r11, 4K, r0, r9, LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

        PageNumToL3PTInit r4,r5,r6,r7

        LDR     r9,=LL3PT
        ADD     r9,r9,r3,LSR #(Log2PageSize-3) ;r9 -> L3PT for 1st new logical page

        CMP     r8,#2
        BLT     %FT20
10
        LDMIA   r10!,{r0,r2}         ;next 2 page numbers
        PageNumToL3PT r0,r1,r4,r5,r6,r7,r11,r12
        PageNumToL3PT r2,r3,r4,r5,r6,r7,r11,r12
        STRD    r0,[r9],#16          ;write L3PT entries
        STRD    r2,[r9,#-8]
        SUB     r8,r8,#2
        CMP     r8,#2
        BGE     %BT10
20
        CMP     r8,#0
        BEQ     %FT35
        LDR     r0,[r10],#4
        PageNumToL3PT r0,r1,r4,r5,r6,r7,r11,r12
        STRD    r0,[r9]
35
        PageTableSync
        EXIT

; ----------------------------------------------------------------------------------
;
;AMB_movecacheablepagesout_L2PT
;
;updates L3PT for old logical page positions, does not update CAM
;
; entry:
;       r3  =  old page flags
;       r4  =  old logical address of 1st page
;       r8  =  number of pages
;
410
AMB_movecacheablepagesout_L2PT_LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
411 412 413 414 415 416
        Entry   "r0-r8"

        ; Calculate L3PT flags needed to make the pages uncacheable
        ; Assume all pages will have identical flags (or at least close enough)
        LDR     lr,=ZeroPage
        LDR     lr,[lr, #MMU_PCBTrans]
417
        GetTempUncache_LongDesc r0, r3, lr, r1
Jeffrey Lee's avatar
Jeffrey Lee committed
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
        LDR     r1, =TempUncache_L3PTMask

        LDR     lr,=LL3PT
        ADD     lr,lr,r4,LSR #(Log2PageSize-3)    ;lr -> L3PT 1st entry

        CMP     r8,#2
        BLT     %FT20
10
        LDRD    r2,[lr]
        LDRD    r4,[lr,#8]
        BIC     r2,r2,r1
        BIC     r4,r4,r1
        ORR     r2,r2,r0
        ORR     r4,r4,r0
        STRD    r2,[lr],#16
        STRD    r4,[lr,#-8]
        SUB     r8,r8,#2
        CMP     r8,#2
        BGE     %BT10
20
        CMP     r8,#0
        BEQ     %FT35
        LDRD    r2,[lr]
        BIC     r2,r2,r1
        ORR     r2,r2,r0
        STRD    r2,[lr]
35
        FRAMLDR r0,,r4                           ;address of 1st page
        FRAMLDR r1,,r8                           ;number of pages
        LDR     r3,=ZeroPage
        ARMop   MMU_ChangingEntries,,,r3
        FRAMLDR r4
        FRAMLDR r8
        B       %FT55 ; -> moveuncacheablepagesout_L2PT (avoid pop+push of large stack frame)

; ----------------------------------------------------------------------------------
;
;AMB_moveuncacheablepagesout_L2PT
;
;updates L3PT for old logical page positions, does not update CAM
;
; entry:
;       r4  =  old logical address of 1st page
;       r8  =  number of pages
;
463
AMB_moveuncacheablepagesout_L2PT_LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
        ALTENTRY
55      ; Enter here from movecacheablepagesout
        LDR     lr,=LL3PT
        ADD     lr,lr,r4,LSR #(Log2PageSize-3)    ;lr -> L2PT 1st entry

        MOV     r0,#0                             ;0 means translation fault
        MOV     r1,#0

        CMP     r8,#8
        BLT     %FT70
60
        STRD    r0,[lr],#8*8
        STRD    r0,[lr,#-7*8]
        STRD    r0,[lr,#-6*8]
        STRD    r0,[lr,#-5*8]
        STRD    r0,[lr,#-4*8]
        STRD    r0,[lr,#-3*8]
        STRD    r0,[lr,#-2*8]
        STRD    r0,[lr,#-1*8]
        SUB     r8,r8,#8
        CMP     r8,#8
        BGE     %BT60
70
        CMP     r8,#0
        BEQ     %FT85
80
        STRD    r0,[lr],#8
        SUBS    r8,r8,#1
        BNE     %BT80
85
        FRAMLDR r0,,r4                           ;address of 1st page
        FRAMLDR r1,,r8                           ;number of pages
        LDR     r3,=ZeroPage
        ARMop   MMU_ChangingUncachedEntries,,,r3 ;no cache worries, hoorah
        EXIT

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
 [ AMB_LazyMapIn
AMB_SetPageFlags_LongDesc
        Entry
; Calculate default page flags
        MOV     R0, #0
        MOV     R1, #0
        GetPTE  R0, 4K, R0, R1, LongDesc
        STR     R0,AMBPageFlags
        Exit
 ]

; Scan a sparsely mapped dynamic area for the next mapped/unmapped section
;
; In:
;   r3 = end address
;   r4 = 0 to release, 1 to claim
;   r8 = start address
; Out:
;   r1 = size of found area
;   r8 = start address of found area, or >= r3 if nothing found
;   r5-r7,r9 corrupt
ScanSparse_LongDesc ROUT
        LDR     r5,=LL3PT
        ADD     r5,r5,r8,LSR #9      ;r5 -> L3PT for base (assumes 4k page)
;
;look for next fragment of region that needs to have mapping change
20
        CMP     r8,r3
        MOVHS   pc,lr                ;done
        LDRD    r6,[r5],#8           ;pick-up next L3PT entry
        CMP     r4,#0                ;if operation is a release...
        CMPEQ   r6,#0                ;...and L3PT entry is 0 (not mapped)...
        ADDEQ   r8,r8,#&1000         ;...then skip page (is ok)
        BEQ     %BT20
        CMP     r4,#0                ;if operation is a claim (not 0)...
        CMPNE   r6,#0                ;...and L3PT entry is non-0 (mapped)...
        ADDNE   r8,r8,#&1000         ;...then skip page (is ok)
        BNE     %BT20
        MOV     r1,#&1000            ;else we need to do a change (1 page so far)
30
        ADD     r9,r8,r1
        CMP     r9,r3
        MOVHS   pc,lr
        LDRD    r6,[r5],#8           ;pick-up next L3PT entry
        CMP     r4,#1                ;if operation is a release (not 1)...
        CMPNE   r6,#0                ;...and L3PT entry is non-0 (mapped)...
        ADDNE   r1,r1,#&1000         ;...then count page as needing change
        BNE     %BT30
        CMP     r4,#1                ;if operation is a claim...
        CMPEQ   r6,#0                ;...and L3PT entry is 0 (not mapped)...
        ADDEQ   r1,r1,#&1000         ;...then count page as needing change
        BEQ     %BT30
        MOV     pc, lr

; Set a page as temporarily uncacheable.
; Doesn't modify the CAM or perform any cache/TLB maintenance.
;
; In:
;   r0 = address
;   r6 = page flags for r0
;   r8 = page table flags from last call (undefined for first call)
;   r9 = page flags from last call (-1 for first call)
; Out:
;   r8, r9 updated
;   r1, r4, r5 corrupt
MakeTempUncache_LongDesc ROUT
        ; Calculate required page flags
        CMP     r9, r6
        BEQ     %FT06
        LDR     r1, =ZeroPage
        MOV     r9, r6
        LDR     r1, [r1, #MMU_PCBTrans]
        GetTempUncache_LongDesc r8, r9, r1, r4
06
        LDR     r1, =LL3PT
        ; Bypass BangCAM and update L2PT directly (avoids CAM gaining any unwanted temp uncacheability flags)
        ADD     r1, r1, r0, LSR #9
        LDRD    r4, [r1]
        BIC     r4, r4, #TempUncache_L3PTMask
        ORR     r4, r4, r8
        STRD    r4, [r1]
        MOV     pc, lr

; void *AccessPhysicalAddress(unsigned int flags, uint64_t phys, void **oldp)
;
; APCS calling convention.
;
; flags: RISC OS page flags
; phys: Physical address to access
; oldp: Pointer to word to store old state (or NULL)
;
; On exit: Returns logical address corresponding to 'phys'.
;
; Arranges for the physical address 'phys' to be (temporarily) mapped into
; logical memory. In fact, at least the whole megabyte containing 'phys' is
; mapped in. All mappings use the same shared logical window; the current state
; of the window will be returned in 'oldp', to allow it to be restored (via
; ReleasePhysicalAddress) once you've finised with it.
;
; Note: No cache maintenance performed. Assumption is that mappings will be
; non-cacheable.
AccessPhysicalAddress_LongDesc ROUT
        ; Check physical address is valid on current CPU
        LDR     ip, =ZeroPage
        Push    "a1,v3,lr"
        LDR     v3, [ip, #PhysIllegalMask]
        TST     a3, v3
        BNE     %FT90
        UBFX    v3, a2, #0, #21                         ; v3 = offset for result
        ; Use Get2MPTE to convert into page table entry
        MOV     ip, a2
        BFI     ip, a3, #0, #21                         ; Get2MPTE packs the high address bits into the low bits
        GetPTE  a1, 2M, ip, a1, LongDesc
        ; Force XN (easier to do afterwards since PPL mapping is non-trivial)
        ORR     a2, a2, #LL_Page_HighAttr_XN
615 616 617 618 619 620 621
 [ EmulateAP1
        ; Don't allow AP1 emulation (usermode readonly); there's no XN+SW0
        ; format defined in PPLTrans, so to ensure we can map from the page
        ; table entry flags back to a valid AP number, we must clear SW0 and
        ; lose the usermode access.
        BIC     a2, a2, #LL_Page_HighAttr_SW0
 ]
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
        MOV     lr, a4
        LDR     ip, =LL2PT + (PhysicalAccess:SHR:18)    ; ip -> L2PT entry
        LDRD    a3, [ip]                                ; Get old entry
        STRD    a1, [ip]                                ; Put new entry
        ; Compact old entry into a single word if necessary
        CMP     lr, #0
        BFINE   a3, a4, #12, #8
        STRNE   a3, [lr]
        LDR     a1, =PhysicalAccess
        ORR     a1, a1, v3
        STR     a1, [sp]
        ARMop   MMU_ChangingUncached                    ; sufficient, cause not cacheable
        Pull    "a1,v3,pc"

90      ; Invalid physical address
        ADD     sp, sp, #1*4
        MOV     a1, #0
        Pull    "v3,pc"

; void ReleasePhysicalAddress(void *old)
;
; APCS calling convention.
;
; Call with the 'oldp' value from a previous AccessPhysicalAddress call to
; restore previous physical access window state.
ReleasePhysicalAddress_LongDesc
        LDR     ip, =LL2PT + (PhysicalAccess:SHR:18)    ; ip -> L2PT entry
        ; The 8 byte page table entry is packed into the 4 byte token as folllows:
        ; * Bits 0-11 give the low 12 bits of the page table entry (page type + low attributes)
        ; * Bits 21-31 give bits 21-31 of the page table entry (low PA)
        ; * Bits 12-20 give bits 32-39 of the page table entry (high PA)
        ; * Bit 20 is spare  (kept at zero)
        ; * The upper attributes are fixed (always XN)
        UBFX    a2, a1, #12, #8                         ; Get high word
        BICS    a1, a1, #&FF000                         ; Get low word
        ORRNE   a2, a2, #LL_Page_HighAttr_XN            ; Always XN
        STRD    a1, [ip]
        ARMop   MMU_ChangingUncached,,tailcall          ; sufficient, cause not cacheable

;
; In:  a1 = flags  (L1_B,L1_C,L1_TEX)
;           bit 20 set if doubly mapped
;           bit 21 set if L1_AP specified (else default to AP_None)
;      a2 = physical address
;      a3 = size
; Out: a1 = assigned logical address, or 0 if failed (no room)
;
RISCOS_MapInIO_LongDesc ROUT
        MOV     a4, a3
        MOV     a3, #0
        ; drop through...
;
; In:  a1 = flags  (L1_B,L1_C,L1_TEX)
;           bit 20 set if doubly mapped
;           bit 21 set if L1_AP specified (else default to AP_None)
;      a2,a3 = physical address
;      a4 = size
; Out: a1 = assigned logical address, or 0 if failed (no room)
;
RISCOS_MapInIO64_LongDesc ROUT

; Will detect and return I/O space already mapped appropriately, or map and return new space
; For simplicity and speed of search, works on a section (1Mb) granularity
;

        ASSERT  L1_B = 1:SHL:2
        ASSERT  L1_C = 1:SHL:3
 [ MEMM_Type = "VMSAv6"
        ASSERT  L1_AP = 2_100011 :SHL: 10
        ASSERT  L1_TEX = 2_111 :SHL: 12
 |
        ASSERT  L1_AP = 3:SHL:10
        ASSERT  L1_TEX = 2_1111 :SHL: 12
 ]

        ! 0, "LongDescTODO Decide how to handle this"
        AND     a1, a1, #MapInFlag_DoublyMapped
        ORR     a1, a1, #LL12_Block+LLAttr_SO           ; privileged device mapping
        ORR     a1, a1, #LL_Page_LowAttr_AF+LL_Page_LowAttr_SH1+LL_Page_LowAttr_SH0
RISCOS_MapInIO_LowAttr_LongDesc ; a1 bits 0-11 = low attributes, bits 20+ = our extra flags
        Entry   "a2,v1-v8"
        LDR     ip, =ZeroPage
        SUB     a4, a4, #1                              ; reduce by 1 so end physical address is inclusive
        ADDS    v1, a2, a4
        ADC     v2, a3, #0                              ; v1,v2 = end physical address
        LDR     v3, [ip, #PhysIllegalMask]
        TST     v2, v3
        MOVNE   a1, #0
        BNE     %FT90                                   ; can't map in physical addresses in this range

        MOV     v4, a2, LSR #21
        ORR     v4, v4, a3, LSL #11                     ; v4 = physical start 2MB to map
        MOV     v5, v1, LSR #21
        ORR     v5, v5, v2, LSL #11
        ADD     v5, v5, #1                              ; v5 = exclusive physical end 2MB to map
        ANDS    v8, a1, #MapInFlag_DoublyMapped
        SUBNE   v8, v5, v4                              ; v8 = offset of second mapping (in 2MB) or 0
        UBFX    a1, a1, #0, #LL_LowAttr_Start+LL_LowAttr_Size ; mask out our extra flags
        ORR     a1, a1, v4, LSL #21
        MOV     a2, v4, LSR #11
        ORR     a2, a2, #LL_Page_HighAttr_XN            ; a1,a2 = first PT entry to match
723 724 725 726 727 728 729
 [ EmulateAP1
        ; Don't allow AP1 emulation (usermode readonly); there's no XN+SW0
        ; format defined in PPLTrans, so to ensure we can map from the page
        ; table entry flags back to a valid AP number, we must clear SW0 and
        ; lose the usermode access.
        BIC     a2, a2, #LL_Page_HighAttr_SW0
 ]
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
        LDR     v7, [ip, #IOAllocPtr]
        MOV     v7, v7, LSR #18                         ; v7 = logical 2MB*8 that we're checking for a match
        LDR     v1, =LL2PT
        LDR     v2, [ip, #IOAllocTop]
        MOV     v2, v2, LSR #18                         ; v2 = last logical 2MB*8 to check (exclusive)
10
        ADD     ip, v7, v8, LSL #3                      ; logical 2MB*8 of base mapping or second mapping if there is one
        CMP     ip, v2
        BHS     %FT50                                   ; run out of logical addresses to check
        LDRD    a3, [v1, v7]                            ; check only or first entry
        TEQ     a1, a3
        TEQEQ   a2, a4
        LDREQD  a3, [v1, ip]                            ; check only or second entry
        TEQEQ   a1, a3
        TEQEQ   a2, a4
        ADD     v7, v7, #8                              ; next logical 2MB to check
        BNE     %BT10

        ; Found start of requested IO already mapped, and with required flags
        ; Now check that the remaining secions are all there too
        MOV     v6, v4                                  ; current 2MB being checked
        MOV     v3, v7, LSL #18
        SUB     v3, v3, #1:SHL:21                       ; start logical address
20
        ADD     v6, v6, #1                              ; next physical 2MB
        ADDS    a1, a1, #1:SHL:21                       ; next PTE
        ADC     a2, a2, #0
        ADD     ip, v7, v8, LSL #3
        CMP     v6, v5
        BHS     %FT80
        CMP     ip, v2
        BHS     %FT45                                   ; run out of logical addresses to check
        LDRD    a3, [v1, v7]                            ; check only or first entry
        TEQ     a1, a3
        TEQEQ   a2, a4
        LDREQD  a3, [v1, ip]                            ; check only or second entry
        TEQEQ   a1, a3
        TEQEQ   a2, a4
        ADDEQ   v7, v7, #8                              ; next logical 2MB*8
        BEQ     %BT20                                   ; good so far, try next entry
        ; Mismatch, rewind PTE and continue outer loop
        SUB     v6, v6, v4
        SUBS    a1, a1, v6, LSL #21
        SBC     a2, a2, v6, LSR #11    
        B       %BT10

45
        ; Rewind PTE
        SUB     v6, v6, v4
        SUBS    a1, a1, v6, LSL #21
        SBC     a2, a2, v6, LSR #11
50      ; Request not currently mapped, only partially mapped, or mapped with wrong flags
        LDR     ip, =ZeroPage
        LDR     a3, [ip, #IOAllocPtr]
        MOV     a3, a3, LSR #21
        SUB     v7, v5, v4                              ; v7 = number of 2MB required
        SUB     a3, a3, v7
        MOV     v3, a3, LSL #21
        LDR     v6, [ip, #IOAllocLimit]
        CMP     v3, v6                                  ; run out of room to allocate IO?
        MOVLS   a1, #0                                  ; LS is to match previous version of the code - perhaps should be LO?
        BLS     %FT90
        STR     v3, [ip, #IOAllocPtr]
        MOV     a3, a3, LSL #3
60
        ADD     ip, a3, v8, LSL #3
        STRD    a1, [v1, a3]                            ; write only or first entry
        STRD    a1, [v1, ip]                            ; write only or second entry
        ADDS    a1, a1, #1:SHL:21
        ADC     a2, a2, #0
        ADD     a3, a3, #8
        SUBS    v7, v7, #1
        BNE     %BT60

        PageTableSync                                   ; corrupts a1
80
        LDR     a2, [sp]                                ; retrieve original physical address from stack
        BFI     v3, a2, #0, #21                         ; apply sub-2MB offset
        MOV     a1, v3
90
        EXIT

; Helper for MapIOpermanent / MapIO64permanent
;
; In:
;   r0 = MapIO flags
;   r1,r2 = phys addr
;   r3 = size
;   r12 = page flags
; Out:
;   r0 = assigned log addr, or 0 if failed (no room)
MapIO_Helper_LongDesc
        ; Convert DA flags to page table entry
        Push    "r0,r1"
        MOV     r0, #0
        GetPTE  r0, 2M, r0, r12, LongDesc
        UBFX    r0, r0, #0, #LL_LowAttr_Start+LL_LowAttr_Size ; Only keep the low attribtues (only high attribute is XN/PXN, which is always forced)
        Pull    "r1"
        ORR     r0, r0, r1              ; Add in the extra flags
        Pull    "r1"
        B       RISCOS_MapInIO_LowAttr_LongDesc

Jeffrey Lee's avatar
Jeffrey Lee committed
832 833 834
        LTORG

        END