ShortDesc 34.5 KB
Newer Older
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
; 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 - "short descriptor" format (ARMv3+ 4 bytes per entry)

;----------------------------------------------------------------------------------------
; 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.
;
30
logical_to_physical_ShortDesc
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
        LDR     r5, =L2PT
        MOV     r9, r4, LSR #12         ; r9 = logical page number
        ADD     r9, r5, r9, LSL #2      ; r9 -> L2PT entry for logical address
        MOV     r8, r9, LSR #12         ; r8 = page offset to L2PT entry for logical address
        LDR     r8, [r5, r8, LSL #2]    ; r8 = L2PT entry for L2PT entry for logical address
      [ MEMM_Type = "ARM600"
        ASSERT  ((L2_SmallPage :OR: L2_ExtPage) :AND: 2) <> 0
        ASSERT  (L2_LargePage :AND: 2) = 0
      |
        ASSERT  L2_SmallPage = 2
        ASSERT  L2_XN = 1               ; Because XN is bit 0, bit 1 is the only bit we can check when looking for small pages
      ]
        TST     r8, #2                  ; Check for valid (4K) page.
        BEQ     meminfo_returncs

        LDR     r8, [r9]                ; r8 = L2PT entry for logical address
        TST     r8, #2                  ; Check for valid (4K) page.
        BEQ     meminfo_returncs

      [ NoARMT2
        LDR     r9, =&FFF               ; Valid so
        BIC     r8, r8, r9              ;   mask off bits 0-11,
        AND     r9, r4, r9              ;   get page offset from logical page
        ORR     r8, r8, r9              ;   combine with physical page address.
      |
        BFI     r8, r4, #0, #12         ; Valid, so apply offset within the page
      ]
        MOV     r9, #0                  ; 4K pages are always in the low 4GB
        CLC
        MOV     pc, lr

 [ CacheablePageTables
63
MakePageTablesCacheable_ShortDesc ROUT
64 65 66 67 68 69 70 71 72 73 74 75 76 77
        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, =L1PT
        LDR     r2, =16*1024
        BL      AdjustMemoryPageFlags
        LDR     r1, =L2PT
        LDR     r2, =4*1024*1024
        BL      AdjustMemoryPageFlags
        ; Update the TTBR
        LDR     r4, =L1PT
78
        BL      logical_to_physical_ShortDesc
79 80
        MOV     r0, r8 ; Assume only 32bit address
        LDR     r1, =ZeroPage
81
        BL      SetTTBR_ShortDesc
82 83 84 85
        ; Perform a full TLB flush to make sure the new mappings are visible
        ARMop   TLB_InvalidateAll,,,r1
        EXIT

86
MakePageTablesNonCacheable_ShortDesc ROUT
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
        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, =L1PT
        ADD     r1, r0, #16*1024
        LDR     r4, =ZeroPage
        ARMop   Cache_CleanRange,,,r4
        LDR     r0, =L2PT
        ADD     r1, r0, #4*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, =L1PT
102
        BL      logical_to_physical_ShortDesc
103 104
        MOV     r0, r8 ; Assume only 32bit address
        LDR     r1, =ZeroPage
105
        BL      SetTTBR_ShortDesc
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 136
        ; 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, =L1PT
        LDR     r2, =16*1024
        BL      AdjustMemoryPageFlags
        LDR     r1, =L2PT
        LDR     r2, =4*1024*1024
        BL      AdjustMemoryPageFlags
        EXIT
 ]

;**************************************************************************
;
;       AllocateBackingLevel2 - Allocate L2 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

137
AllocateBackingLevel2_ShortDesc Entry "r0-r8,r11"
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 4M boundaries

        ADD     r5, r5, r3                              ; r5 -> end
        MOV     r0, #1 :SHL: 22
        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 L2PT working out how much we need

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

        ADD     r1, r7, r3, LSR #10                     ; r1 -> L2PT for r3
        ADD     r2, r7, r5, LSR #10                     ; r2 -> L2PT for r5

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

        MOV     r4, #0                                  ; number of backing pages needed
10
        LDR     r6, [r1], #4                            ; get L2PT entry for L2PT
        TST     r6, #3                                  ; 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, =L1PT
        ADD     r8, lr, r8, LSR #18                     ; point r8 at start of L1 we may be updating
        ADD     r1, r7, r3, LSR #10                     ; point r1 at L2PT for r3 again
        LDR     r11, =ZeroPage
        LDR     r11, [r11, #PageTable_PageFlags]        ; access privs (+CB bits)
20
        LDR     r6, [r1], #4                            ; get L2PT entry again
        TST     r6, #3                                  ; 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_ShortDesc                 ; Map in to L2PT access window
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238

; now that the page is mapped in we can zero its contents (=> cause translation fault for area initially)
; L1PT 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 L1PT to make
        ; it visible to the MMU
        PageTableSync

        Pull    "r0-r2, r4"

        LDR     lr, =ZeroPage
Jeffrey Lee's avatar
Jeffrey Lee committed
239
        LDR     r6, [lr, #LxPTUsed]
240
        ADD     r6, r6, #4096
Jeffrey Lee's avatar
Jeffrey Lee committed
241
        STR     r6, [lr, #LxPTUsed]
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 282 283 284 285 286 287 288 289 290 291

; now update 4 words in L1PT (corresponding to 4M of address space which is covered by the 4K of L2)
; and point them at the physical page we've just allocated (r1!-4 will already hold physical address+bits now!)

        LDR     r6, [r1, #-4]                           ; r6 = physical address for L2 page + other L2 bits
        MOV     r6, r6, LSR #12                         ; r6 = phys.addr >> 12
 [ MEMM_Type = "VMSAv6"
        LDR     lr, =L1_Page
 |
        LDR     lr, =L1_Page + L1_U                     ; form other bits to put in L1
 ]
        ORR     lr, lr, r6, LSL #12                     ; complete L1 entry
        STR     lr, [r8, #0]                            ; store entry for 1st MB
        ADD     lr, lr, #1024                           ; advance L2 pointer
        STR     lr, [r8, #4]                            ; store entry for 2nd MB
        ADD     lr, lr, #1024                           ; advance L2 pointer
        STR     lr, [r8, #8]                            ; store entry for 3rd MB
        ADD     lr, lr, #1024                           ; advance L2 pointer
        STR     lr, [r8, #12]                           ; store entry for 4th MB
25
        ADD     r3, r3, #4096                           ; advance L2PT logical address
        ADD     r8, r8, #16                             ; move onto L1 for next 4M

        TEQ     r1, r2
        BNE     %BT20
        PageTableSync
30
        CLRV
        EXIT

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

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

;**************************************************************************
;
;       UpdateL1PTForPageReplacement
;
; Updates L1PT to point to the right place, if a physical L2PT page has been
; replaced with a substitute.
;
; In: r0 = log addr of page being replaced
292
;     r1,r2 = phys addr of replacement page
293 294 295
;
; Out: r0-r4, r7-r12 can be corrupted
;
296
UpdateL1PTForPageReplacement_ShortDesc ROUT
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 323 324 325 326 327 328 329 330 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
        LDR     r2, =L2PT
        SUBS    r0, r0, r2
        MOVCC   pc, lr                          ; address is below L2PT
        CMP     r0, #4*1024*1024
        MOVCS   pc, lr                          ; address is above L2PT

        LDR     r2, =L1PT
        ADD     r2, r2, r0, LSR #(12-4)         ; address in L1 of 4 consecutive words to update
        LDR     r3, [r2]                        ; load 1st L1PT entry
        MOV     r3, r3, LSL #(31-9)             ; junk old phys addr
        ORR     r3, r1, r3, LSR #(31-9)         ; merge in new phys addr
        STR     r3, [r2], #4
        ADD     r3, r3, #&400                   ; advance by 1K for each L1PT word
        STR     r3, [r2], #4
        ADD     r3, r3, #&400
        STR     r3, [r2], #4
        ADD     r3, r3, #&400
        STR     r3, [r2], #4
    [ MEMM_Type = "VMSAv6"
        ; 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
    |
        ; With older architectures there shouldn't be any TLB maintenance
        ; required. But we do potentially need to drain the write buffer to make
        ; sure the CPU actually sees the changes.
      [ CacheablePageTables
        LDR     r3, =ZeroPage
        ARMop   DSB_ReadWrite,,tailcall,r3        
      |
        MOV     pc, lr
      ]
    ]

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

        MACRO
        PageNumToL2PT $pnum,$ptable,$cache0,$cache1,$cache2,$pbits,$temp
        SUB     $temp,$pnum,$cache0 ; no. pages into block
        CMP     $temp,$cache2
        BLHS    PageNumToL2PTCache_$ptable._$cache0._$cache1._$cache2._$temp
        ADD     $pnum,$cache1,$temp,LSL #Log2PageSize ; physical address of page
        ORR     $pnum,$pbits,$pnum ; munge in protection bits
        MEND

        MACRO
        PageNumToL2PTInit $ptable,$cache0,$cache1,$cache2
        ASSERT  $cache2 > $cache1
        LDR     $ptable,=ZeroPage+PhysRamTable
        MOV     $cache0,#0
        LDMIA   $ptable,{$cache1,$cache2}
362
        MOV     $cache1,$cache1,LSL #12
363 364 365 366 367 368 369 370 371 372 373 374 375 376
        MOV     $cache2,$cache2,LSR #12
        MEND

PageNumToL2PTCache_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
377
        MOV     r6,r6,LSL #12
378 379 380 381 382 383 384 385 386 387 388 389 390 391
        EXIT    ; r5-r7 = cache entry, r12 = offset into entry

; ----------------------------------------------------------------------------------
;
;AMB_movepagesin_L2PT
;
;updates L2PT 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
;
392
AMB_movepagesin_L2PT_ShortDesc ROUT
393 394 395
        Entry   "r0-r12"

        MOV     r0, #0
396
        GetPTE  r11, 4K, r0, r9, ShortDesc
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

        PageNumToL2PTInit r4,r5,r6,r7

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

        CMP     r8,#4
        BLT     %FT20
10
        LDMIA   r10!,{r0-r3}         ;next 4 page numbers
        PageNumToL2PT r0,r4,r5,r6,r7,r11,r12
        PageNumToL2PT r1,r4,r5,r6,r7,r11,r12
        PageNumToL2PT r2,r4,r5,r6,r7,r11,r12
        PageNumToL2PT r3,r4,r5,r6,r7,r11,r12
        STMIA   r9!,{r0-r3}          ;write 4 L2PT entries
        SUB     r8,r8,#4
        CMP     r8,#4
        BGE     %BT10
20
        CMP     r8,#0
        BEQ     %FT35
30
        LDR     r0,[r10],#4
        PageNumToL2PT r0,r4,r5,r6,r7,r11,r12
        STR     r0,[r9],#4
        SUBS    r8,r8,#1
        BNE     %BT30
35
        PageTableSync
        EXIT

; ----------------------------------------------------------------------------------
;
;AMB_movecacheablepagesout_L2PT
;
;updates L2PT 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
;
439
AMB_movecacheablepagesout_L2PT_ShortDesc
440 441 442 443 444 445
        Entry   "r0-r8"

        ; Calculate L2PT 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]
446
        GetTempUncache_ShortDesc r0, r3, lr, r1
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 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
        LDR     r1, =TempUncache_L2PTMask

        LDR     lr,=L2PT
        ADD     lr,lr,r4,LSR #(Log2PageSize-2)    ;lr -> L2PT 1st entry

        CMP     r8,#4
        BLT     %FT20
10
        LDMIA   lr,{r2-r5}
        BIC     r2,r2,r1
        BIC     r3,r3,r1
        BIC     r4,r4,r1
        BIC     r5,r5,r1
        ORR     r2,r2,r0
        ORR     r3,r3,r0
        ORR     r4,r4,r0
        ORR     r5,r5,r0
        STMIA   lr!,{r2-r5}
        SUB     r8,r8,#4
        CMP     r8,#4
        BGE     %BT10
20
        CMP     r8,#0
        BEQ     %FT35
30
        LDR     r2,[lr]
        BIC     r2,r2,r1
        ORR     r2,r2,r0
        STR     r2,[lr],#4
        SUBS    r8,r8,#1
        BNE     %BT30
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 L2PT for old logical page positions, does not update CAM
;
; entry:
;       r4  =  old logical address of 1st page
;       r8  =  number of pages
;
497
AMB_moveuncacheablepagesout_L2PT_ShortDesc
498
        ALTENTRY
Jeffrey Lee's avatar
Jeffrey Lee committed
499
55      ; Enter here from movecacheablepagesout
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
        LDR     lr,=L2PT
        ADD     lr,lr,r4,LSR #(Log2PageSize-2)    ;lr -> L2PT 1st entry

        MOV     r0,#0                             ;0 means translation fault
        MOV     r1,#0
        MOV     r2,#0
        MOV     r3,#0
        MOV     r4,#0
        MOV     r5,#0
        MOV     r6,#0
        MOV     r7,#0

        CMP     r8,#8
        BLT     %FT70
60
        STMIA   lr!,{r0-r7}                       ;blam! (8 entries)
        SUB     r8,r8,#8
        CMP     r8,#8
        BGE     %BT60
70
        CMP     r8,#0
        BEQ     %FT85
80
        STR     r0,[lr],#4
        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

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 615 616 617 618 619 620 621 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 723 724 725 726 727 728 729 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 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
 [ AMB_LazyMapIn
AMB_SetPageFlags_ShortDesc
        Entry
; Calculate default page flags
        MOV     R0, #0
        MOV     R1, #0
        GetPTE  R0, 4K, R0, R1, ShortDesc
        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_ShortDesc ROUT
        LDR     r5,=L2PT
        ADD     r5,r5,r8,LSR #10     ;r5 -> L2PT 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
        LDR     r6,[r5],#4           ;pick-up next L2PT entry
        CMP     r4,#0                ;if operation is a release...
        CMPEQ   r6,#0                ;...and L2PT 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 L2PT 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
        LDR     r6,[r5],#4           ;pick-up next L2PT entry
        CMP     r4,#1                ;if operation is a release (not 1)...
        CMPNE   r6,#0                ;...and L2PT 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 L2PT 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_ShortDesc ROUT
        ; Calculate required page flags
        CMP     r9, r6
        BEQ     %FT06
        LDR     r1, =ZeroPage
        MOV     r9, r6
        LDR     r1, [r1, #MMU_PCBTrans]
        GetTempUncache_ShortDesc r8, r9, r1, r4
06
        LDR     r1, =L2PT
        ; Bypass BangCAM and update L2PT directly (avoids CAM gaining any unwanted temp uncacheability flags)
        LDR     r5, =TempUncache_L2PTMask
        LDR     r4, [r1, r0, LSR #10]
        BIC     r4, r4, r5
        ORR     r4, r4, r8
        STR     r4, [r1, r0, LSR #10]
        MOV     pc, lr

        MACRO
$lab    ConstructIOPTE $pte, $phys_mb, $flags, $tmp
        ; $pte (output)                L1 page table entry word
        ; $phys_mb (input, preserved)  physical address, in megabytes
        ;   for vanilla sections:
        ;     bits 0..11 go in bits 20..31
        ;   for supersections:
        ;     bits 0..3 assumed zero
        ;     bits 4..11 go in bits 24..31
        ;     bits 12..15 go in bits 20..23
        ;     bits 16..20 go in bits 5..8
        ; $flags (input, preserved)    page table attribute bits
        ;
        ; UBFXNE should be safe pre v6T2, since we won't attempt to use
        ; supersections on such CPUs and they won't trap untaken undefined instructions
        ASSERT  $pte <> $phys_mb
        ASSERT  $pte <> $flags
        ASSERT  $pte <> $tmp
        ASSERT  $tmp <> $phys_mb
        ASSERT  $tmp <> $flags
$lab    ANDS    $tmp, $flags, #L1_SS
        UBFXNE  $tmp, $phys_mb, #32-20, #L1_SSb32Width
        ORR     $pte, $phys_mb, $tmp
        UBFXNE  $tmp, $phys_mb, #36-20, #L1_SSb36Width
        ASSERT  L1_SSb32Shift = 20
        ORR     $pte, $flags, $pte, LSL #L1_SSb32Shift
        ORRNE   $pte, $pte, $tmp, LSL #L1_SSb36Shift
        MEND

; 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_ShortDesc 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
        ; Use Get1MPTE to convert DA flags into L1PT section-mapping flags
        MOV     ip, #0
        GetPTE  a1, 1M, ip, a1, ShortDesc
        ; Mapping size (section or supersection) depends on address
        MOV     lr, a2, LSR #20
        ORR     lr, lr, a3, LSL #12                     ; lr = physical megabyte number
        TEQ     a3, #0
        ORRNE   a1, a1, #L1_SS                          ; need to use supersection for such addresses
        BIC     a2, a2, #&FF000000                      ; at most, bits 0-23 are used as offsets into section/supersection
        BICNE   lr, lr, #&F                             ; if address >4GB, round mapped address to 16MB (supersection)
        BICEQ   a2, a2, #&00F00000                      ; else no further rounding needed (section) and bits 20-23 are not used as an offset either
        ConstructIOPTE v3, lr, a1, ip
        LDR     ip, =L1PT + (PhysicalAccess:SHR:18)     ; ip -> L1PT entry
 [ MEMM_Type = "VMSAv6"
        ORR     v3, v3, #L1_XN                          ; force non-executable to prevent speculative instruction fetches
 ]
        TEQ     a4, #0
        LDRNE   lr, [ip]                                ; read old value (if necessary)
        STRNE   lr, [a4]                                ; put old one in [oldp]
        MOV     a4, #15
        STR     v3, [ip], #4                            ; store first of 16 new L1PT entries
        TST     v3, #L1_SS
        MOVEQ   v3, #0                                  ; if supersection mapped then use 16 duplicate entries, else remaining entries unmapped
10      SUBS    a4, a4, #1
        STR     v3, [ip], #4
        BNE     %BT10

        LDR     a1, =PhysicalAccess
        ORR     a1, a1, a2
        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_ShortDesc
        LDR     ip, =L1PT + (PhysicalAccess:SHR:18)     ; ip -> L1PT entry
        MOV     a4, #15
        STR     a1, [ip], #4                            ; restore first of 16 L1PT entries
        TST     a1, #L1_SS
        MOVEQ   a1, #0                                  ; if supersection mapped then use 16 duplicate entries, else remaining entries unmapped
10      SUBS    a4, a4, #1
        STR     a1, [ip], #4
        BNE     %BT10
        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_ShortDesc 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_ShortDesc 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
 ]

        TST     a1, #MapInFlag_APSpecified
        BICEQ   a1, a1, #L1_AP
        ; For VMSAv6, assume HAL knows what it's doing and requests correct settings for AP_ROM
        ORREQ   a1, a1, #L1_APMult * AP_None
        BIC     a1, a1, #3
 [ MEMM_Type = "VMSAv6"
        ORR     a1, a1, #L1_Section+L1_XN               ; force non-executable to prevent speculative instruction fetches
 |
        ORR     a1, a1, #L1_Section
 ]
RISCOS_MapInIO_PTE_ShortDesc ; a1 bits 0-19 = L1 section entry flags, 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

        MOVS    v3, v2
        MOVNE   v3, #&F                                 ; v3 = number of MB to use in rounding (0 for sections, 15 for supersections)
        MOV     v4, a2, LSR #20
        ORR     v4, v4, a3, LSL #12
        BIC     v4, v4, v3                              ; v4 = physical start MB to map
        MOV     v5, v1, LSR #20
        ORR     v5, v5, v2, LSL #12
        ADD     v5, v5, #1                              ; make exclusive
        ADD     v5, v5, v3
        BIC     v5, v5, v3                              ; v5 = physical end MB to map
        ANDS    a2, a1, #MapInFlag_DoublyMapped
        SUBNE   a2, v5, v4                              ; a2 = offset of second mapping (in MB) or 0
        LDR     v6, =&FFFFF
        AND     a1, a1, v6                              ; mask out our extra flags
        CMP     v5, #&1000
        ORRHI   a1, a1, #L1_SS                          ; set supersection flag if necessary
        LDR     a3, [ip, #IOAllocPtr]
        MOV     a3, a3, LSR #20
        ADD     a3, a3, v3
        BIC     a3, a3, v3                              ; a3 = logical MB that we're checking for a match
        ConstructIOPTE a4, v4, a1, lr                   ; a4 = first PT entry to match
        ADD     v3, v3, #1                              ; v3 = number of MB to step between sections or supersections
        LDR     v1, =L1PT
        LDR     v2, [ip, #IOAllocTop]
        MOV     v2, v2, LSR #20                         ; v2 = last logical MB to check (exclusive)
        SUB     a3, a3, v3                              ; no increment on first iteration
10
        ADD     a3, a3, v3                              ; next logical MB to check
        ADD     ip, a3, a2                              ; logical MB of base mapping or second mapping if there is one
        CMP     ip, v2
        BHS     %FT50                                   ; run out of logical addresses to check
        LDR     lr, [v1, a3, LSL #2]                    ; check only or first entry
        TEQ     lr, a4
        LDREQ   lr, [v1, ip, LSL #2]                    ; check only or second entry
        TEQEQ   lr, a4
        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                                  ; v6 = expected physical MB
        MOV     v7, a3                                  ; v7 = logical MB we expect to find it at
20
        ADD     v6, v6, v3                              ; next physical MB
        ADD     v7, v7, v3                              ; next logical MB
        ADD     ip, v7, a2                              ; logical MB of base mapping or second mapping if there is one
        CMP     v6, v5
        MOVHS   a4, a3, LSL #20
        BHS     %FT80                                   ; reached end and everything matched
        CMP     ip, v2
        BHS     %FT50                                   ; run out of logical addresses to check
        ConstructIOPTE v8, v6, a1, lr
        LDR     lr, [v1, v7, LSL #2]                    ; check only or first entry
        TEQ     lr, v8
        LDREQ   lr, [v1, ip, LSL #2]                    ; check only or second entry
        TEQEQ   lr, v8
        BEQ     %BT20                                   ; good so far, try next entry
        B       %BT10                                   ; mismatch, continue outer loop

50      ; Request not currently mapped, only partially mapped, or mapped with wrong flags
        LDR     ip, =ZeroPage
        SUB     v8, v3, #1                              ; v8 = number of MB to use in rounding (0 for sections, 15 for supersections)
        LDR     a3, [ip, #IOAllocPtr]
        MOV     a3, a3, LSR #20
        BIC     a3, a3, v8                              ; round down to 1MB or 16MB boundary (some memory may remain unmapped above when we map in a supersection)
        SUB     a4, v5, v4
        ADD     a4, a4, a2                              ; a4 = number of MB required
        SUB     a3, a3, a4
        MOV     a4, a3, LSL #20
        LDR     v6, [ip, #IOAllocLimit]
        CMP     a4, 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     a4, [ip, #IOAllocPtr]
60
        ConstructIOPTE v8, v4, a1, lr                   ; v8 = page table entry for this (super)section
        MOV     v7, v3                                  ; number of consecutive entries to program the same
70
        ADD     v6, a3, a2
        STR     v8, [v1, a3, LSL #2]                    ; write only or first entry
        ADD     a3, a3, #1
        STR     v8, [v1, v6, LSL #2]                    ; write only or second entry
        SUBS    v7, v7, #1
        BNE     %BT70
        ADD     v4, v4, v3
        CMP     v4, v5
        BLO     %BT60

        MOV     a2, a1
        PageTableSync                                   ; corrupts a1
        MOV     a1, a2
80
        LDR     a2, [sp]                                ; retrieve original physical address from stack
        BIC     a2, a2, #&FF000000                      ; distance from 16MB boundary for supersections
        TST     a1, #L1_SS
        BICEQ   a2, a2, #&00F00000                      ; distance from 1MB boundary for sections
        ADD     a1, a4, a2
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_ShortDesc
        ; Convert DA flags to page table entry
        GetPTE  r0, 1M, r0, r12, ShortDesc
   [ MEMM_Type = "VMSAv6"
        ORR     r0, r0, #L1_XN          ; force non-executable to prevent speculative instruction fetches
   ]
        ; Map in the region
        B       RISCOS_MapInIO_PTE_ShortDesc

899 900 901
        LTORG

        END