MemInfo 103 KB
Newer Older
Neil Turton's avatar
Neil Turton 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
; Copyright 1996 Acorn Computers 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.
;
; > MemInfo

        LTORG

;----------------------------------------------------------------------------------------
; MemorySWI
;
;       In:     r0 = reason code and flags
;                       bits 0-7  = reason code
;                       bits 3-31 = reason specific flags
;       Out:    specific to reason codes
;
;       Perform miscellaneous operations for memory management.
;
MemorySWI       ROUT
        Push    lr                              ; Save real return address.
        AND     lr, r0, #&FF                    ; Get reason code.
32 33
        CMP     lr, #OSMemReason_Convert64
        BHS     %FT50
Neil Turton's avatar
Neil Turton committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
        CMP     lr, #(%40-%30):SHR:2            ; If valid reason code then
        ADDCC   lr, lr, #(%30-%10):SHR:2        ;   determine where to jump to in branch table,
        ADDCC   lr, pc, lr, LSL #2
        Push    lr, CC                          ;   save address so we can
10
        ADRCC   lr, MemReturn                   ;   set up default return address for handler routines
        Pull    pc, CC                          ;   and jump into branch table.
20
        ADRL    r0, ErrorBlock_HeapBadReason    ; Otherwise, unknown reason code.
        SETV
        ; Drop through to...

MemReturn
 [ International
        BLVS    TranslateError
 ]
        Pull    lr                              ; Get back real return address.
        BVS     SLVK_SetV
        ExitSWIHandler

30
55
        B       MemoryConvertFIQCheck           ; 0
Neil Turton's avatar
Neil Turton committed
56 57 58 59 60
        B       %BT20                           ; Reason codes 1-5 are reserved.
        B       %BT20
        B       %BT20
        B       %BT20
        B       %BT20
61 62 63 64
        B       MemoryPhysSize                  ; 6
        B       MemoryReadPhys                  ; 7
        B       MemoryAmounts                   ; 8
        B       MemoryIOSpace                   ; 9
65
        B       %BT20                           ; Reason code 10 reserved (for free pool locking)
66
        B       %BT20                           ; Reason code 11 reserved (for PCImapping).
67 68 69 70
        B       RecommendPage                   ; 12
        B       MapIOpermanent                  ; 13
        B       AccessPhysAddr                  ; 14
        B       ReleasePhysAddr                 ; 15
71
        B       MemoryAreaInfo                  ; 16
72 73
        B       MemoryAccessPrivileges          ; 17
        B       FindAccessPrivilege             ; 18
74
        B       DMAPrep                         ; 19
75
        B       ChangeCompatibility             ; 20
76
        B       MapIO64permanent                ; 21
77
        B       AccessPhysAddr64                ; 22
78
        B       ReservePages                    ; 23
79 80
        B       CheckMemoryAccess               ; 24
                                                ; 25+ reserved for ROL
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
40      ; End of list

50
        SUB     lr, lr, #OSMemReason_Convert64
        CMP     lr, #(%90-%80):SHR:2            ; If valid reason code then
        ADDCC   lr, lr, #(%80-%60):SHR:2        ;   determine where to jump to in branch table,
        ADDCC   lr, pc, lr, LSL #2
        Push    lr, CC                          ;   save address so we can
60
        ADRCC   lr, MemReturn                   ;   set up default return address for handler routines
        Pull    pc, CC                          ;   and jump into branch table.
        B       %BT20                           ; Otherwise, unknown reason code.

80
        B       MemoryConvert64                 ; 64
96
        B       MemoryLogToPhys                 ; 65
97
90      ; End of list
Neil Turton's avatar
Neil Turton committed
98 99 100 101 102 103 104


;----------------------------------------------------------------------------------------
; MemoryConvert
;
;       In:     r0 = flags
;                       bit     meaning
105 106
;                       0-7     0 (reason code, page list uses 32bit addrs)
;                               or 64 (reason code, list uses 64bit addrs)
Neil Turton's avatar
Neil Turton committed
107 108 109 110 111 112 113 114 115 116
;                       8       page number provided when set
;                       9       logical address provided when set
;                       10      physical address provided when set
;                       11      fill in page number when set
;                       12      fill in logical address when set
;                       13      fill in physical address when set
;                       14-15   0,1=don't change cacheability
;                               2=disable caching on these pages
;                               3=enable caching on these pages
;                       16-31   reserved (set to 0)
117 118 119
;               r1 -> page block. 3 words per entry (OS_Memory 0) or 5 words
;                     per entry (OS_Memory 64).
;               r2 = number of entries in page block
Neil Turton's avatar
Neil Turton committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
;
;       Out:    r1 -> updated page block
;
;       Converts between representations of memory addresses. Can also set the
;       cacheability of the specified pages.
;

; Declare symbols used for decoding flags (given and wanted are used
; so that C can be cleared by rotates of the form a,b). We have to munge
; the flags a bit to make the rotates even.
;
ppn             *       1:SHL:0         ; Bits for address formats.
logical         *       1:SHL:1
physical        *       1:SHL:2
all             *       ppn :OR: logical :OR: physical
given           *       24              ; Rotate for given fields.
wanted          *       20              ; Rotate for wanted fields.
ppn_bits        *       ((ppn :SHL: 4) :OR: ppn)
logical_bits    *       ((logical :SHL: 4) :OR: logical)
physical_bits   *       ((physical :SHL: 4) :OR: physical)
cacheable_bit   *       1:SHL:15
alter_cacheable *       1:SHL:16
142 143 144 145 146 147 148 149 150
mem0_64         *       OSMemReason_Convert64

MemoryConvert64 ROUT
        ; Wrapper which checks for unsupported flags in R0
        CMP     r0, #1:SHL:16
        BLO     MemoryConvertFIQCheck
        ADRL    r0, ErrorBlock_BadParameters
        SETV
        MOV     pc, lr
Neil Turton's avatar
Neil Turton committed
151

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
; Small wrapper to make sure FIQs are disabled if we're making pages uncacheable
; (Modern ARMs ignore unexpected cache hits, so big coherency issues if we make
; a page uncacheable which is being used by FIQ).
MemoryConvertFIQCheck ROUT
        AND     r11, r0, #3:SHL:14
        TEQ     r11, #2:SHL:14
        BNE     MemoryConvertNoFIQCheck
        Entry   "r0-r1"
        MOV     r1, #Service_ClaimFIQ
        SWI     XOS_ServiceCall
        LDMIA   sp, {r0-r1}
        BL      MemoryConvertNoFIQCheck
        FRAMSTR r0
        MRS     r11, CPSR
        MOV     r1, #Service_ReleaseFIQ
        SWI     XOS_ServiceCall
168
        MSR     CPSR_f, r11
169 170 171
        EXIT

MemoryConvertNoFIQCheck   ROUT
172
        Entry   "r0-r11"                ; Need lots of registers!!
Neil Turton's avatar
Neil Turton committed
173

Kevin Bracey's avatar
Kevin Bracey committed
174 175 176 177 178
;        MRS     lr, CPSR
;        Push    "lr"
;        ORR     lr, lr, #I32_bit+F32_bit
;        MSR     CPSR_c, lr

179 180 181
        MOV     lr, r0, LSR #11         ; Need to munge r0 to get rotates to work (must be even)
        BIC     r0, r0, lr, LSL #11
        ORR     r0, r0, lr, LSL #12     ; Move bits 11-30 to 12-31. Bits 7 & 11 are clear, so the rotate will always clear C
Neil Turton's avatar
Neil Turton committed
182 183 184 185 186 187 188 189 190 191 192

        TST     r0, #all,given          ; Check for invalid argument (no fields provided)
        TEQNE   r2, #0                  ;   (no entries in table).
        ADREQL  r0, ErrorBlock_BadParameters
        BEQ     %FT95

        EOR     lr, r0, r0, LSL #given-wanted   ; If flag bits 8-10 and 12-14 contain common bits then
        AND     lr, lr, #all,wanted             ;   clear bits in 12-14 (ie. don't fill in fields already given).
        EOR     lr, lr, #all,wanted
        BIC     r0, r0, lr

Jeffrey Lee's avatar
Jeffrey Lee committed
193
        LDR     r6, =ZeroPage
Neil Turton's avatar
Neil Turton committed
194 195
        LDR     r7, [r6, #MaxCamEntry]
        LDR     r6, [r6, #CamEntriesPointer]
196 197 198 199

        TST     r0, #mem0_64                 ; Step back one entry (main loop
        SUBEQ   r1, r1, #MemPageBlock32_Size ; increments ptr at the start of
        SUBNE   r1, r1, #MemPageBlock64_Size ; the loop)
Neil Turton's avatar
Neil Turton committed
200 201 202 203
10
        SUBS    r2, r2, #1
        BCC     %FT70

204 205 206 207
        TST     r0, #mem0_64
        ADDEQ   r1, r1, #MemPageBlock32_Size
        ADDNE   r1, r1, #MemPageBlock64_Size

208 209 210
        ASSERT  MemPageBlock32_PageNum=0
        ASSERT  MemPageBlock32_LogAddr=4
        ASSERT  MemPageBlock32_PhysAddr=8
211 212 213 214 215 216 217 218 219 220 221 222 223 224
        LDMIA   r1, {r3-r4,r8}          ; Get next three words (PN,LA,PA)
        MOVEQ   r9, #0                  ; High word of phys addr
        LDRNE   r9, [r1, #MemPageBlock64_PhysHigh]

        ; If we're using the 64bit API, the LDM will have actually loaded these
        ASSERT  MemPageBlock64_PageNum=0
        ASSERT  MemPageBlock64_LogLow=4
        ASSERT  MemPageBlock64_LogHigh=8
        ; Load the correct low phys addr, and if log addr given, check high word is zero
        MOVNE   lr, r8
        LDRNE   r8, [r1, #MemPageBlock64_PhysLow]
        TSTNE   r0, #logical,given
        CMPNE   lr, #0
        BNE     %FT80
Neil Turton's avatar
Neil Turton committed
225

226
   [ AMB_LazyMapIn
227 228 229
        BL      handle_AMBHonesty       ; may need to make page honest (as if not lazily mapped)
   ]

Neil Turton's avatar
Neil Turton committed
230 231
        TST     r0, #physical,wanted    ; If PA not wanted
        BEQ     %FT20                   ;   then skip.
232
        TST     r0, #logical,given      ; If LA given (rotate clears C) then
233 234 235 236
        BEQ     %FT11
        PTOp    logical_to_physical     ; Get PA from LA
        B       %FT15
11
237 238 239
        BL      ppn_to_logical          ; Else get LA from PN (PA wanted (not given) & LA not given => PN given).
        BLCC    ppn_to_physical         ; And get PA from PN (more accurate than getting PA from LA - page may be mapped out)
15
Neil Turton's avatar
Neil Turton committed
240
        BCS     %FT80
241 242 243 244 245 246 247 248 249 250 251 252
        MVN     lr, r0
        CMP     r9, #0                  ; If high phys addr non-zero
        TSTNE   lr, #mem0_64            ; And not using 64bit API
        BNE     %FT80                   ; Throw error

        ; Store phys addr
        TST     r0, #mem0_64
        STREQ   r8, [r1, #MemPageBlock32_PhysAddr]
        STRNE   r8, [r1, #MemPageBlock64_PhysLow]
        STRNE   r9, [r1, #MemPageBlock64_PhysHigh]

        ; Store log addr, if wanted
Neil Turton's avatar
Neil Turton committed
253
        TST     r0, #logical,wanted
254 255 256 257 258
        ASSERT  MemPageBlock64_LogLow = MemPageBlock32_LogAddr
        STRNE   r4, [r1, #MemPageBlock32_LogAddr]
        TSTNE   r0, #mem0_64
        MOVNE   lr, #0
        STRNE   lr, [r1, #MemPageBlock64_LogHigh]
Neil Turton's avatar
Neil Turton committed
259 260 261 262 263 264 265
20
        TST     r0, #alter_cacheable    ; If altering cacheability
        EORNE   lr, r0, #ppn,given      ;   and PN not given
        TSTNE   lr, #ppn,given
        TSTEQ   r0, #ppn,wanted         ;   OR PN wanted then don't skip
        BEQ     %FT30                   ; else skip.
        TST     r0, #physical_bits,given        ; If PA not given and PA not wanted (rotate clears C) then
266
        PTOpEQ  logical_to_physical             ;   get it from LA (PN wanted/not given & PA not given => LA given).
Neil Turton's avatar
Neil Turton committed
267 268 269
        BLCC    physical_to_ppn         ; Get PN from PA.
        BCS     %FT80
        TST     r0, #ppn,wanted
270 271
        ASSERT  MemPageBlock64_PageNum = MemPageBlock32_PageNum
        STRNE   r3, [r1, #MemPageBlock32_PageNum] ; Store back PN if wanted.
Neil Turton's avatar
Neil Turton committed
272 273 274 275 276 277 278 279 280 281
30
        TST     r0, #logical,wanted     ; If LA wanted
        EORNE   lr, r0, #physical,wanted
        TSTNE   lr, #physical,wanted    ;   and PA not wanted then don't skip
        BEQ     %FT40                   ; else skip.
        TST     r0, #alter_cacheable    ; If not changing cacheability (already have PN)
        TSTEQ   r0, #ppn_bits,given     ;   and PN not given and PN not wanted (rotate clears C) then
        BLEQ    physical_to_ppn         ;   get it from PA (LA wanted (not given) & PN not given => PA given).
        BLCC    ppn_to_logical          ; Get LA from PN.
        BCS     %FT80
282 283 284 285 286 287
        ; Store back log addr
        ASSERT  MemPageBlock64_LogLow = MemPageBlock32_LogAddr
        STR     r4, [r1, #MemPageBlock32_LogAddr]
        TST     r0, #mem0_64
        MOVNE   lr, #0
        STRNE   lr, [r1, #MemPageBlock64_LogHigh]
Neil Turton's avatar
Neil Turton committed
288 289 290 291 292 293 294
40
        TST     r0, #alter_cacheable
        BEQ     %BT10

        CMP     r7, r3                  ; Make sure page number is valid (might not have done any conversion).
        BCC     %FT80

295 296 297
        ADD     r3, r6, r3, LSL #CAM_EntrySizeLog2 ; Point to CAM entry for this page.
        ASSERT  CAM_LogAddr=0
        ASSERT  CAM_PageFlags=4
Neil Turton's avatar
Neil Turton committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
        LDMIA   r3, {r4,r5}             ; Get logical address and PPL.

        AND     lr, r5, #PageFlags_TempUncacheableBits
        TST     r0, #cacheable_bit
        BNE     %FT50

        TEQ     lr, #PageFlags_TempUncacheableBits      ; Make uncacheable (increment count).
        BEQ     %BT10                                   ; If count has reached max then go no further (should not happen).
        TEQ     lr, #0                                  ; EQ => we have to change L2.
        ADD     r5, r5, #1:SHL:TempUncacheableShift
        B       %FT60
50
        TEQ     lr, #0                                  ; Make cacheable (decrement count).
        BEQ     %BT10                                   ; If count is already 0 then go no further (page already cacheable).
        SUB     r5, r5, #1:SHL:TempUncacheableShift
        TST     r5, #PageFlags_TempUncacheableBits      ; EQ => we have to change L2.
60
315
        STR     r5, [r3, #CAM_PageFlags] ; Write back new PPL.
Neil Turton's avatar
Neil Turton committed
316 317 318
        BNE     %BT10                   ; Do next entry if we don't have to change L2.

        MOV     r4, r4, LSR #12
319 320 321 322
 [ LongDesc :LAND: ShortDesc
        PTWhich r8
        BEQ     %FT65
 ]
Jeffrey Lee's avatar
Jeffrey Lee committed
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 362 363 364 365 366 367 368 369 370 371 372 373
 [ LongDesc
        LDR     r8, =LL3PT
        LDR     r3, =ZeroPage
        ADD     r4, r8, r4, LSL #3      ; Address of L3 entry for logical address.
        ; VMSAv6 is hard, use XCBTable/PCBTrans
        ASSERT  DynAreaFlags_CPBits = 7*XCB_P :SHL: 10
        ASSERT  DynAreaFlags_NotCacheable = XCB_NC :SHL: 4
        ASSERT  DynAreaFlags_NotBufferable = XCB_NB :SHL: 4
        TST     r0, #cacheable_bit      ; n.b. must match EQ/NE used by ARMop calls
        AND     lr, r5, #DynAreaFlags_NotCacheable + DynAreaFlags_NotBufferable
        AND     r5, r5, #DynAreaFlags_CPBits
        ORR     lr, lr, r5, LSR #10-4
        LDR     r5, [r3, #MMU_PCBTrans]
        ORREQ   lr, lr, #XCB_TU<<4      ; if temp uncache, set TU bit
        LDRB    lr, [r5, lr, LSR #4]    ; get AttrIndx value
        LDRD    r8, [r4]                ; Get L3 entry (safe as we know address is valid).
        BIC     r8, r8, #TempUncache_L3PTMask ; Knock out existing attributes (n.b. assumed to not be large page!)
        ORR     r8, r8, lr              ; Set new attributes
        BNE     %FT63
        ; Making page non-cacheable
        ; There's a potential interrupt hole here - many ARMs ignore cache hits
        ; for pages which are marked as non-cacheable (seen on XScale,
        ; Cortex-A53, Cortex-A15 to name but a few, and documented in many TRMs)
        ; We can't be certain that this page isn't being used by an interrupt
        ; handler, so if we're making it non-cacheable we have to take the safe
        ; route of disabling interrupts around the operation.
        ; Note - currently no consideration is given to FIQ handlers.
        ; Note - we clean the cache as the last step (as opposed to doing it at
        ; the start) to make sure prefetching doesn't pull data back into the
        ; cache.
        MRS     r11, CPSR
        ORR     lr, r11, #I32_bit       ; IRQs off
        ; Yuck, we also need to deal with the case where we're making the
        ; current SVC stack page uncacheable (coherency issue when calling the
        ; ARMops if cache hits to uncacheable pages are ignored). Deal with this
        ; by temporarily dropping into IRQ mode (and thus a different stack) if
        ; we think this is going to happen.
        MOV     r5, r4, LSL #9          ; R5 = original logical address
      [ (LL3PT:SHL:9) <> 0
        SUB     r5, r5, #LL3PT:SHL:9
      ]
        SUB     r10, sp, r5
        CMP     r10, #8192              ; Be extra cautious
        EORLO   lr, lr, #SVC32_mode :EOR: IRQ32_mode
        MSR     CPSR_c, lr              ; Switch mode
        Push    "r0, lr"                ; Preserve OS_Memory flags and (potential) IRQ lr
        STRD    r8, [r4]                ; Write back new L3 entry.
        MOV     r0, r5
        ARMop   MMU_ChangingEntry,,,r3  ; Clean TLB+cache
        Pull    "r0, lr"                ; Restore OS_Memory flags + IRQ lr
        MSR     CPSR_c, r11             ; Back to original mode + IRQ state
374
        B       %BT10
Jeffrey Lee's avatar
Jeffrey Lee committed
375 376 377 378 379 380 381
63
        ; Making page cacheable again
        ; Shouldn't be any cache maintenance worries
        STRD    r8, [r4]                ; Write back new L2 entry.
        MOV     r4, r0
        MOV     r0, r5
        ARMop   MMU_ChangingUncachedEntry,,,r3   ; Clean TLB
382
        MOV     r0, r4 
Jeffrey Lee's avatar
Jeffrey Lee committed
383
        B       %BT10
384 385 386
 ]
 [ ShortDesc
65
387
        LDR     r8, =L2PT
388
        LDR     r3, =ZeroPage
Neil Turton's avatar
Neil Turton committed
389
        ADD     r4, r8, r4, LSL #2      ; Address of L2 entry for logical address.
Jeffrey Lee's avatar
Jeffrey Lee committed
390
   [ MEMM_Type = "VMSAv6"
391 392 393 394 395 396 397 398 399 400
        ; VMSAv6 is hard, use XCBTable/PCBTrans
        ASSERT  DynAreaFlags_CPBits = 7*XCB_P :SHL: 10
        ASSERT  DynAreaFlags_NotCacheable = XCB_NC :SHL: 4
        ASSERT  DynAreaFlags_NotBufferable = XCB_NB :SHL: 4
        TST     r0, #cacheable_bit      ; n.b. must match EQ/NE used by ARMop calls
        AND     lr, r5, #DynAreaFlags_NotCacheable + DynAreaFlags_NotBufferable
        AND     r5, r5, #DynAreaFlags_CPBits
        ORR     lr, lr, r5, LSR #10-4
        LDR     r5, [r3, #MMU_PCBTrans]
        ORREQ   lr, lr, #XCB_TU<<4      ; if temp uncache, set TU bit
401 402
        MOV     lr, lr, LSR #3
        LDRH    lr, [r5, lr]            ; convert to C, B and TEX bits for this CPU
403
        LDR     r5, [r4]                ; Get L2 entry (safe as we know address is valid).
404
        BIC     r5, r5, #TempUncache_L2PTMask ; Knock out existing attributes (n.b. assumed to not be large page!)
405
        ORR     r5, r5, lr              ; Set new attributes
Jeffrey Lee's avatar
Jeffrey Lee committed
406
   |
Neil Turton's avatar
Neil Turton committed
407 408 409 410
        LDR     r5, [r4]                ; Get L2 entry (safe as we know address is valid).
        TST     r0, #cacheable_bit
        BICEQ   r5, r5, #L2_C           ; Disable/enable cacheability.
        ORRNE   r5, r5, #L2_C
Jeffrey Lee's avatar
Jeffrey Lee committed
411
   ]
412
        BNE     %FT67
Jeffrey Lee's avatar
Jeffrey Lee committed
413 414 415 416 417 418 419 420 421 422 423
        ; Making page non-cacheable
        ; There's a potential interrupt hole here - many ARMs ignore cache hits
        ; for pages which are marked as non-cacheable (seen on XScale,
        ; Cortex-A53, Cortex-A15 to name but a few, and documented in many TRMs)
        ; We can't be certain that this page isn't being used by an interrupt
        ; handler, so if we're making it non-cacheable we have to take the safe
        ; route of disabling interrupts around the operation.
        ; Note - currently no consideration is given to FIQ handlers.
        ; Note - we clean the cache as the last step (as opposed to doing it at
        ; the start) to make sure prefetching doesn't pull data back into the
        ; cache.
424 425 426 427 428 429 430 431 432 433 434 435 436
        MRS     r11, CPSR
        ORR     lr, r11, #I32_bit       ; IRQs off
        ; Yuck, we also need to deal with the case where we're making the
        ; current SVC stack page uncacheable (coherency issue when calling the
        ; ARMops if cache hits to uncacheable pages are ignored). Deal with this
        ; by temporarily dropping into IRQ mode (and thus a different stack) if
        ; we think this is going to happen.
        MOV     r10, r4, LSL #10
        SUB     r10, sp, r10
        CMP     r10, #8192              ; Be extra cautious
        EORLO   lr, lr, #SVC32_mode :EOR: IRQ32_mode
        MSR     CPSR_c, lr              ; Switch mode
        Push    "r0, lr"                ; Preserve OS_Memory flags and (potential) IRQ lr
Jeffrey Lee's avatar
Jeffrey Lee committed
437 438 439
        STR     r5, [r4]                ; Write back new L2 entry.
        ASSERT  (L2PT :SHL: 10) = 0     ; Ensure we can convert r4 back to the page log addr
        MOV     r0, r4, LSL #10
440
        ARMop   MMU_ChangingEntry,,,r3  ; Clean TLB+cache
Jeffrey Lee's avatar
Jeffrey Lee committed
441
        Pull    "r0, lr"                ; Restore OS_Memory flags + IRQ lr
442
        MSR     CPSR_c, r11             ; Back to original mode + IRQ state
443 444
        B       %BT10
67
Jeffrey Lee's avatar
Jeffrey Lee committed
445 446 447
        ; Making page cacheable again
        ; Shouldn't be any cache maintenance worries
        STR     r5, [r4]                ; Write back new L2 entry.
448
        MOV     r5, r0
449
        ASSERT  (L2PT :SHL: 10) = 0     ; Ensure we can convert r4 back to the page log addr
450
        MOV     r0, r4, LSL #10
Jeffrey Lee's avatar
Jeffrey Lee committed
451
        ARMop   MMU_ChangingUncachedEntry,,,r3   ; Clean TLB
452 453
        MOV     r0, r5
        B       %BT10
Jeffrey Lee's avatar
Jeffrey Lee committed
454
 ]
455 456

70
457
        CLRV
Neil Turton's avatar
Neil Turton committed
458 459 460 461 462 463 464 465 466 467 468 469 470
        EXIT

80
        TST     r0, #alter_cacheable    ; If we haven't changed any cacheability stuff then
        BEQ     %FT90                   ;   just return error.

        AND     lr, r0, #all,wanted             ; Get wanted flags.
        LDMIA   sp, {r0,r1,r3}                  ; Get back original flags, pointer and count.
        ORR     r0, r0, lr, LSR #given-wanted   ; Wanted fields are now also given as we have done the conversion.
        BIC     r0, r0, #all:SHL:11             ; Clear wanted flags, we only want to change cacheability.
        EOR     r0, r0, #cacheable_bit          ; If we made them uncacheable then make them cacheable again & v.v.
        SUB     r2, r3, r2
        SUBS    r2, r2, #1              ; Change back the entries we have changed up to (but excluding) the error entry.
471
        BLNE    MemoryConvertNoFIQCheck
Neil Turton's avatar
Neil Turton committed
472 473 474
90
        ADRL    r0, ErrorBlock_BadAddress
95
475
        STR     r0, [sp, #Proc_RegOffset+0]
Neil Turton's avatar
Neil Turton committed
476 477 478
        SETV
        EXIT

479
   [ AMB_LazyMapIn
480
;
481
;  entry: r3,r4,r8,r9 = provided PN,LA,PA triple for entry to make honest (at least one given)
482 483 484
;         r0 bits flag which of PN,LA,PA are given
;  exit:  mapping made honest (as if not lazily mapped) if necessary
handle_AMBHonesty  ROUT
485
        Push    "r0, r3-r4, lr"
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
        TST     r0, #logical,given
        BEQ     %FT10
        MOV     r0, r4
        BL      AMB_MakeHonestLA
        B       %FT90
10
        TST     r0, #ppn,given
        BEQ     %FT20
15
        MOV     r0, r3
        BL      AMB_MakeHonestPN
        B       %FT90
20
        TST     r0, #physical,given
        BEQ     %FT90
501
        Push    "r5, r7, r10-r11"
Jeffrey Lee's avatar
Jeffrey Lee committed
502
        LDR     r14, =ZeroPage
503 504
        LDR     r7, [r14, #MaxCamEntry]
        BL      physical_to_ppn
505
        Pull    "r5, r7, r10-r11"
506 507
        BCC     %BT15
90
508
        Pull    "r0, r3-r4, pc"
509

510
   ] ;AMB_LazyMapIn
511

Neil Turton's avatar
Neil Turton committed
512 513 514 515 516

;----------------------------------------------------------------------------------------
; ppn_to_logical
;
;       In:     r3 = page number
517
;               r8,r9 = physical address if given
Neil Turton's avatar
Neil Turton committed
518 519 520
;               r6 = CamEntriesPointer
;               r7 = MaxCamEntry
;
521
;       Out:    r5 corrupted
Neil Turton's avatar
Neil Turton committed
522 523 524 525 526 527 528
;               CC => r4 = logical address
;               CS => invalid page number
;
;       Convert physical page number to logical address.
;
ppn_to_logical
        CMP     r7, r3                  ; Validate page number.
Kevin Bracey's avatar
Kevin Bracey committed
529
        BCC     meminfo_returncs        ; Invalid so return C set.
Neil Turton's avatar
Neil Turton committed
530

531 532
        ASSERT  CAM_LogAddr=0
        LDR     r4, [r6, r3, LSL #CAM_EntrySizeLog2] ; If valid then lookup logical address.
Neil Turton's avatar
Neil Turton committed
533
        TST     r0, #physical,given     ; If physical address was given then
534 535 536 537 538 539 540
      [ NoARMT2
        LDRNE   r5, =&FFF
        ANDNE   r5, r8, r5              ;   mask off page offset
        ORRNE   r4, r4, r5              ;   and combine with logical address.
      |
        BFINE   r4, r8, #0, #12         ;   apply page offset
      ]
Kevin Bracey's avatar
Kevin Bracey committed
541 542
        CLC
        MOV     pc, lr
Neil Turton's avatar
Neil Turton committed
543

544 545
meminfo_returncs_pullr8
        Pull    "r8"
Kevin Bracey's avatar
Kevin Bracey committed
546 547 548
meminfo_returncs
        SEC
        MOV     pc, lr
Neil Turton's avatar
Neil Turton committed
549 550 551 552

;----------------------------------------------------------------------------------------
; physical_to_ppn
;
553
;       In:     r8,r9 = physical address
Neil Turton's avatar
Neil Turton committed
554 555
;               r7 = MaxCamEntry
;
556 557 558
;       Out:    r5,r10 corrupted
;               CC => r3 = page number, low 12 bits of r11 = PhysRamTable flags
;               CS => invalid physical address, r3+r11 corrupted
Neil Turton's avatar
Neil Turton committed
559 560 561 562
;
;       Convert physical address to physical page number.
;
physical_to_ppn ROUT
563 564
        Push    "r8"
        LDR     r5, =ZeroPage+PhysRamTable
Neil Turton's avatar
Neil Turton committed
565
        MOV     r3, #0                  ; Start at page 0.
566
        MOV     r8, r8, LSR #12
567
        ORR     r8, r8, r9, LSL #20
Neil Turton's avatar
Neil Turton committed
568 569
10
        CMP     r7, r3                  ; Stop if we run out of pages
570
        BCC     meminfo_returncs_pullr8
Neil Turton's avatar
Neil Turton committed
571

572
        LDMIA   r5!, {r10,r11}          ; Get start address and size of next block.
573
        SUB     r10, r8, r10            ; Determine if given address is in this block.
574
        CMP     r10, r11, LSR #12
Neil Turton's avatar
Neil Turton committed
575 576 577
        ADDCS   r3, r3, r11, LSR #12    ; Move on to next block.
        BCS     %BT10

578
        Pull    "r8"
Jeffrey Lee's avatar
Jeffrey Lee committed
579

580
        ADD     r3, r3, r10
Kevin Bracey's avatar
Kevin Bracey committed
581 582
        CLC
        MOV     pc, lr
Neil Turton's avatar
Neil Turton committed
583

584 585 586 587 588
;----------------------------------------------------------------------------------------
; ppn_to_physical
;
;       In:     r3 = page number
;
589 590 591
;       Out:    r5 corrupted
;               CC => r8,r9 = physical address
;               CS => invalid page number, r8,r9 corrupted
592 593 594 595 596
;
;       Convert physical page number to physical address.
;
ppn_to_physical ROUT
        Push    "r3,lr"
597
        LDR     r5, =ZeroPage+PhysRamTable
598
10
599
        LDMIA   r5!, {r8,lr}            ; Get start address and size of next block.
600 601 602 603 604 605
        MOVS    lr, lr, LSR #12
        BEQ     %FT20
        CMP     r3, lr
        SUBHS   r3, r3, lr
        BHS     %BT10

606 607 608
        ADD     r8, r8, r3
        MOV     r9, r8, LSR #20
        MOV     r8, r8, LSL #12
609 610 611 612 613
        Pull    "r3,pc"
20
        SEC
        Pull    "r3,pc"

Neil Turton's avatar
Neil Turton committed
614 615 616 617 618 619

;----------------------------------------------------------------------------------------
; Symbols used in MemoryPhysSize and MemoryReadPhys
;

; Shifts to determine number of bytes/words to allocate in table.
620 621
ByteShift       *       1             ; 2^1 pages per byte
WordShift       *       ByteShift + 2 ; 2^3 pages per word
Neil Turton's avatar
Neil Turton committed
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642

; Bit patterns for different types of memory.
NotPresent      *       &00000000
DRAM_Pattern    *       &11111111
VRAM_Pattern    *       &22222222
ROM_Pattern     *       &33333333
IO_Pattern      *       &44444444
NotAvailable    *       &88888888


;----------------------------------------------------------------------------------------
; MemoryPhysSize
;
;       In:     r0 = 6 (reason code with flag bits 8-31 clear)
;
;       Out:    r1 = table size (in bytes)
;               r2 = page size (in bytes)
;
;       Returns information about the memory arrangement table.
;
MemoryPhysSize
643
        Entry   "r0-r1,r3,sb,ip"
644
        AddressHAL
645 646
        MOV     r0, #PhysInfo_GetTableSize
        ADD     r1, sp, #4
647 648
        CallHAL HAL_PhysInfo
        MOV     r2, #4*1024
649
        CLRV
650
        EXIT
Neil Turton's avatar
Neil Turton committed
651 652 653 654 655 656 657 658 659 660 661 662 663 664


;----------------------------------------------------------------------------------------
; MemoryReadPhys
;
;       In:     r0 = 7 (reason code with flag bits 8-31 clear)
;               r1 -> memory arrangement table to be filled in
;
;       Out:    r1 -> filled in memory arrangement table
;
;       Returns the physical memory arrangement table in the given block.
;
MemoryReadPhys  ROUT

665 666
        Entry   "r0-r12"
        AddressHAL
667 668 669
        MOV     r0, #PhysInfo_WriteTable
        SUB     sp, sp, #8
        MOV     r2, sp
670
        CallHAL HAL_PhysInfo            ; fills in everything except DRAM
671
        ADD     sp, sp, #8              ; We don't use this address range any more
672

673 674
        MOV     r5, #0                  ; Current page number.
        LDR     r6, =ZeroPage+PhysRamTable
675 676 677 678
        LDR     r7, [r6, #CamEntriesPointer-PhysRamTable]
        ADD     r7, r7, #CAM_PageFlags  ; Point to PPL entries.
        LDR     r8, [r6, #MaxCamEntry-PhysRamTable]
10
679
        LDMIA   r6!, {r9,r10}           ; Get physical address and size of next block.
680

681 682 683 684
        TST     r10, #OSAddRAM_IsVRAM   ; If not DRAM then
        ADDNE   r5, r5, r10, LSR #12    ;   adjust current page number
        BNE     %BT10                   ;   and try next block.

685
        MOV     r10, r10, LSR #12
686 687 688
        LDR     r1, [sp, #4]            ; Get table address back
        MOV     r3, r9, LSR #WordShift
        LDR     r3, [r1, r3, LSL #2]!   ; Get first word of block
689 690
        MOV     r4, r9, LSL #3
        AND     r4, r4, #31             ; Bit offset of first page in the word
691 692 693 694 695 696
        RSB     r4, r4, #32             ; number of bits left to process
        MOV     r3, r3, LSL r4

        ; r1 -> current table location
        ; r3 = next word to store in table
        ; r4 = how much we have to shift r3 before storing it
697 698 699 700 701 702 703
20
        SUBS    r4, r4, #4              ; Reduce shift.
        MOVCS   r3, r3, LSR #4          ; If more space in current word then shift it.
        STRCC   r3, [r1], #4            ; Otherwise, store current word
        MOVCC   r3, #0                  ;   and start a new one.
        MOVCC   r4, #28

704
        LDR     lr, [r7, r5, LSL #CAM_EntrySizeLog2] ; Page is there so get PPL and determine if it's available or not.
705
        TST     lr, #PageFlags_Unavailable
706
        TSTEQ   lr, #PageFlags_Reserved
707 708 709 710
        ORREQ   r3, r3, #DRAM_Pattern :SHL: 28
        ORRNE   r3, r3, #(DRAM_Pattern :OR: NotAvailable) :SHL: 28
        ADD     r5, r5, #1              ; Increment page count.
30
711 712 713 714 715 716 717 718 719 720 721 722 723
        SUBS    r10, r10, #1            ; Decrease size of block.
        BNE     %BT20                   ; Stop if no more block left.

        ; Store the partial last word
        LDR     lr, [r1]
        MOV     r3, r3, LSR r4          ; put bits in correct position
        RSB     r4, r4, #32
        MOV     lr, lr, LSR r4          ; drop the low bits of lr
        ORR     r3, r3, lr, LSL r4      ; combine with r3
        STR     r3, [r1]                ; and store word.

        CMP     r8, r5                  ; Stop if we run out of pages.
        BCS     %BT10
724

725 726 727 728 729 730 731
        ; If softloaded, mark that as unavailable DRAM.
        MOV     r0, #8
        SWI     XOS_ReadSysInfo
        BVS     %FT40
        AND     r1, r1, r2
        ANDS    r1, r1, #1:SHL:4        ; Test OS-runs-from-RAM flag
        BEQ     %FT40
Jeffrey Lee's avatar
Jeffrey Lee committed
732
        LDR     r0, =ZeroPage
733 734
        LDR     r0, [r0, #ROMPhysAddr]
        LDR     r1, [sp, #4]
735
        ADD     r0, r1, r0, LSR #ByteShift+12
736
        LDR     r1, =DRAM_Pattern :OR: NotAvailable
737
        MOV     r2, #(OSROM_ImageSize :SHR: 2) :SHR: ByteShift
738 739
        BL      memset
40
740
        CLRV
Neil Turton's avatar
Neil Turton committed
741 742 743 744 745 746 747 748 749
        EXIT


;----------------------------------------------------------------------------------------
; MemoryAmounts
;
;       In:     r0 = flags
;                       bit     meaning
;                       0-7     8 (reason code)
750
;                       8-11    1=return amount of DRAM (excludes any soft ROM)
Neil Turton's avatar
Neil Turton committed
751 752 753
;                               2=return amount of VRAM
;                               3=return amount of ROM
;                               4=return amount of I/O space
754
;                               5=return amount of soft ROM (ROM loaded into hidden DRAM)
Neil Turton's avatar
Neil Turton committed
755 756 757 758 759 760 761 762
;                       12-31   reserved (set to 0)
;
;       Out:    r1 = number of pages of the specified type of memory
;               r2 = page size (in bytes)
;
;       Return the amount of the specified type of memory.
;
MemoryAmounts   ROUT
763
        Entry   "r3"
Neil Turton's avatar
Neil Turton committed
764

765
        BICS    lr, r0, #&FF            ; Get type of memory required (leave bits 12-31, non-zero => error).
766 767 768 769 770 771 772 773 774
        CMP     lr, #6:SHL:8
        ADDCC   pc, pc, lr, LSR #8-2
        NOP
        B       %FT99                   ; Don't understand 0 (so the spec says).
        B       %FT10                   ; DRAM
        B       %FT20                   ; VRAM
        B       %FT30                   ; ROM
        B       %FT40                   ; I/O
        B       %FT50                   ; Soft ROM
Kevin Bracey's avatar
Kevin Bracey committed
775

776 777
10
        LDR     r1, =ZeroPage
778 779
        LDR     r3, [r1, #VideoSizeFlags]
        TST     r3, #OSAddRAM_IsVRAM
780 781 782 783
        MOVNE   r3, r3, LSR #12         ; Extract size from flags when genuine VRAM
        MOVEQ   r3, #0
        LDR     r1, [r1, #RAMLIMIT]
        SUB     r1, r1, r3              ; DRAM = RAMLIMIT - VRAMSize
784
        B       %FT98
785 786
20
        LDR     r1, =ZeroPage
787 788
        LDR     r1, [r1, #VideoSizeFlags]
        TST     r1, #OSAddRAM_IsVRAM
789 790 791 792 793
        MOVNE   r1, r1, LSR #12
        MOVNE   r1, r1, LSL #12         ; VRAM = VRAMSize
        MOVEQ   r1, #0
        B       %FT97
30
794 795 796 797 798 799 800
        Push    "r0, sb, ip"
        AddressHAL
        MOV     r0, #PhysInfo_HardROM
        SUB     sp, sp, #8
        MOV     r2, sp
        CallHAL HAL_PhysInfo
        LDMIA   sp!, {r0-r1}
801 802
        SUBS    r1, r1, r0
        ADDNE   r1, r1, #1              ; ROM = ROMPhysTop + 1 - ROMPhysBot
803
        Pull    "r0, sb, ip"
804 805 806
        B       %FT97
40
        LDR     r1, =ZeroPage
Jeffrey Lee's avatar
Jeffrey Lee committed
807
        LDR     r3, [r1, #IOAllocTop]
808 809 810 811
        LDR     r1, [r1, #IOAllocLimit]
        SUB     r1, r3, r1              ; IO = IO ceiling - IO floor
        B       %FT97
50
812 813
        Push    "r0"
        MOV     r0, #8
814
        SWI     XOS_ReadSysInfo         ; Are we softloaded?
815 816
        Pull    "r0"
        AND     r1, r1, r2
817 818 819 820
        ANDS    r1, r1, #1:SHL:4        ; Test OS-runs-from-RAM flag
        MOVNE   r1, #OSROM_ImageSize*1024
        B       %FT97
97
821
        MOV     r1, r1, LSR #12         ; Return as number of pages.
822
98
823
        MOV     r2, #4*1024             ; Return page size.
824
        CLRV
825
        EXIT
826
99
827 828 829 830
        PullEnv
        ; Fall through...
MemoryBadParameters
        ADRL    r0, ErrorBlock_BadParameters ; n.b. MemReturn handles internationalisation
831
        SETV
832
        MOV     pc, lr
Neil Turton's avatar
Neil Turton committed
833 834 835 836 837 838 839 840 841 842 843


;----------------------------------------------------------------------------------------
; MemoryIOSpace
;
;       In:     r0 = 9 (reason code with flag bits 8-31 clear)
;               r1 = controller ID
;                       bit     meaning
;                       0-7     controller sequence number
;                       8-31    controller type:
;                               0 = EASI card access speed control
Kevin Bracey's avatar
Kevin Bracey committed
844
;                               1 = EASI space(s)
Neil Turton's avatar
Neil Turton committed
845 846
;                               2 = VIDC1
;                               3 = VIDC20
Kevin Bracey's avatar
Kevin Bracey committed
847 848
;                               4 = S space (IOMD,podules,NICs,blah blah)
;                               5 = Extension ROM(s)
849 850 851 852 853 854
;                               6 = Tube ULA
;                               7-31 = Reserved (for us)
;                               32 = Primary ROM
;                               33 = IOMD
;                               34 = FDC37C665/SMC37C665/82C710/SuperIO/whatever
;                               35+ = Reserved (for ROL)
Neil Turton's avatar
Neil Turton committed
855 856 857 858 859
;
;       Out:    r1 = controller base address or 0 if not present
;
;       Return the location of the specified controller.
;
Kevin Bracey's avatar
Kevin Bracey committed
860 861 862 863 864

MemoryIOSpace   ROUT
        Entry   "r0,r2,r3,sb,ip"
        AddressHAL
        CallHAL HAL_ControllerAddress
Kevin Bracey's avatar
Kevin Bracey committed
865 866
        CMP     r0, #-1
        MOVNE   r1, r0
867 868 869
        PullEnv
        MOVNE   pc, lr
        B       MemoryBadParameters
Neil Turton's avatar
Neil Turton committed
870

871
;----------------------------------------------------------------------------------------
872
; MemoryFreePoolLock - removed now that free pool is a PMP
873 874 875 876 877 878 879 880 881 882 883

;----------------------------------------------------------------------------------------
;PCImapping - reserved for Acorn use (PCI manager)
;
; See code on Ursula branch


;----------------------------------------------------------------------------------------
;RecommendPage
;
;       In:     r0 bits 0..7  = 12 (reason code 12)
884
;               r0 bit 8 = 1 if region must be DMAable
885 886
;               r0 bit 9 = 1 if r4-r7 provided
;               r0 bits 10..31 = 0 (reserved flags)
887 888
;               r1 = size of physically contiguous RAM region required (bytes)
;               r2 = log2 of required alignment of base of region (eg. 12 = 4k, 20 = 1M)
889 890
;               r4,r5 = lowest acceptable physical address (inclusive) (if bit 9 of r0 set)
;               r6,r7 = highest acceptable physical address (inclusive) (if bit 9 of r0 set)
891 892 893 894 895 896
;
;       Out:    r3 = page number of first page of recommended region that could be
;                    grown as specific pages by dynamic area handler (only guaranteed
;                    if grow is next page claiming operation)
;        - or error if not possible (eg too big, pages unavailable)
;
897 898 899 900
; Notes:
; * Default address range in r4-r7 is for the lower 4GB of physical space
; * The high address in r6,r7 is for the end of the memory block, not the start
;
901
RecommendPage ROUT
902
        Entry   "r0-r2,r4-r12"
903
        CMP     r2,#30
904 905 906
        BHI     RP_failed         ;refuse to look for alignments above 1G
        ANDS    r11,r0,#1:SHL:8   ;convert flag into something usable in the loop
        MOVNE   r11,#OSAddRAM_NoDMA
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
;
        TST     r0,#1:SHL:9       ;If no range specified, limit to lower 4GB
        MOVEQ   r10,#0
        MOVEQ   r12,#1:SHL:20
        BEQ     %FT10
        CMP     r5,#1:SHL:8
        BHS     RP_failed         ; LPAE/long descriptor format limits us to 40 bit physical addresses (although technically PhysRamTable can store 44 bit addresses)
        CMP     r7,#1:SHL:8       ; Clamp high address
        MOVCS   r7,#&FF
        MOVCS   r6,#-1
        LDR     lr,=4095
        ADD     r10,r4,lr         ; Round up low address
        MOV     r10,r10,LSR #12
        ORR     r10,r10,r5,LSL #20
        MOV     r12,r6,LSR #12    ; Round down high address
        ORR     r12,r12,r7,LSL #20
        ADD     r12,r12,#1        ; Make exclusive
10
925 926 927
;
        ADD     r1,r1,#&1000
        SUB     r1,r1,#1
928
        MOV     r1,r1,LSR #12     ;size rounded up to whole no. of pages
929
;
930 931
        SUBS    r2,r2,#12         ;log2 alignment, in terms of pages
        MOVLT   r2,#0             ;must be at least zero
932
        MOV     r0,#1
933 934 935 936 937
        MOV     r4,r0,LSL r2      ;required alignment, page units
;
        SUB     r12,r12,r1
        MOV     r12,r12,LSR r2
        MOV     r12,r12,LSL r2    ; Last acceptable block start address
938
;
Jeffrey Lee's avatar
Jeffrey Lee committed
939
        LDR     r0,=ZeroPage+PhysRamTable
940
        MOV     r3,#0            ;page number, starts at 0
Jeffrey Lee's avatar
Jeffrey Lee committed
941
        LDR     r5,=ZeroPage+CamEntriesPointer
942
        LDR     r5,[r5]
943
        ADD     r5,r5,#CAM_PageFlags ; [r5,<page no.>,LSL #3] addresses flags word in CAM
944 945 946 947 948
        LDMIA   r0!,{r7,r8}      ;address,size of video chunk (skip this one)
;
RP_nextchunk
        ADD     r3,r3,r8,LSR #12 ;page no. of first page of next chunk
        LDMIA   r0!,{r7,r8}      ;address,size of next physical chunk
949 950 951 952 953 954 955 956 957 958 959
; R0 -> PhysRamTable
; R1 = Required length in pages
; R2 = Required log2 alignment-12
; R3 = current phys page no.
; R4 = Required alignment, page units
; R5 -> CAM
; R7,R8 = Current PhysRamTable entry
; R10 = Low address limit
; R11 = Flags
; R12 = High address limit
; R6,R9 = spare
960 961
        CMP     r8,#0
        BEQ     RP_failed
962 963
        TST     r8,r11           ;ignore non-DMA regions if bit 8 of R0 was set
        BNE     RP_nextchunk
964
;
965
        MOV     r8,r8,LSR #12
966 967 968
        CMP     r7,r10
        ADDLO   r6,r10,r4
        ADDHS   r6,r7,r4
969
        MOV     r8,r8,LSL #12
970 971
        SUB     r6,r6,#1         ;round up
        MOV     r6,r6,LSR r2
972 973 974
        MOV     r6,r6,LSL r2     ;address of first page of acceptable alignment
        SUBS    lr,r12,r6
        BLS     RP_nextchunk     ;exceeded upper address limit
975
        SUB     r6,r6,r7         ;adjustment to first address of acceptable alignment
976
        CMP     r6,r8,LSR #12
977
        BHS     RP_nextchunk     ;negligible chunk
978 979 980 981 982
        ADD     r7,r3,r6         ;first page number of acceptable alignment
        RSB     r9,r6,r8,LSR #12 ;remaining size of chunk
        CMP     r9,lr
        ADDHI   r9,lr,r1         ;clamp effective chunk length if we're going to hit the upper address limit
        
983 984 985 986
;
;find first available page
RP_nextpage
        CMP     r9,r1
987
        BLO     RP_nextchunk      ;not enough pages left in chunk
988
        LDR     r6,[r5,r7,LSL #CAM_EntrySizeLog2] ;page flags from CAM
989 990
        ;must not be marked Unavailable or Required
        TST     r6,#PageFlags_Unavailable :OR: PageFlags_Required
991
        TSTEQ   r6,#PageFlags_Reserved
992 993 994
        BEQ     RP_checkotherpages
        CMP     r9,r4
        BLS     RP_nextchunk
995
        ADD     r7,r7,r4           ;next page of suitable alignment
996 997 998
        SUB     r9,r9,r4
        B       RP_nextpage
;
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
RP_nextpagecontinue
        ; r7 = start page, r6 = page that failed
        ; No point checking any of r7...r6 again, so skip ahead past r6
        SUB     r6,r6,r7           ;number of pages to skip (minus 1)
        ADD     r6,r6,r4
        MOV     r6,r6,LSR r2
        MOV     r6,r6,LSL r2       ;number to skip, rounded up by alignment
        CMP     r9,r6
        BLS     RP_nextchunk
        ADD     r7,r7,r6           ;next page of suitable alignment
        SUB     r9,r9,r6
        B       RP_nextpage
;
1012
RP_checkotherpages
1013 1014
        ADD     r6,r7,r1
        SUB     r6,r6,#1          ;last page required
1015
RP_checkotherpagesloop
1016 1017 1018
        LDR     lr,[r5,r6,LSL #CAM_EntrySizeLog2] ;page flags from CAM
        TST     lr,#PageFlags_Unavailable :OR: PageFlags_Required
        TSTEQ   lr,#PageFlags_Reserved
1019
        BNE     RP_nextpagecontinue
1020 1021
        SUB     r6,r6,#1
        CMP     r6,r7
1022 1023 1024 1025 1026
        BHI     RP_checkotherpagesloop
;
;success!
;
        MOV     r3,r7
1027
        Exit
1028 1029 1030

RP_failed
        MOV     r3,#0
1031
        ADR     r0,ErrorBlock_NoMemChunkAvailable
1032
        SETV
1033 1034
        FRAMSTR r0
        Exit
1035

1036
        MakeErrorBlock NoMemChunkAvailable
1037

1038 1039 1040 1041 1042
;----------------------------------------------------------------------------------------
;MapIOpermanent - map IO space (if not already mapped) and return logical address
;
;       In:     r0 bits 0..7  = 13 (reason code 13)
;               r0 bit  8     = 1 to map bufferable space (0 is normal, non-bufferable)
1043
;               r0 bit  9     = 1 to map cacheable space (0 is normal, non-cacheable)
1044
;               r0 bits 10..12 = cache policy
1045
;               r0 bits 13..15 = 0 (reserved flags)
Kevin Bracey's avatar
Kevin Bracey committed
1046
;               r0 bit  16    = 1 to doubly map
1047 1048 1049 1050
;               r0 bit  17    = 1 if access privileges specified
;               r0 bits 18..23 = 0 (reserved flags)
;               r0 bits 24..27 = access privileges (if bit 17 set)
;               r0 bits 28..31 = 0 (reserved flags)
1051 1052 1053 1054 1055 1056 1057
;               r1 = physical address of base of IO space required
;               r2 = size of IO space required (bytes)
;
;       Out:    r3 = logical address of base of IO space
;        - or error if not possible (no room)
;
MapIOpermanent ROUT
1058
        Entry   "r0-r2,r12"
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
        MOV     r3, r2
        MOV     r2, #0
        B       %FT10

;----------------------------------------------------------------------------------------
;MapIO64permanent - map IO space (if not already mapped) from 64-bit physical space
;and return logical address
;
;       In:     r0 bits 0..7  = 21 (reason code 21)
;               r0 bit  8     = 1 to map bufferable space (0 is normal, non-bufferable)
;               r0 bit  9     = 1 to map cacheable space (0 is normal, non-cacheable)
;               r0 bits 10..12 = cache policy
;               r0 bits 13..15 = 0 (reserved flags)
;               r0 bit  16    = 1 to doubly map
;               r0 bit  17    = 1 if access privileges specified
;               r0 bits 18..23 = 0 (reserved flags)
;               r0 bits 24..27 = access privileges (if bit 17 set)
;               r0 bits 28..31 = 0 (reserved flags)
;               r1,r2 = physical address of base of IO space required
;               r3 = size of IO space required (bytes)
;
;       Out:    r3 = logical address of base of IO space
;        - or error if not possible (no room)
;
MapIO64permanent
        ALTENTRY
10      ; Convert the input flags to some DA flags
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
        TST     r0, #1:SHL:17
        MOVEQ   r12, #2                 ; Default AP: SVC RW, USR none
        MOVNE   r12, r0, LSR #24        ; Else use given AP
        ANDNE   r12, r12, #DynAreaFlags_APBits
        AND     lr, r0, #&300
        EOR     lr, lr, #&300
        ASSERT  DynAreaFlags_NotBufferable = 1:SHL:4
        ASSERT  DynAreaFlags_NotCacheable = 1:SHL:5
        ORR     r12, r12, lr, LSR #4
        AND     lr, r0, #7:SHL:10
        ASSERT  DynAreaFlags_CPBits = 7:SHL:12
        ORR     r12, r12, lr, LSL #2
        ; Calculate the extra flags needed for RISCOS_MapInIO
        AND     r0, r0, #1:SHL:16
        ASSERT  MapInFlag_DoublyMapped = 1:SHL:20
        MOV     r0, r0, LSL #4
1102 1103
        ; Call RISCOS_MapInIO (via pagetable-specific helper)
        PTOp    MapIO_Helper
1104
        MOV     r3, r0
1105
        PullEnv
1106
        CMP     r3, #0              ;MOV,CMP rather than MOVS to be sure to clear V
1107 1108 1109
        ADREQ   r0, ErrorBlock_NoRoomForIO
        SETV    EQ
        MOV     pc, lr
1110

1111
        MakeErrorBlock NoRoomForIO
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134

;----------------------------------------------------------------------------------------
;AccessPhysAddr - claim temporary access to given physical address (in fact,
;                 controls access to the 1Mb aligned space containing the address)
;                 The access remains until the next AccessPhysAddr or until a
;                 ReleasePhysAddr (although interrupts or subroutines may temporarily
;                 make their own claims, but restore on Release before returning)
;
;       In:     r0 bits 0..7  = 14 (reason code 14)
;               r0 bit  8     = 1 to map bufferable space, 0 for unbufferable
;               r0 bits 9..31 = 0 (reserved flags)
;               r1 = physical address
;
;       Out:    r2 = logical address corresponding to phys address r1
;               r3 = old state (for ReleasePhysAddr)
;
; Use of multiple accesses: it is fine to make several Access calls, and
; clean up with a single Release at the end. In this case, it is the old state
; (r3) of the *first* Access call that should be passed to Release in order to
; restore the state before any of your accesses. (The r3 values of the other
; access calls can be ignored.)
;
AccessPhysAddr ROUT
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
        Push    "r0-r3,r12,lr"
        MOV     r2, #0
        B       %FT10

;----------------------------------------------------------------------------------------
;AccessPhysAddr64 - claim temporary access to given 64-bit physical address (in fact,
;                 controls access to the 1-16Mb aligned space containing the address)
;                 The access remains until the next AccessPhysAddr or until a
;                 ReleasePhysAddr (although interrupts or subroutines may temporarily
;                 make their own claims, but restore on Release before returning)
;
;       In:     r0 bits 0..7  = 22 (reason code 22)
;               r0 bit  8     = 1 to map bufferable space, 0 for unbufferable
;               r0 bits 9..31 = 0 (reserved flags)
;               r1,r2 = physical address
;
;       Out:    r2 = logical address corresponding to phys address r1
;               r3 = old state (for ReleasePhysAddr)
;
; Use of multiple accesses: it is fine to make several Access calls, and
; clean up with a single Release at the end. In this case, it is the old state
; (r3) of the *first* Access call that should be passed to Release in order to
; restore the state before any of your accesses. (The r3 values of the other
; access calls can be ignored.)
;
AccessPhysAddr64
        Push    "r0-r3,r12,lr"
10      TST     r0, #&100           ;test bufferable bit
1163 1164
        LDR     r0, =OSAP_None+DynAreaFlags_NotCacheable
        ORREQ   r0, r0, #DynAreaFlags_NotBufferable
1165
        SUB     sp, sp, #4          ; word for old state
1166
        MOV     r3, sp              ; pointer to word
1167
        PTOp    AccessPhysicalAddress
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
        MOVS    r2, r0              ; null pointer means invalid physical address
        LDMIB   sp, {r0,r1}
        BEQ     %FT90
        LDR     r3, [sp], #5*4      ; load old state, and skip stacked r0-r3
        Pull    "r12,pc"

90      ADRL    r0, ErrorBlock_CantGetPhysMem
        SETV
        ADD     sp, sp, #2*4
        Pull    "r1-r3,r12,pc"
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188

;----------------------------------------------------------------------------------------
;ReleasePhysAddr - release temporary access that was claimed by AccessPhysAddr
;
;       In:     r0 bits 0..7  = 15 (reason code 15)
;               r0 bits 8..31 = 0 (reserved flags)
;               r1 = old state to restore
;
ReleasePhysAddr
        Push    "r0-r3,r12,lr"
        MOV     r0, r1
1189
        PTOp    ReleasePhysicalAddress
1190 1191
        Pull    "r0-r3,r12,pc"

1192 1193
        LTORG

1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
;----------------------------------------------------------------------------------------
;
;        In:    r0 = flags
;                       bit     meaning
;                       0-7     16 (reason code)
;                       8-15    1=cursor/system/sound
;                               2=IRQ stack
;                               3=SVC stack
;                               4=ABT stack
;                               5=UND stack
;                               6=Soft CAM
;                               7=Level 1 page tables
;                               8=Level 2 page tables
;                               9=HAL workspace
;                               10=Kernel buffers
1209
;                               11=HAL uncacheable workspace
1210 1211 1212 1213
;                               12=Kernel 'ZeroPage' workspace
;                               13=Processor vectors
;                               14=DebuggerSpace
;                               15=Scratch space
1214
;                               16=Compatibility page
Jeffrey Lee's avatar
Jeffrey Lee committed
1215
;                               17=RW data section
Jeffrey Lee's avatar
Jeffrey Lee committed
1216
;                       16-31   reserved (set to 0)
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
;
;       Out:    r1 = base of area
;               r2 = address space allocated for area (whole number of pages)
;               r3 = actual memory used by area (whole number of pages)
;               all values 0 if not present, or incorporated into another area
;
;       Return size of various low-level memory regions
MemoryAreaInfo ROUT
        Entry   "r0"
        MOV     r1, #0
        MOV     r2, #0
        MOV     r3, #0
        MOV     lr, r0, LSR #8
        AND     lr, lr, #&FF
        CMP     lr, #(MAI_TableEnd - MAI_TableStart)/4
        ADDLO   pc, pc, lr, LSL #2
        B       %FT70
MAI_TableStart
        B       %FT70
        B       MAI_CursSysSound
        B       MAI_IRQStk
        B       MAI_SVCStk
        B       MAI_ABTStk
        B       MAI_UNDStk
        B       MAI_SoftCAM
        B       MAI_L1PT
        B       MAI_L2PT
        B       MAI_HALWs
        B       MAI_Kbuffs
1246
        B       MAI_HALWsNCNB
1247 1248 1249 1250
        B       MAI_ZeroPage
        B       MAI_ProcVecs
        B       MAI_DebuggerSpace
        B       MAI_ScratchSpace
1251
        B       MAI_CompatibilityPage
Jeffrey Lee's avatar
Jeffrey Lee committed
1252
        B       MAI_RWData
1253 1254 1255
MAI_TableEnd

70
1256 1257
        PullEnv
        B       MemoryBadParameters
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291

MAI_CursSysSound
        LDR     r1, =CursorChunkAddress
        MOV     r2, #32*1024
        MOV     r3, r2
        EXIT

MAI_IRQStk
 [ IRQSTK < CursorChunkAddress :LOR: IRQSTK > CursorChunkAddress+32*1024
        LDR     r1, =IRQStackAddress
        MOV     r2, #IRQSTK-IRQStackAddress
        MOV     r3, r2
 ]
        EXIT

MAI_SVCStk
        LDR     r1, =SVCStackAddress
        MOV     r2, #SVCSTK-SVCStackAddress
        MOV     r3, r2
        EXIT

MAI_ABTStk
        LDR     r1, =ABTStackAddress
        MOV     r2, #ABTSTK-ABTStackAddress
        MOV     r3, r2
        EXIT

MAI_UNDStk
        LDR     r1, =UNDSTK :AND: &FFF00000
        LDR     r2, =UNDSTK :AND: &000FFFFF
        MOV     r3, r2
        EXIT

MAI_SoftCAM
Jeffrey Lee's avatar
Jeffrey Lee committed
1292
        LDR     r0, =ZeroPage
1293
        LDR     r1, [r0, #CamEntriesPointer]
Jeffrey Lee's avatar
Jeffrey Lee committed
1294 1295
        LDR     r2, [r0, #SoftCamMapSize]
        MOV     r3, r2
1296 1297 1298
        EXIT

MAI_L1PT
1299 1300 1301 1302 1303 1304 1305 1306 1307
      [ LongDesc :LAND: ShortDesc
        PTWhich r1
        ; Combined L1PT & L2PT
        ASSERT  LL1PT = LL2PT+16*1024
        LDRNE   r1, =LL2PT
        MOVNE   r2, #16*1024+4096
        LDREQ   r1, =L1PT
        MOVEQ   r2, #16*1024
      ELIF LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
1308 1309 1310 1311 1312
        ; Combined L1PT & L2PT
        ASSERT  LL1PT = LL2PT+16*1024
        LDR     r1, =LL2PT
        MOV     r2, #16*1024+4096
      |
1313 1314
        LDR     r1, =L1PT
        MOV     r2, #16*1024
Jeffrey Lee's avatar
Jeffrey Lee committed
1315
      ]
1316 1317 1318 1319
        MOV     r3, r2
        EXIT

MAI_L2PT
Jeffrey Lee's avatar
Jeffrey Lee committed
1320
        LDR     r0, =ZeroPage
1321 1322 1323 1324 1325 1326 1327 1328
      [ LongDesc :LAND: ShortDesc
        PTWhich r1
        ; Actually L3PT
        LDRNE   r1, =LL3PT
        MOVNE   r2, #8*1024*1024
        LDREQ   r1, =L2PT
        MOVEQ   r2, #4*1024*1024
      ELIF LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
1329 1330 1331 1332
        ; Actually L3PT
        LDR     r1, =LL3PT
        MOV     r2, #8*1024*1024
      |
1333 1334
        LDR     r1, =L2PT
        MOV     r2, #4*1024*1024
Jeffrey Lee's avatar
Jeffrey Lee committed
1335 1336
      ]
        LDR     r3, [r0, #LxPTUsed]
1337 1338 1339
        EXIT

MAI_HALWs
Jeffrey Lee's avatar
Jeffrey Lee committed
1340
        LDR     r0, =ZeroPage
1341
        LDR     r1, =HALWorkspace
1342
        MOV     r2, #HALWorkspaceSize
1343 1344 1345
        LDR     r3, [r0, #HAL_WsSize]
        EXIT

1346
MAI_HALWsNCNB
Jeffrey Lee's avatar
Jeffrey Lee committed
1347
        LDR     r0, =ZeroPage
1348 1349 1350 1351 1352 1353 1354 1355
        LDR     r1, =HALWorkspaceNCNB
        MOV     r2, #32*1024
        LDR     r3, [r0, #HAL_Descriptor]
        LDR     r3, [r3, #HALDesc_Flags]
        ANDS    r3, r3, #HALFlag_NCNBWorkspace
        MOVNE   r3, r2
        EXIT

1356 1357 1358 1359 1360
MAI_Kbuffs
        LDR     r1, =KbuffsBaseAddress
        MOV     r2, #KbuffsMaxSize
        LDR     r3, =(KbuffsSize + &FFF) :AND: :NOT: &FFF
        EXIT
1361

1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392
MAI_ZeroPage
        LDR     r1, =ZeroPage
        MOV     r2, #16*1024
        MOV     r3, #16*1024
        EXIT

MAI_ProcVecs
      [ ZeroPage != ProcVecs
        LDR     r1, =ProcVecs
        MOV     r2, #4096
        MOV     r3, #4096
      ]
        EXIT

MAI_DebuggerSpace
        ; Only report if DebuggerSpace is a standalone page. The debugger module
        ; finds DebuggerSpace via OS_ReadSysInfo 6, this call is only for the
        ; benefit of the task manager.
      [ DebuggerSpace_Size >= &1000
        LDR     r1, =DebuggerSpace
        MOV     r2, #DebuggerSpace_Size
        MOV     r3, #DebuggerSpace_Size
      ]
        EXIT

MAI_ScratchSpace
        LDR     r1, =ScratchSpace
        MOV     r2, #16*1024
        MOV     r3, #16*1024
        EXIT

1393 1394 1395 1396
MAI_CompatibilityPage
      [ CompatibilityPage
        MOV     r1, #0
        MOV     r2, #4096
1397 1398
        LDR     r0, =ZeroPage
        LDRB    r3, [r0,#CompatibilityPageEnabled]
1399 1400 1401 1402 1403
        CMP     r3, #0
        MOVNE   r3, #4096
      ]
        EXIT

Jeffrey Lee's avatar
Jeffrey Lee committed
1404 1405 1406 1407 1408 1409 1410 1411 1412
MAI_RWData
        LDR     r1, =|Image$$RW$$Base|
        LDR     r2, =|Image$$ZI$$Limit|+&FFF
        SUB     r2, r2, r1
        BIC     r2, r2, #&FF
        BIC     r2, r2, #&F00
        MOV     r3, r2
        EXIT

1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
;----------------------------------------------------------------------------------------
;
;        In:    r0 = flags
;                       bit     meaning
;                       0-7     17 (reason code)
;                       8-31    reserved (set to 0)
;               r1 = AP number to start search from (0 to start enumeration)
;                    increment by 1 on each call to enumerate all values
;
;       Out:    r1 = AP number (-1 if end of list reached)
;               r2 = Permissions:
;               bit 0: executable in user mode
;               bit 1: writable in user mode
;               bit 2: readable in user mode
;               bit 3: executable in privileged modes
;               bit 4: writable in privileged modes
;               bit 5: readable in privileged modes
;               bits 6+: reserved
;
;       Returns permission information for a given AP / enumerates all AP
Jeffrey Lee's avatar
Jeffrey Lee committed
1433
        EXPORT  MemoryAccessPrivileges
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561
MemoryAccessPrivileges ROUT
        CMP     r0, #17
        BNE     MemoryBadParameters
        Entry   "r3-r4"
        LDR     r3, =ZeroPage
        MOV     lr, r1
        LDR     r3, [r3, #MMU_PPLAccess]
        ; Currently we don't have any gaps in the table, so we can just index the r1'th element (being careful to not go past the table end)
10
        LDR     r4, [r3], #4
        CMP     r4, #-1
        BEQ     %FT98
        SUBS    lr, lr, #1
        BGE     %BT10
        BL      PPL_CMA_to_RWX
        EXIT
98
        MOV     r1, #-1
        MOV     r2, #0
        EXIT

; In: r4 = CMA-style AP/PPL access flags (from MMU_PPLAccess)
; Out: r2 = RWX-style AP/PPL access flags (for OS_Memory 17/18)
PPL_CMA_to_RWX ROUT
        Entry
        AND     r2, r4, #CMA_Partially_UserR
        ASSERT  CMA_Partially_UserR = 1<<4
        ASSERT  MemPermission_UserR = 1<<2
        MOV     r2, r2, LSR #4-2
        AND     lr, r4, #CMA_Partially_UserW
        ASSERT  CMA_Partially_UserW = 1<<5
        ASSERT  MemPermission_UserW = 1<<1
        ORR     r2, r2, lr, LSR #5-1
        AND     lr, r4, #CMA_Partially_UserXN ; (internally, XN flags are stored inverted)
        ASSERT  CMA_Partially_UserXN = 1<<14
        ASSERT  MemPermission_UserX = 1<<0
        ORR     r2, r2, lr, LSR #14-0
        AND     lr, r4, #CMA_Partially_PrivR
        ASSERT  CMA_Partially_PrivR = 1<<6
        ASSERT  MemPermission_PrivR = 1<<5
        ORR     r2, r2, lr, LSR #6-5
        AND     lr, r4, #CMA_Partially_PrivW
        ASSERT  CMA_Partially_PrivW = 1<<7
        ASSERT  MemPermission_PrivW = 1<<4
        ORR     r2, r2, lr, LSR #7-4
        AND     lr, r4, #CMA_Partially_PrivXN
        ASSERT  CMA_Partially_PrivXN = 1<<15
        ASSERT  MemPermission_PrivX = 1<<3
        ORR     r2, r2, lr, LSR #15-3
        EXIT

;----------------------------------------------------------------------------------------
;
;        In:    r0 = flags
;                       bit     meaning
;                       0-7     18 (reason code)
;                       8-31    reserved (set to 0)
;               r1 = Permission flag values (as per OS_Memory 17)
;               r2 = Permission flag mask
;
;       Out:    r0 = AP number that gives closest permissions
;               r2 = Permission flags of that AP (== r1 if exact match)
;               Error if no suitable AP found
;
;       Searches for an AP where ((permissions AND r2) == r1), and which
;       grants the least extra permissions
;
;       Extra permissions are weighted as follows (least acceptable first):
;       * User write
;       * User execute
;       * User read
;       * Privileged write
;       * Privileged execute
;       * Privileged read
FindAccessPrivilege ROUT
        CMP     r0, #18 ; No extra flags in r0
        BICEQS  r0, r1, r2 ; r1 must be a subset of r2
        BICEQS  r0, r2, #63 ; Only 6 known permission flags
        BNE     MemoryBadParameters
        ; n.b. r0 is now 0
        Entry   "r3-r11"
        LDR     r3, =ZeroPage
        MOV     r5, r1
        LDR     r3, [r3, #MMU_PPLAccess]
        MOV     r6, r2
        MOV     r7, #-1 ; Best AP
        MOV     r8, #0 ; Best flags
        MOV     r9, #-1 ; Best difference
        ; Magic constants for weighting the difference
        LDR     r10, =(1<<1)+(1<<6)+(1<<12)+(1<<18)+(1<<24)+(1<<30)
        LDR     r11, =(MemPermission_PrivR<<1)+(MemPermission_PrivX<<6)+(MemPermission_PrivW<<12)+(MemPermission_UserR<<18)+(MemPermission_UserX<<24)+(MemPermission_UserW<<30)
10
        LDR     r4, [r3], #4
        CMP     r4, #-1
        BEQ     %FT50
        BL      PPL_CMA_to_RWX ; -> r2 = flags
        ; Check it satisfies the mask
        AND     lr, r2, r6
        CMP     lr, r5
        BNE     %FT40
        ; Calculate diff
        BIC     lr, r2, r6
        MUL     lr, r10, lr ; Replicate the six bits six times
        AND     lr, r11, lr ; Select just the bits that we care about
        CMP     lr, r9
        BEQ     %FT80       ; Exact match found
        MOVLO   r7, r0      ; Remember new result if better
        MOVLO   r8, r2
        MOVLO   r9, lr
40
        ADD     r0, r0, #1
        B       %BT10
50
        MOVS    r0, r7
        BMI     %FT90
        MOV     r2, r8
80
        CLRV
        EXIT

90
        MOV     r2, r6 ; Restore original r2
        ADR     r0, ErrorBlock_AccessPrivilegeNotFound
        SETV
        EXIT

        MakeErrorBlock AccessPrivilegeNotFound

1562 1563 1564 1565
;----------------------------------------------------------------------------------------
;
;        In:    r0 = flags
;                       bit     meaning
1566 1567 1568 1569
;                       0-7     19 (reason code)
;                       8       Input function provides physical addresses
;                       9       DMA is writing to RAM
;                       10      DMA is complete, perform any post-op cache maintenance
1570 1571
;                       11      Physical addresses are 64bit
;                       12-31   reserved (set to 0)
1572 1573
;               r1 = R12 value to provide to called functions
;               r2 = Initial R9 value to provide to input function
1574
;               r3 -> Input function
1575
;               r4 = Initial R9 value to provide to output function
1576
;               r5 -> Output function (if bit 10 of R0 clear)
1577 1578 1579 1580
;
;       Out:    r2, r4 updated to match values returned by input/output calls
;               All other regs preserved
;
1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
; Input function, 32bit version:
;  in:  r9 = r2 from SWI / value from previous call
;       r12 = r1 from SWI
;  out: r0 = start address of region
;       r1 = length of region (0 if end of transfer)
;       r2 = flags:
;            bit 0: Bounce buffer will be used
;       r9 = new r9 for next input call
;       r12 corrupt
;
; Output function, 32bit version:
;   in: r0 = logical address of start of region
;       r1 = physical address of start of region
;       r2 = length of region
;       r3 = flags:
;            bit 0: Bounce buffer must be used
;       r9 = r4 from SWI / value from previous call
;       r12 = r1 from SWI
;   out: r9 = new r9 value for next output call
;        r0-r3, r12 corrupt
;
; Input function, 64bit version:
;  in:  r9 = r2 from SWI / value from previous call
;       r12 = r1 from SWI
;  out: r0,r1 = start address of region
;       r2 = flags:
;            bit 0: Bounce buffer will be used
;       r3 = length of region (0 if end of transfer)
;       r9 = new r9 for next input call
;       r12 corrupt
;
; Output function, 64bit version:
;   in: r0 = logical address of start of region
;       r1,r2 = physical address of start of region
;       r3 = flags:
;            bit 0: Bounce buffer must be used
;       r4 = length of region
;       r9 = r4 from SWI / value from previous call
;       r12 = r1 from SWI
;   out: r9 = new r9 value for next output call
;        r0-r4, r12 corrupt
;
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
; Performs address translation and cache maintenance necessary to allow for DMA
; to be performed to/from cacheable memory.
;
; To allow Service_PagesUnsafe to be dealt with in a straightforward manner, we
; have to be careful not to cache the results of any address translations over
; calls to the input/output functions. E.g. if the output function tries to
; allocate from PCI RAM, that may trigger claiming of a specific page by the
; PCI DA, potentially invalidating any existing logical -> physical translation.
; This restriction hampers the routines ability to merge together input and
; output blocks, and to perform minimal cache maintenance. However for typical
; scatter lists of low to medium complexity it should still produce acceptable
; output.
;
; Note that if the input function provides physical addresses, the caller must
; take care to abort the entire operation if one of the physical pages involved
; in the request becomes claimed by someone else while the OS_Memory call is in
; progress. This is because we have no sensible way of dealing with this case
; ourselves (even if we didn't attempt to call the input function multiple times
; and merge together the blocks, we'd still have to buffer things internally to
; deal with when blocks need splitting for cache alignment)
;
; Internally, blocks are stored in the following format:
;
; Word 0 = Start logical address (incl.)
; Word 1 = Logical -> physical address offset (low bits) + flags (high bits)
; Word 2 = End logical address (excl.)
;
; This minimises the number of registers needed to hold a block, and simplifies
; the merge calculation (blocks can be merged if words 2 + 1 of first block
1652 1653 1654 1655 1656 1657
; match words 0 + 1 of second block).
;
; Note: InChunk uses a slightly different format, which essentially assumes a
; flat 1:1 logical to physical mapping. I.e. start & end addresses are in
; whatever unit the input function provided, and only the upper 8 bits of the
; log -> phys offset are used (storing the high bits of large phys addresses)
1658 1659 1660 1661 1662 1663 1664 1665 1666

; Workspace struct that's stored on the stack
                    ^ 0
DMAPrepW_InHold     # 12
DMAPrepW_InChunk    # 12
DMAPrepW_PhyChunk   # 12
DMAPrepW_CacheMask  # 4 ; Cache line length - 1
DMAPrepW_ARMop      # 4 ; Cache maintenenace ARMop to use
DMAPrepW_CamEntriesPointer # 4 ; CamEntriesPointer copy
1667
DMAPrepW_MaxCamEntry # 4 ; MaxCamEntry copy
1668 1669 1670 1671 1672 1673 1674 1675 1676
DMAPrepW_Size       # 0
                        ; These next few correspond directly to the input registers in the stack frame
DMAPrepW_Flags      # 4
DMAPrepW_R12        # 4
DMAPrepW_InR9       # 4
DMAPrepW_InFunc     # 4
DMAPrepW_OutR9      # 4
DMAPrepW_OutFunc    # 4

1677 1678
DMAPrep_FlagOffset * 28 ; We need 28 address bits for 40 bit physical addresses (dropping the lower 12 bits which provide the page offset)
DMAPrep_NonCacheable * 1:SHL:29 ; Internal flag used for tracking non-cacheable pages
1679 1680

DMAPrep ROUT
1681
        CMP     r0, #1<<12
1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699
        BHS     MemoryBadParameters
        ; The end of a read from RAM is a no-op (no cache maintenance required)
        AND     r11, r0, #DMAPrep_Write :OR: DMAPrep_End
        TEQ     r11, #DMAPrep_End
        MOVEQ   pc, lr
        Entry   "r0-r9", DMAPrepW_Size
        ; Determine the cache maintenance function we need to use
        CMP     r11, #DMAPrep_Write
        LDR     r10, =ZeroPage
        ASSERT  DMAPrep_End > DMAPrep_Write
        LDRLE   r11, [r10, #Proc_Cache_CleanRange] ; Start of DMA (read or write)
        LDRGT   r11, [r10, #Proc_Cache_InvalidateRange] ; End of DMA write
        STR     r11, [sp, #DMAPrepW_ARMop]
        ; Get the params needed for address translation
        LDR     r6, [r10, #CamEntriesPointer]
        LDR     r7, [r10, #MaxCamEntry]
        ; Init workspace
        STR     r6, [sp, #DMAPrepW_CamEntriesPointer]
1700
        STR     r7, [sp, #DMAPrepW_MaxCamEntry]
1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722
        ; Get the cache line mask value
      [ MEMM_Type == "ARM600"
        LDRB    r1, [r10, #DCache_LineLen]
      |
        ; Yuck, need to probe for the last cache level
        MOV     r5, #Cache_Lx_MaxLevel-1
01
        MOV     r1, r5
        ARMop   Cache_Examine,,,r10
        CMP     r1, #0
        SUBEQ   r5, r5, #1
        BEQ     %BT01
        CMP     r3, r1
        MOVHI   r1, r3
      ]
        SUB     r1, r1, #1
        STR     r1, [sp, #DMAPrepW_CacheMask]
        ; Get initial input region
        BL      DMAPrep_CallInputFunc
        CMP     r0, r3
        BEQ     %FT90
05
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734
        ; r0 > r3 implies the input crosses a 4G barrier. Barriers are annoying
        ; for us to deal with using this 3-word chunk format, so split things
        ; up.
        STMLOIA lr, {r0, r2, r3}
        BLO     %FT10
        MOV     r4, #0
        STMIA   lr, {r0, r2, r4}        ; First part
        CMP     r0, #0
        ADDNE   r2, r2, #1:SHL:20
        MOVNE   r0, #0                  ; Second part
        BLEQ    DMAPrep_CallInputFunc   ; Or, (non-merged) next chunk if we ended on a 4G barrier
        B       %FT19
1735 1736 1737 1738
10
        ; Get another input region, see if we can merge it with InChunk
        BL      DMAPrep_CallInputFunc
        CMP     r0, r3
1739
        BHS     %FT19                   ; Zero-length (end of input), or 4G crossing
1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750
        LDMIB   lr, {r4, r5}
        CMP     r4, r2
        CMPEQ   r5, r0
        STREQ   r3, [lr, #8]
        BEQ     %BT10
19
        ; Can't merge this region, store it in InHold
        ASSERT  DMAPrepW_InHold = DMAPrepW_InChunk-12
        STMDB   lr, {r0, r2, r3}
20
        ; Perform address translation for the start of InChunk
1751
        LDR     r5, [sp, #DMAPrepW_InChunk]
1752 1753 1754
        BL      DMAPrep_Translate
        ; Store in PhyChunk
        ADD     lr, sp, #DMAPrepW_PhyChunk
1755
        STMIA   lr, {r5-r7}
1756
        ; Align start to cache boundary
1757
        TST     r6, #DMAPrep_NonCacheable+(DMAPrep_UseBounceBuffer :SHL: DMAPrep_FlagOffset)
1758 1759 1760 1761
        BNE     %FT25
        LDR     lr, [sp, #DMAPrepW_Flags]
        LDR     r10, [sp, #DMAPrepW_CacheMask]
        TST     lr, #DMAPrep_Write
1762
        TSTNE   r5, r10
1763 1764
        BEQ     %FT25
        ; Unaligned write to cacheable memory -> bounce required
1765
        ADD     r1, r5, r10
1766 1767 1768 1769
        BIC     r1, r1, r10 ; End of current cache line
        ; Only round down to end of current cache line if the end of the chunk
        ; is at or beyond the end of the next cache line
        ADD     r2, r1, r10 ; Last byte we can accept without needing to truncate
1770 1771 1772
        CMP     r7, r2
        MOVHI   r7, r1 ; Truncate! N.B. this compare may break if we map memory at &FFFFF000
        ORR     r6, r6, #DMAPrep_UseBounceBuffer :SHL: DMAPrep_FlagOffset
1773 1774 1775 1776 1777
        B       %FT40
25
        ; Start doesn't need splitting, so translate + append more pages
        ADD     lr, sp, #DMAPrepW_InChunk
        ASSERT  DMAPrepW_PhyChunk = DMAPrepW_InChunk + 12
1778 1779
        LDMIA   lr, {r0-r2, r5-r7}
        SUB     r3, r7, r5 ; Length of translated region
1780 1781 1782
        SUB     r2, r2, r0 ; Length of input region
        CMP     r3, r2
        BEQ     %FT30
1783
        ADD     r5, r0, r3 ; Translate next address in input address space
1784 1785 1786 1787
        BL      DMAPrep_Translate
        ; Try and merge with PhyChunk
        ADD     lr, sp, #DMAPrepW_PhyChunk
        LDMIB   lr, {r0, r1}
1788 1789 1790
        CMP     r0, r6
        CMPEQ   r1, r5
        STREQ   r7, [sp, #DMAPrepW_PhyChunk + 8]
1791
        BEQ     %BT25
1792
        LDMIA   lr, {r5-r7}
1793
30
1794
        ; Can't merge any more pages into this chunk {r5-r7}
1795
        ; Truncate / bounce the end if necessary
1796
        TST     r6, #DMAPrep_NonCacheable+(DMAPrep_UseBounceBuffer :SHL: DMAPrep_FlagOffset)
1797 1798 1799 1800
        BNE     %FT50
        LDR     lr, [sp, #DMAPrepW_Flags]
        LDR     r10, [sp, #DMAPrepW_CacheMask]
        TST     lr, #DMAPrep_Write
1801
        TSTNE   r7, r10
1802 1803
        BEQ     %FT40
        ; Unaligned write to cacheable memory -> bounce required
1804 1805 1806 1807
        BIC     r3, r7, r10
        CMP     r3, r5
        ORREQ   r6, r6, #DMAPrep_UseBounceBuffer :SHL: DMAPrep_FlagOffset ; Bounce
        MOVNE   r7, r3 ; Truncate
1808 1809 1810
40
        ; Perform cache maintenance if necessary
        ; For safety we always perform this before calling the output function, rather than caching and attempting to merge the regions (output function may alter cacheability of pages?)
1811
        TST     r6, #DMAPrep_NonCacheable+(DMAPrep_UseBounceBuffer :SHL: DMAPrep_FlagOffset)
1812
        BNE     %FT50
1813 1814
        ADD     r1, r7, r10
        BIC     r0, r5, r10
1815 1816 1817 1818 1819 1820 1821 1822
        BIC     r1, r1, r10
        MOV     lr, pc
        LDR     pc, [sp, #DMAPrepW_ARMop]
50
        ; Call the output function
        LDR     lr, [sp, #DMAPrepW_Flags]
        TST     lr, #DMAPrep_End
        BNE     %FT60 ; No output func for end-of-op
1823 1824 1825 1826 1827 1828 1829
        MOV     r0, r5
        ADDS    r1, r5, r6, LSL #12
        MOV     r2, r6, LSR #20
        ADC     r2, r2, #0              ; Yuck, need to deal with carry propagation
        AND     r2, r2, #255            ; ... and keep modulo 2^40
        SUB     r4, r7, r5
        MOV     r3, r6, LSR #DMAPrep_FlagOffset
1830 1831
        LDR     r12, [sp, #DMAPrepW_R12]
        AND     r3, r3, #DMAPrep_UseBounceBuffer ; Mask out internal flags
1832 1833
        TST     lr, #DMAPrep_Phys64
        MOVEQ   r2, r4                  ; For the 32bit API, this will drop the high physical address bits. But that should be safe, since we force high addresses to use a bounce buffer (in which case the physical address *should* be completely ignored)
1834 1835 1836 1837 1838 1839 1840 1841
        ADD     r9, sp, #DMAPrepW_OutR9
        CLRV    ; Ensure V is clear on entry so simple functions don't confuse us
        MOV     lr, pc
        ASSERT  DMAPrepW_OutFunc = DMAPrepW_OutR9 + 4
        LDMIA   r9, {r9, pc}            ; Call output function
        STR     r9, [sp, #DMAPrepW_OutR9] ; Always write back updated R9
        BVS     %FT90
60
1842
        ; Advance InChunk by the length of {r5-r7}
1843
        LDR     r0, [sp, #DMAPrepW_InChunk]
1844
        ADD     r0, r0, r7
1845
        LDR     r1, [sp, #DMAPrepW_InChunk+8]
1846
        SUB     r0, r0, r5
1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875
        STR     r0, [sp, #DMAPrepW_InChunk]
        CMP     r0, r1
        BNE     %BT20
        ; InChunk depleted, copy InHold to InChunk and try for more input
        ADD     lr, sp, #DMAPrepW_InChunk
        ASSERT  DMAPrepW_InHold = 0
        LDMIA   sp, {r0,r2,r3}
        CMP     r0, r3
        BNE     %BT05
        ; InHold was empty, so no more regions to process
90
        FRAMSTR r0, VS
        EXIT

95
        ADRL    r0, ErrorBlock_BadAddress
        SETV
        B       %BT90

96
        PullEnv
        B       MemoryBadParameters

; Out: R0, R2, R3 = block
;      LR -> InChunk
;      R1, R4, R9, R12 corrupt
DMAPrep_CallInputFunc
        LDR     r12, [sp, #DMAPrepW_R12]
        ADD     r9, sp, #DMAPrepW_InR9
1876
        Push    "lr"
1877 1878 1879 1880
        CLRV    ; Ensure V is clear on entry so simple functions don't confuse us
        MOV     lr, pc
        ASSERT  DMAPrepW_InFunc = DMAPrepW_InR9 + 4
        LDMIA   r9, {r9, pc}            ; Call the input function
1881
        Pull    "r12"
1882 1883
        STR     r9, [sp, #DMAPrepW_InR9] ; Always write back updated R9
        BVS     %BT90
1884 1885 1886 1887 1888 1889 1890
        ; Shuffle registers if we're using the 32bit API
        LDR     r9, [sp, #DMAPrepW_Flags]
        TST     r9, #DMAPrep_Phys64
        MOVEQ   r3, r1
        MOVEQ   r1, #0
        CMP     r3, #0
        BEQ     %FT50
1891 1892
        CMP     r2, #DMAPrep_UseBounceBuffer
        BHI     %BT96
1893 1894
        CMP     r1, #255                ; Max 40 bit phys addr
        BHI     %BT95
1895 1896
        ; Pack into InChunk
        MOV     r2, r2, LSL #DMAPrep_FlagOffset
1897 1898 1899 1900 1901 1902 1903 1904
        ORR     r2, r2, r1, LSL #20
        ADD     lr, sp, #DMAPrepW_InChunk
        ADD     r3, r0, r3
        MOV     pc, r12
50
        ; End of input - just set everything to zero
        MOV     r0, #0
        MOV     r2, #0
1905
        ADD     lr, sp, #DMAPrepW_InChunk
1906
        MOV     pc, r12
1907 1908

; Translate the start of InChunk into a block
1909 1910 1911
; In: r5 = Address to translate
; Out: r5-r7 = block
;      r1, r3, r4, r8-r12 corrupt
1912 1913 1914
DMAPrep_Translate
        MOV     r1, lr
        LDR     r12, [sp, #DMAPrepW_InChunk+8]
1915
        SUB     r12, r12, r5            ; Length of input region (guaranteed 32bit)
1916 1917
        LDR     lr, [sp, #DMAPrepW_Flags]
        LDR     r6, [sp, #DMAPrepW_CamEntriesPointer]
1918 1919
        LDR     r7, [sp, #DMAPrepW_MaxCamEntry]
        LDR     r9, [sp, #DMAPrepW_InChunk+4]
1920 1921
        TST     lr, #DMAPrep_PhysProvided
        BNE     %FT20
1922 1923
        TST     r9, #255:SHL:20         ; Logical addresses must be 32bit!
        BNE     %BT95
1924 1925
      [ AMB_LazyMapIn
        MOV     r9, r0
1926
        MOV     r0, r5
1927 1928 1929
        BL      AMB_MakeHonestLA
        MOV     r0, r9
      ]
1930
        MOV     r4, r5
1931
        PTOp    logical_to_physical     ; r4 -> r8, r9
1932
        BLCC    physical_to_ppn         ; r7, r8, r9 -> r3, r11
1933
        BCS     %BT95
1934
        ; r5,r10 corrupt
1935 1936 1937 1938 1939
        ; Grab page flags
        ADD     lr, r6, r3, LSL #CAM_EntrySizeLog2
        LDR     lr, [lr, #CAM_PageFlags]
        B       %FT30
20
1940 1941 1942 1943 1944 1945 1946
        MOV     r8, r5
      [ NoARMT2
        MOV     r9, r9, LSR #20
        AND     r9, r9, #255
      |
        UBFX    r9, r9, #20, #8
      ]
1947
        BL      physical_to_ppn         ; r7, r8, r9 -> r3, r11
1948
        BCS     %BT95
1949
        ; r5, r10 corrupt
1950 1951 1952
        ; Manual ppn -> logical so we can get the page flags at the same time
        ; TODO this won't deal with mapped out pages in a sensible manner (will output them all individually)
      [ AMB_LazyMapIn
1953
        MOV     r10, r0
1954 1955
        MOV     r0, r3
        BL      AMB_MakeHonestPN
1956
        MOV     r0, r10
1957 1958 1959 1960 1961 1962
      ]
        ADD     lr, r6, r3, LSL #CAM_EntrySizeLog2
        ASSERT  CAM_LogAddr=0
        ASSERT  CAM_PageFlags=4
        LDMIA   lr, {r3, lr}
        ; Merge in the offset within the page
1963
      [ NoARMT2
1964
        MOV     r3, r3, LSR #12
1965
        ORR     r4, r3, r8, LSL #20
1966
        MOV     r4, r4, ROR #20
1967
      |
1968
        BFI     r3, r8, #0, #12
1969 1970
        MOV     r4, r3
      ]
1971
30
1972
        ; We now have r4 = log addr, r8,r9 = phys addr, lr = page flags, r11 = PhysRamTable flags
1973
        LDR     r3, [sp, #DMAPrepW_InChunk+4]
1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
        ; Combine the cacheability + phys offset into r6
        SUBS    r6, r8, r4              ; r6 = phys-log
        AND     r3, r3, #&FFFFFFFF:SHL:DMAPrep_FlagOffset ; Get the chunk flags
        ORR     r6, r3, r6, LSR #12
        SBC     r7, r9, #0
      [ NoARMT2
        AND     r7, r7, #255
        ORR     r6, r6, r7, LSL #20
      |
        BFI     r6, r7, #20, #8
      ]
1985
        TST     lr, #DynAreaFlags_NotCacheable
1986 1987 1988 1989 1990 1991 1992 1993
        ORRNE   r6, r6, #DMAPrep_NonCacheable
        ; For the 32bit API, any large phys addresses get forced to use bounce
        ; buffers. Force it here, so that the main logic will know not to bother
        ; with cache maintenance for the region.
        CMP     r9, #0
        LDRNE   lr, [sp, #DMAPrepW_Flags]
        EORNE   lr, lr, #DMAPrep_Phys64
        TSTNE   lr, #DMAPrep_Phys64
1994 1995 1996
        ; We also want to force bounce buffer usage for RAM chunks which have
        ; been flagged by the HAL as not supporting DMA
        TSTEQ   r11, #OSAddRAM_NoDMA
1997
        ORRNE   r6, r6, #DMAPrep_UseBounceBuffer:SHL:DMAPrep_FlagOffset
1998 1999 2000
        ; Work out how much of r12 fits into this page
        ; This is done by comparing against the length of the input region,
        ; since the input could be logical or physical
2001 2002 2003 2004 2005 2006 2007
        ADD     r7, r4, #4096
        MOV     r7, r7, LSR #12
        RSB     r7, r4, r7, LSL #12
        CMP     r7, r12
        MOVHI   r7, r12
        ADD     r7, r4, r7
        MOV     r5, r4
2008 2009
        MOV     pc, r1

2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
;----------------------------------------------------------------------------------------
;
;       In:     r0 = flags
;                       bit     meaning
;                       0-7     20 (reason code)
;                       8-31    reserved (set to 0)
;               r1 = 0 to disable compatibility page
;                    1 to enable compatibility page
;                    -1 to read state
;
;       Out:    r1 = new/current state:
;                    0 if disabled
;                    1 if enabled
;                    -1 if not supported
;
;       Controls the page zero compatibility page located at &0
;
;       If the compatibility page isn't supported, attempts to enable it will
;       silently fail, with a result of r1 = -1
;
ChangeCompatibility ROUT
        CMP     r1, #-1
        CMPNE   r1, #1
        CMPLS   r0, #255
        BHI     MemoryBadParameters
 [ :LNOT: CompatibilityPage
        MOV     r1, #-1
        MOV     pc, lr
 |
        Entry   "r0-r11", DANode_NodeSize
2040 2041 2042
        LDR     r12, =ZeroPage
        LDRB    r0, [r12, #CompatibilityPageEnabled]
        FRAMSTR r0,,r1 ; return pre-change state in r1 (will be updated later, as necessary)
2043 2044 2045
        CMP     r1, #-1
        CMPNE   r0, r1
        EXIT    EQ
2046 2047
        ; If we're attempting to enable it, make sure nothing else has mapped itself in to page zero
        CMP     r1, #0
2048 2049
        BEQ     %FT05
        MOV     r4, #0
2050
        PTOp    logical_to_physical
2051 2052 2053 2054
        MOVCC   r1, #-1
        FRAMSTR r1,CC
        EXIT    CC
05
2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075
        ; Set up temp DANode on the stack so we can use a Batcall to manage the mapping
        MOV     r2, sp
        MOV     r0, #DynAreaFlags_NotCacheable
        STR     r0, [r2, #DANode_Flags]
        MOV     r0, #0
        STR     r0, [r2, #DANode_Base]
        STR     r0, [r2, #DANode_Handler]
        CMP     r1, #1
        STREQ   r0, [r2, #DANode_Size]
        MOV     r0, #4096
        STRNE   r0, [r2, #DANode_Size]
        STR     r0, [r2, #DANode_MaxSize]
        MOV     r0, #ChangeDyn_Batcall
        MOV     r1, #4096
        RSBNE   r1, r1, #0
        SWI     XOS_ChangeDynamicArea
        FRAMSTR r0,VS
        EXIT    VS
        ; If we just enabled the page, fill it with the special value and then change it to read-only
        FRAMLDR r1
        RSBS    r1, r1, #1 ; invert returned state, to be correct for the above action
2076
        STRB    r1, [r12, #CompatibilityPageEnabled] ; Also update our state flag
2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088
        FRAMSTR r1
        EXIT    EQ
        MOV     r0, #0
        ADR     r1, %FT20
10
        CMP     r0, #%FT30-%FT20
        LDRLO   r2, [r1, r0]
        STR     r2, [r0], #4
        CMP     r0, #4096
        BNE     %BT10
        LDR     r7, [r12, #MaxCamEntry]
        MOV     r4, #0
2089
        PTOp    logical_to_physical
2090
        BL      physical_to_ppn
2091
        ; r5, r10-r11 corrupt, r3 = page number, r8,r9 = phys addr
2092 2093 2094 2095 2096 2097 2098
        MOV     r0, #OSMemReason_FindAccessPrivilege
        MOV     r1, #2_100100
        MOV     r2, #2_100100
        SWI     XOS_Memory ; Get AP number for read-only access (will make area XN on ARMv6+)
        ORRVC   r11, r0, #DynAreaFlags_NotCacheable
        MOVVC   r2, r3
        MOVVC   r3, #0
2099
        PTOpVC  BangCamUpdate
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112
        EXIT

20
        ; Pattern to place in compatibility page
        DCD     &FDFDFDFD ; A few of words of invalid addresses, which should also be invalid instructions on ARMv5 (ARMv6+ will have this page non-executable, ARMv4 and lower can't have high processor vectors)
        DCD     &FDFDFDFD
        DCD     &FDFDFDFD
        DCD     &FDFDFDFD
        = "!!!!NULL.POINTER.DEREFERENCE!!!!", 0 ; Readable message if interpretered as a string. Also, all words are unaligned pointers.
        ALIGN
        DCD     0 ; Fill the rest with zero (typically, most of ZeroPage is zero)
30
 ]
Jeffrey Lee's avatar
Jeffrey Lee committed
2113
        LTORG
2114

2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201
;----------------------------------------------------------------------------------------
;
;       In:     r0 = flags
;                       bit     meaning
;                       0-7     23 (reason code)
;                       8       0 = reserve, 1 = release reservation
;                       9-31    reserved (set to 0)
;               r1 = base page number
;               r2 = page count
;
;       Attempts to reserve (or remove the reservation) on a range of pages.
;       Dynamic areas can still use the memory, but only the code that reserved
;       it will be allowed to claim exclusive use over it (i.e. perform an
;       action that will cause PageFlags_Unavailable to be set)
;
;       This is useful for systems such as the PCI heap, where physically
;       contiguous memory is required, but the memory isn't needed all of the
;       time. By reserving the pages, it allows other regular DAs to make use
;       of the memory when the PCI heap is small. But when the PCI heap needs
;       to grow, it guarantees that (if there's enough free memory in the
;       system) the previously reserved pages can be allocated to the PCI heap.
;
;       Notes:
;
;       * Reservations are handled on an honour system; there's no checking
;         that the program that reserved the memory is the one attempting to
;         mark it Unavailable.
;       * For regular NeedsSpecificPages DAs, reserved pages can only be used
;         if the special "RESV" R0 return value is used (DAHandler_RESV)
;       * For PMP DAs, reserved pages can only be made Unavailable if the entry
;         in the page block also specifies the Reserved page flag. The actual
;         state of the Reserved flag can't be modified via PMP DA ops; the flag
;         is only used to indicate the caller's permission/intent to make an
;         already Reserved page Unavailable.
;       * If a PMP DA tries to make a Reserved page Unavailable without
;         specifying the Reserved flag, the kernel will try to swap it out for
;         a replacement page taken from the free pool (preserving the contents
;         and generating Service_PagesUnsafe / Service_PagesSafe, as if another
;         DA had claimed the page)
;
ReservePages ROUT
        Entry   "r1-r5"
        LDR     r3, =ZeroPage+CamEntriesPointer
        LDR     r4, [r3, #MaxCamEntry-CamEntriesPointer]
        LDR     r3, [r3]
        SUBS    r4, r4, r1
        SUBHSS  r4, r4, r2
        BLO     %FT90
        ADD     r3, r3, #CAM_PageFlags
        ADD     r3, r3, r1, LSL #CAM_EntrySizeLog2
        MOV     r5, r3
        TST     r0, #1:SHL:8
        BEQ     %FT20
10
        SUBS    r2, r2, #1
        EXIT    LO
        LDR     r4, [r5]
        BIC     r4, r4, #PageFlags_Reserved
        STR     r4, [r5], #CAM_EntrySize
        B       %BT10

20
        SUBS    r2, r2, #1
        EXIT    LO
        LDR     r4, [r5]
        TST     r4, #PageFlags_Unavailable  ; If already claimed
        TSTEQ   r4, #PageFlags_Reserved     ; Or already reserved
        BNE     %FT30                       ; Then complain
        ORR     r4, r4, #PageFlags_Reserved
        STR     r4, [r5], #CAM_EntrySize
        B       %BT20
        
30
        CMP     r3, r5
        LDRNE   r4, [r3]
        BICNE   r4, r4, #PageFlags_Reserved ; Remove reservations we just added
        STRNE   r4, [r3], #CAM_EntrySize
        BNE     %BT30
        ADRL    r0, ErrorBlock_CantGetPhysMem
        SETV
        EXIT

90
        ADRL    r0, ErrorBlock_BadParameters
        SETV
        EXIT

2202 2203 2204 2205
;----------------------------------------------------------------------------------------
;
;        In:    r0 = flags
;                       bit     meaning
2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221
;                       0-7     24 (reason code)
;                       8-31    reserved (set to 0)
;               r1 = low address (inclusive)
;               r2 = high address (exclusive)
;
;       Out:    r1 = access flags:
;               bit 0: completely readable in user mode
;               bit 1: completely writable in user mode
;               bit 2: completely readable in privileged modes
;               bit 3: completely writable in privileged modes
;               bit 4: partially readable in user mode
;               bit 5: partially writable in user mode
;               bit 6: partially readable in privileged modes
;               bit 7: partially writable in privileged modes
;               bit 8: completely physically mapped (i.e. IO memory)
;               bit 9: completely abortable (i.e. custom data abort handler)
2222 2223
;               bit 10: completely non-executable in user mode
;               bit 11: completely non-executable in privileged modes
2224 2225
;               bit 12: partially physically mapped
;               bit 13: partially abortable
2226 2227 2228
;               bit 14: partially non-executable in user mode
;               bit 15: partially non-executable in privileged modes
;               bits 16+: reserved
2229 2230
;
;       Return various attributes for the given memory region
2231 2232 2233

; NOTE: To make the flags easier to calculate, this routine calculates executability rather than non-executability. This means that unmapped memory has flags of zero. On exit we invert the sense of the bits in order to get non-executability (so that the public values are backwards-compatible with OS versions that didn't return executability information)
CMA_Completely_Inverted * CMA_Completely_UserXN + CMA_Completely_PrivXN
2234 2235

CMA_CheckL2PT          * 1<<31 ; Pseudo flag used internally for checking sparse areas
2236
CMA_DecodeAP           * 1<<30 ; Used with CheckL2PT to indicate AP flags should be decoded from L2PT
2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253

CheckMemoryAccess ROUT
        Entry   "r0,r2-r10"
        CMP     r0, #24
        BNE     %FT99
        LDR     r10, =ZeroPage
        ; Set all the 'completely' flags, we'll clear them as we go along
        LDR     r0, =&0F0F0F0F
        ; Make end address inclusive so we don't have to worry so much about
        ; wrap around at 4G
        TEQ     r1, r2
        SUBNE   r2, r2, #1
        ; Split memory up into five main regions:
        ; * scratchspace/zeropage
        ; * application space
        ; * dynamic areas
        ; * IO memory
2254
        ; * special areas (stacks, ROM, HAL workspace, etc.)
2255 2256 2257 2258 2259 2260 2261 2262 2263
        ; All ranges are checked in increasing address order, so the
        ; completeness flags are returned correctly if we happen to cross from
        ; one range into another
        ; Note that application space can't currently be checked in DA block as
        ; (a) it's not linked to DAList/DynArea_AddrLookup
        ; (b) we need to manually add the abortable flag
        CMP     r1, #32*1024
        BHS     %FT10
        ; Check zero page
2264
        ASSERT  ProcVecs = ZeroPage
2265
     [ ZeroPage = 0
2266 2267
        MOV     r3, #0
        MOV     r4, #16*1024
2268
        LDR     r5, =CMA_ZeroPage
2269
        BL      CMA_AddRange
2270 2271 2272
     |
      [ CompatibilityPage
        ; Zero page compatibility page
2273 2274
        LDR     r3, =ZeroPage
        LDRB    r3, [r3, #CompatibilityPageEnabled]
2275 2276 2277 2278 2279 2280 2281 2282 2283
        CMP     r3, #0
        BEQ     %FT05
        MOV     r3, #0
        MOV     r4, #4096
        ; This represents our ideal access flags; it may not correspond to reality
        LDR     r5, =CMA_Partially_UserR+CMA_Partially_PrivR
        BL      CMA_AddRange
05
      ]
2284 2285 2286 2287
        ; DebuggerSpace
        ASSERT  DebuggerSpace < ScratchSpace
        LDR     r3, =DebuggerSpace
        LDR     r4, =(DebuggerSpace_Size + &FFF) :AND: &FFFFF000
2288
        LDR     r5, =CMA_DebuggerSpace
2289
        BL      CMA_AddRange
2290
     ]
2291 2292 2293
        ; Scratch space
        LDR     r3, =ScratchSpace
        MOV     r4, #16*1024
2294
        LDR     r5, =CMA_ScratchSpace
2295 2296 2297 2298 2299 2300 2301 2302 2303 2304
        BL      CMA_AddRange
10
        ; Application space
        ; Note - checking AplWorkSize as opposed to AplWorkMaxSize to cope with
        ; software which creates DAs within application space (e.g. Aemulor)
        LDR     r4, [r10, #AplWorkSize]
        CMP     r1, r4
        BHS     %FT20
        LDR     r3, [r10, #AMBControl_ws]
        LDR     r3, [r3, #:INDEX:AMBFlags]
2305
        LDR     r5, =CMA_AppSpace
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317
        TST     r3, #AMBFlag_LazyMapIn_disable :OR: AMBFlag_LazyMapIn_suspend
        MOV     r3, #32*1024
        ORREQ   r5, r5, #CMA_Partially_Abort
        BL      CMA_AddRange2
20
        ; Dynamic areas
        LDR     r7, [r10, #IOAllocLimit]
        CMP     r1, r7
        BHS     %FT30
        ; Look through the quick lookup table until we find a valid DANode ptr
        LDR     r6, [r10, #DynArea_ws]
        MOV     r3, r1
2318
        TEQ     r6, #0 ; We can get called during ROM init, before the workspace is allocated (pesky OS_Heap validating its pointers)
2319
        ADD     r6, r6, #(:INDEX:DynArea_AddrLookup) :AND: &00FF
2320
        LDREQ   r9, [r10, #DAList] ; So just start at the first DA
2321
        ADD     r6, r6, #(:INDEX:DynArea_AddrLookup) :AND: &FF00
2322
        BEQ     %FT22
2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337
21
        AND     r8, r3, #DynArea_AddrLookupMask
        LDR     r9, [r6, r8, LSR #30-DynArea_AddrLookupBits]
        TEQ     r9, #0
        BNE     %FT22
        ; Nothing here, skip ahead to next block
        ADD     r3, r8, #DynArea_AddrLookupSize
        CMP     r3, r2
        BHI     %FT90 ; Hit end of search area
        CMP     r3, r7
        BLO     %BT21
        ; Hit end of DA area and wandered into IO area
        B       %FT30
22
        ; Now that we've found a DA to start from, walk through and process all
2338 2339
        ; the entries until we hit the end of the list, or any DAs above
        ; IOAllocLimit
2340 2341 2342 2343 2344
        LDR     r3, [r9, #DANode_Base]
        LDR     r6, [r9, #DANode_Flags]
        CMP     r3, r7
        BHS     %FT30
        ; Decode AP flags
2345 2346 2347
        LDR     r5, [r10, #MMU_PPLAccess]
        AND     lr, r6, #DynAreaFlags_APBits
        LDR     r5, [r5, lr, LSL #2]
2348 2349
        TST     r6, #DynAreaFlags_Abortable
        ORRNE   r5, r5, #CMA_Partially_Abort
2350 2351 2352
        TST     r6, #DynAreaFlags_PMP
        ORRNE   r5, r5, #CMA_DecodeAP
        TSTEQ   r6, #DynAreaFlags_SparseMap
2353
        LDREQ   lr, [r9, #DANode_Size]
2354
        LDRNE   r4, [r9, #DANode_SparseHWM] ; Use HWM as bounds when checking sparse/PMP areas
2355
        ORRNE   r5, r5, #CMA_CheckL2PT ; ... and request L2PT check
2356 2357
        ADDEQ   r4, r3, lr
        TST     r6, #DynAreaFlags_DoublyMapped ; Currently impossible for Sparse/PMP areas - so use of lr safe
2358
        SUBNE   r3, r3, lr
2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384
        TSTNE   r6, #DynAreaFlags_Abortable
        BEQ     %FT23
        ; Doubly-mapped abortable DA; make sure the unallocated area between
        ; MaxSize and Size is marked as abortable
        MOV     r12, r5
        MOV     r5, #CMA_Partially_Abort
        MOV     r10, r4
        LDR     r4, [r9, #DANode_MaxSize]
        SUB     r4, r4, lr ; MaxSize-Size
        SUB     r3, r3, r4
        BL      CMA_AddRange
        MOV     r3, r4
        MOV     r4, r10
        MOV     r5, r12
23
        ; Map the allocated part of the DA
        BL      CMA_AddRange2
        TST     r6, #DynAreaFlags_Abortable
        BEQ     %FT24
        ; Abortable DA; make sure the unallocated area between R4 and
        ; Base+MaxSize is marked as abortable
        MOV     r5, #CMA_Partially_Abort
        MOV     r3, r4
        LDR     r4, [r9, #DANode_Base]
        LDR     lr, [r9, #DANode_MaxSize]
        ADD     r4, r4, lr
2385
        BL      CMA_AddRange2
2386
24
2387 2388 2389
        LDR     r9, [r9, #DANode_Link]
        TEQ     r9, #0
        BNE     %BT22
2390
        ; Hit the end of the list
2391 2392
30
        ; IO memory
Jeffrey Lee's avatar
Jeffrey Lee committed
2393 2394
        LDR     r9, [r10, #IOAllocTop]
        CMP     r1, r9
2395
        BHS     %FT40
2396
        MOV     r6, r1, LSR #20
2397
        LDR     r4, [r10, #IOAllocPtr]
2398 2399 2400
        MOV     r6, r6, LSL #20 ; Get MB-aligned addr of first entry to check
        CMP     r6, r4
        MOVLO   r6, r4 ; Skip all the unallocated regions
2401
31
2402 2403
        Push    "r0-r2"
        MOV     r0, r6
2404
        PTOp    LoadAndDecodeL1Entry    ; TODO bit wasteful. We only care about access privileges, but this call gives us cache info too.
2405
        LDR     r5, [r10, #MMU_PPLAccess]
2406
        AND     lr, r2, #DynAreaFlags_APBits
2407
        LDR     r5, [r5, lr, LSL #2]
2408 2409 2410 2411 2412
        Pull    "r0-r2"
        SUB     lr, r3, #1
        ADD     r4, r6, r3
        BIC     r3, r6, lr              ; Aligned start addr
        BIC     r4, r4, lr              ; Aligned end addr
2413 2414
        ORR     r5, r5, #CMA_Partially_Phys
        BL      CMA_AddRange2
Jeffrey Lee's avatar
Jeffrey Lee committed
2415
        CMP     r4, r9
2416
        MOV     r6, r4
2417 2418 2419
        BNE     %BT31
40
        ; Everything else!
Jeffrey Lee's avatar
Jeffrey Lee committed
2420 2421 2422 2423 2424 2425
        ASSERT  CAMTop <= HALWorkspace
        LDR     r3, [r10, #CamEntriesPointer]
        LDR     r4, [r10, #SoftCamMapSize]
        LDR     r5, =CMA_CAM
        BL      CMA_AddRange
        ASSERT  HALWorkspace >= CAMTop
2426 2427
        LDR     r3, =HALWorkspace
        LDR     r4, [r10, #HAL_WsSize]
2428
        LDR     r5, =CMA_HALWorkspace
2429 2430 2431 2432
        BL      CMA_AddRange
        ASSERT  IRQStackAddress > HALWorkspace
        LDR     r3, =IRQStackAddress
        LDR     r4, =IRQStackSize
2433
        LDR     r5, =CMA_IRQStack
2434 2435 2436 2437
        BL      CMA_AddRange
        ASSERT  SVCStackAddress > IRQStackAddress
        LDR     r3, =SVCStackAddress
        LDR     r4, =SVCStackSize
2438
        LDR     r5, =CMA_SVCStack
2439 2440 2441 2442
        BL      CMA_AddRange
        ASSERT  ABTStackAddress > SVCStackAddress
        LDR     r3, =ABTStackAddress
        LDR     r4, =ABTStackSize
2443
        LDR     r5, =CMA_ABTStack
2444 2445 2446 2447
        BL      CMA_AddRange
        ASSERT  UNDStackAddress > ABTStackAddress
        LDR     r3, =UNDStackAddress
        LDR     r4, =UNDStackSize
2448
        LDR     r5, =CMA_UNDStack
2449
        BL      CMA_AddRange
2450
        ASSERT  DCacheCleanAddress > UNDStackAddress
2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463
        LDR     r4, =DCacheCleanAddress+DCacheCleanSize
        CMP     r1, r4
        BHS     %FT60
        ; Check that DCacheCleanAddress is actually used
        Push    "r0-r2,r9"
        AddressHAL r10
        MOV     a1, #-1
        CallHAL HAL_CleanerSpace
        CMP     a1, #-1
        Pull    "r0-r2,r9"
        BEQ     %FT60
        SUB     r3, r4, #DCacheCleanSize
        MOV     r4, #DCacheCleanSize
2464 2465
        ; Mark as IO, it may not be actual memory there
        LDR     r5, =CMA_DCacheClean+CMA_Partially_Phys
2466 2467 2468 2469 2470
        BL      CMA_AddRange
60
        ASSERT  KbuffsBaseAddress > DCacheCleanAddress
        LDR     r3, =KbuffsBaseAddress
        LDR     r4, =(KbuffsSize + &FFF) :AND: &FFFFF000
2471
        LDR     r5, =CMA_Kbuffs
2472 2473 2474 2475 2476 2477 2478 2479
        BL      CMA_AddRange
        ASSERT  HALWorkspaceNCNB > KbuffsBaseAddress
        LDR     r3, [r10, #HAL_Descriptor]
        LDR     r3, [r3, #HALDesc_Flags]
        TST     r3, #HALFlag_NCNBWorkspace
        BEQ     %FT70
        LDR     r3, =HALWorkspaceNCNB
        LDR     r4, =32*1024
2480
        LDR     r5, =CMA_HALWorkspaceNCNB
2481 2482
        BL      CMA_AddRange
70
2483 2484 2485 2486 2487
    [ LongDesc :LAND: ShortDesc
        PTWhich r3
        BEQ     %FT71
    ]
    [ LongDesc
Jeffrey Lee's avatar
Jeffrey Lee committed
2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499
        ASSERT  LL3PT > HALWorkspaceNCNB
        LDR     r3, =LL3PT
        MOV     r4, #8*1024*1024
        LDR     r5, =CMA_PageTablesAccess+CMA_CheckL2PT ; L3PT contains gaps due to logical indexing
        BL      CMA_AddRange
        ASSERT  LL2PT > LL3PT
        ASSERT  LL1PT = LL2PT+16*1024
        LDR     r3, =LL2PT
        MOV     r4, #16*1024+4096
        LDR     r5, =CMA_PageTablesAccess
        BL      CMA_AddRange
        ASSERT  CursorChunkAddress > LL1PT
2500 2501 2502 2503 2504 2505
      [ ShortDesc
        B       %FT72
71
      ]
    ]
    [ ShortDesc
2506
        ASSERT  L2PT > HALWorkspaceNCNB
2507 2508
        LDR     r3, =L2PT
        MOV     r4, #4*1024*1024
2509
        LDR     r5, =CMA_PageTablesAccess+CMA_CheckL2PT ; L2PT contains gaps due to logical indexing
2510 2511 2512 2513
        BL      CMA_AddRange
        ASSERT  L1PT > L2PT
        LDR     r3, =L1PT
        MOV     r4, #16*1024
2514
        LDR     r5, =CMA_PageTablesAccess
2515
        BL      CMA_AddRange
2516
        ASSERT  CursorChunkAddress > L1PT
2517 2518
72
    ]
2519 2520
        LDR     r3, =CursorChunkAddress
        MOV     r4, #32*1024
2521
        LDR     r5, =CMA_CursorChunk
2522
        BL      CMA_AddRange
Jeffrey Lee's avatar
Jeffrey Lee committed
2523 2524 2525 2526 2527 2528

        ASSERT  PhysicalAccess > CursorChunkAddress
        CMP     r1, #ROM
        BHS     %FT80
        Push    "r0-r2"
        LDR     r0, =PhysicalAccess
2529
        PTOp    LoadAndDecodeL1Entry
Jeffrey Lee's avatar
Jeffrey Lee committed
2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541
        CMP     r2, #-2
        AND     lr, r2, #DynAreaFlags_APBits
        Pull    "r0-r2"
        BHS     %FT80
        ADD     r4, r3, #PhysicalAccess
        LDR     r5, [r10, #MMU_PPLAccess]
        LDR     r3, =PhysicalAccess
        LDR     r5, [r5, lr, LSL #2]
        ORR     r5, r5, #CMA_Partially_Phys
        BL      CMA_AddRange2
80
        ASSERT  ROM > PhysicalAccess
2542 2543
        LDR     r3, =ROM
        LDR     r4, =OSROM_ImageSize*1024
2544
        LDR     r5, =CMA_ROM
2545
        BL      CMA_AddRange
Jeffrey Lee's avatar
Jeffrey Lee committed
2546 2547 2548 2549 2550 2551 2552
        ASSERT  RWBase > ROM
        LDR     r3, =RWBase
        LDR     r4, =|Image$$ZI$$Limit|+&FFF-RWBase
        BIC     r4, r4, #&FF
        BIC     r4, r4, #&F00
        LDR     r5, =CMA_RWArea
        BL      CMA_AddRange
2553
        ; Finally, high processor vectors/relocated zero page
2554
        ASSERT  ProcVecs = ZeroPage
2555 2556 2557 2558
      [ ZeroPage > 0
        ASSERT  ZeroPage > ROM
        MOV     r3, r10
        LDR     r4, =16*1024
2559
        LDR     r5, =CMA_ZeroPage
2560 2561 2562 2563 2564 2565
        BL      CMA_AddRange
      ]
90
        ; If there's anything else, we've wandered off into unallocated memory
        LDR     r3, =&0F0F0F0F
        BIC     r1, r0, r3
2566
        B       CMA_Done
2567 2568 2569

99
        PullEnv
2570
        B       MemoryBadParameters
2571 2572

        ; Add range r3..r4 to attributes in r0
2573
        ; Corrupts r8, exits with r4 = end addr
2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588
CMA_AddRange ROUT ; r3 = start, r4 = length
        ADD     r4, r3, r4
CMA_AddRange2 ; r3 = start, r4 = end (excl.)
        LDR     r8, =&0F0F0F0F
        ; Increment r1 and exit if we hit r2
        ; Ignore any ranges which are entirely before us
        CMP     r1, r4
        MOVHS   pc, lr
        ; Check for any gap at the start, i.e. r3 > r1
        CMP     r3, r1
        BICHI   r0, r0, r8
        MOVHI   r1, r3 ; Update r1 for L2PT check code
        ; Exit if the range starts after our end point
        CMP     r3, r2
        BHI     %FT10
2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603
    [ EmulateAP1
        Push    "r8-r9"
      [ ShortDesc
        PTWhich r8
        BEQ     %FT03
      ]
        ; Detect AP1 areas and flag them as UserXN + Abort
        LDR     r9, =CMA_Read :AND: :NOT: CMA_Partially_UserXN ; Might already be flagged as UserXN, depending on how the flags were fetched
        BIC     r8, r5, #CMA_Partially_UserXN+CMA_Partially_Phys
        TEQ     r8, r9
        BICEQ   r5, r5, #CMA_Partially_UserXN
        ORREQ   r5, r5, #CMA_Partially_Abort
03
        Pull    "r8-r9"
    ]
2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617
        ; Process the range
        TST     r5, #CMA_CheckL2PT
        BNE     %FT20
        CMP     r3, r4 ; Don't apply any flags for zero-length ranges
        ORR     r8, r5, r8
        ORRNE   r0, r0, r5 ; Set new partial flags
        ANDNE   r0, r0, r8, ROR #4 ; Discard completion flags which aren't for this range
05
        CMP     r4, r2
        MOV     r1, r4 ; Continue search from the end of this range
        MOVLS   pc, lr
10
        ; We've ended inside this range
        MOV     r1, r0
2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629
CMA_Done
        ; Invert the sense of the executability flags
        ;               Completely_X Partially_X -> Completely_XN Partially_XN
        ; Completely X             1           1                0            0
        ; Partially X              0           1                0            1
        ; XN                       0           0                1            1
        ; I.e. swap the positions of the two bits and invert them
        EOR     r0, r1, r1, LSR #4      ; Completely EOR Partially
        MVN     r0, r0                  ; Invert as well as swap
        AND     r0, r0, #CMA_Completely_Inverted ; Only touch these bits
        EOR     r1, r1, r0              ; Swap + invert Completely flags
        EOR     r1, r1, r0, LSL #4      ; Swap + invert Partially flags
2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645
        CLRV
        EXIT

20
        ; Check L2PT for sparse region r1..min(r2+1,r4)
        ; r4 guaranteed page aligned
        CMP     r3, r4
        BIC     r5, r5, #CMA_CheckL2PT
        BEQ     %BT05
        Push    "r2,r4,r5,r8,r9,r10,lr"
        LDR     lr, =&FFF
        CMP     r4, r2
        ADDHS   r2, r2, #4096
        BICHS   r2, r2, lr
        MOVLO   r2, r4
        ; r2 is now page aligned min(r2+1,r4)
2646 2647 2648
        TST     r5, #CMA_DecodeAP
        BIC     r4, r1, lr
        BNE     %FT35
2649 2650
        MOV     r10, #0
30
2651
        PTOp    logical_to_physical
2652 2653 2654 2655 2656
        ORRCC   r10, r10, #1
        ADD     r4, r4, #4096
        ORRCS   r10, r10, #2
        CMP     r4, r2
        BNE     %BT30
2657
        Pull    "r2,r4,r5,r8"
2658 2659 2660 2661
        CMP     r10, #2
        ; 01 -> entirely mapped
        ; 10 -> entirely unmapped
        ; 11 -> partially mapped
2662 2663 2664 2665 2666 2667 2668 2669 2670
        MOVNE   r9, r5
        ANDEQ   r9, r5, #CMA_Partially_Abort ; Completely unmapped, only flag as abortable
        ANDHI   r10, r5, #CMA_Partially_Abort ; Partially mapped, retain the
        ORRHI   r8, r8, r10                   ; abortable completion flag
        ORR     r8, r9, r8
        ORR     r0, r0, r9 ; Set new partial flags
        AND     r0, r0, r8, ROR #4 ; Discard completion flags which aren't for this range
        Pull    "r9,r10,lr"
        B       %BT05
2671

2672 2673
35
        ; Check L2PT, with AP decoding on a per-page basis
2674
        AND     r10, r5, #CMA_Partially_Abort
2675
40
2676
        PTOp    logical_to_physical
2677 2678
        LDR     r8, =&0F0F0F0F
        MOVCS   r5, r10 ; Unmapped page, only take the abortable flag
2679 2680
        BCS     %FT45
        ; Get the L2PT entry and decode the flags
2681
        Push    "r0-r3"
2682
        MOV     r0, r4
2683
        PTOp    LoadAndDecodeL2Entry    ; TODO bit wasteful. We only care about access privileges, but this call gives us cache info too. Also, if we know the L2PT backing exists (it should do) we could skip the logical_to_physical call
2684
        ; r2 = DA flags
2685 2686 2687
        ; Extract and decode AP
        LDR     r0, =ZeroPage
        LDR     r5, [r0, #MMU_PPLAccess]
2688
        AND     lr, r2, #DynAreaFlags_APBits
2689
        LDR     r5, [r5, lr, LSL #2]
2690
        ORR     r5, r5, r10 ; Merge in any abortability flag from the caller
2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701
    [ EmulateAP1
      [ ShortDesc
        PTWhich r0
        BEQ     %FT42
      ]
        ; Detect AP1 areas and them as UserXN + Abort
        TEQ     lr, #OSAP_Read
        BICEQ   r5, r5, #CMA_Partially_UserXN
        ORREQ   r5, r5, #CMA_Partially_Abort
42
    ]
2702
        Pull    "r0-r3"
2703
45
2704 2705 2706
        ORR     r8, r5, r8
        ORR     r0, r0, r5 ; Set new partial flags
        AND     r0, r0, r8, ROR #4 ; Discard completion flags which aren't for this range
2707 2708 2709 2710 2711 2712
        ADD     r4, r4, #4096
        CMP     r4, r2
        BNE     %BT40
        Pull    "r2,r4,r5,r8,r9,r10,lr"
        B       %BT05

2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747
;----------------------------------------------------------------------------------------
;
;       In:     r0 = flags
;                       bit     meaning
;                       0-7     65 (reason code)
;                       8-31    reserved (set to 0)
;               r1 = logical address
;
;       Out:    r0,r1 = physical address
;               r2 = size/alignment of mapping
;               For invalid addresses:
;               r0 = "Address not recognised" error
;               r1 corrupt
;               r2 = size/alignment of mapping (so caller knows how much
;                    to skip)
;
;       Convert a logical address to a physical address. Supports all page types
;       (unlike other logical -> physical SWIs, which only cope with regular
;       4KB RAM pages).
;
MemoryLogToPhys ROUT
        CMP     r0, #OSMemReason_LogToPhys
        BNE     %FT99
        Entry   "r3"
        MOV     r0, r1
        BL      RISCOS_LogToPhys
        MOV     r2, r3
        CMP     r0, #-1
        CMPEQ   r1, #-1
        ADREQL  r0, ErrorBlock_BadAddress
        SETV    EQ
        EXIT
99
        B       MemoryBadParameters

2748 2749
        LTORG

Neil Turton's avatar
Neil Turton committed
2750
        END