Commit fccd5e2f authored by Jeffrey Lee's avatar Jeffrey Lee Committed by ROOL
Browse files

Add abortable DA support

This implementation should be compatible with RISCOS Ltd's
implementation.
parent 5b6c1710
REM Copyright (c) 2021, RISC OS Open Ltd
REM All rights reserved.
REM
REM Redistribution and use in source and binary forms, with or without
REM modification, are permitted provided that the following conditions are met:
REM * Redistributions of source code must retain the above copyright
REM notice, this list of conditions and the following disclaimer.
REM * Redistributions in binary form must reproduce the above copyright
REM notice, this list of conditions and the following disclaimer in the
REM documentation and/or other materials provided with the distribution.
REM * Neither the name of RISC OS Open Ltd nor the names of its contributors
REM may be used to endorse or promote products derived from this software
REM without specific prior written permission.
REM
REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
REM AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
REM IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
REM ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
REM LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
REM CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
REM SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
REM INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
REM CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
REM ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
REM POSSIBILITY OF SUCH DAMAGE.
REM Test abortable DAs
REM
REM Test code itself is just a copy of attest_ap (test different access
REM permissions).
ON ERROR ERROR EXT 0,REPORT$+" at "+STR$(ERL)
da_size%=3*4096
SYS "OS_Module",6,,,4096+da_size% TO ,,rma%
FOR pass=0 TO 2 STEP 2
P%=rma%
[ OPT pass
.da_handler%
; R0 = DA handler reason, 5 = aborttrap
; R1 = flags
; R2 = buffer
; R3 = required address
; R4 = length
; R5 = current end of DA (nonsense for sparse DAs)
; R12 = param
STMFD R13!,{R0-R6,R14}
ADR R14,last_call%
MRS R6,CPSR
STMIA R14,{R0-R5,R6,R12}
CMP R0,#4 ; Normal DA resize op?
LDMLOFD R13!,{R0-R6,PC} ; Allow it
CMP R0,#5 ; Abort op?
BNE return_bad_reason
AND R1,R1,#15
CMP R1,#2
BHS return_bad_reason
ADR R5,buffer%
LDR R6,da_base_ptr%
SUB R3,R3,R6
ADD R3,R3,R5
TST R1,#1
EORNE R2,R2,R3 ; R2^R3, R3
EORNE R3,R3,R2 ; R2^R3, R2
EORNE R2,R2,R3 ; R3, R2
.loop%
SUBS R4,R4,#1
LDRB R6,[R2],#1
STRB R6,[R3],#1
BNE loop%
LDMFD R13!,{R0-R6,PC}
.return_bad_reason
LDMFD R13!,{R0-R6,R14}
MSR CPSR_f,#(1<<28)
ADR R0,bad_reason
MOV PC,LR
.bad_reason
EQUD 0
EQUS "Bad reason" : EQUB 0
ALIGN
.writew_svc%
SWI "OS_EnterOS"
STR R1,[R0]
SWI "OS_LeaveOS"
MOV PC,R14
.readb_svc%
SWI "OS_EnterOS"
LDRB R0,[R0]
SWI "OS_LeaveOS"
MOV PC,R14
.readw_svc%
SWI "OS_EnterOS"
LDR R0,[R0]
SWI "OS_LeaveOS"
MOV PC,R14
.read_sctlr%
SWI "OS_EnterOS"
MRC CP15,0,R0,C1,C0,0
SWI "OS_LeaveOS"
MOV PC,R14
.ldm_usr%
ADR R1, ldm_buf%
LDMIA R0,{R2-R3}
STMIA R1,{R2-R3}
MOV PC,R14
.stm_usr%
ADR R1, ldm_buf%
LDMIA R1,{R2-R3}
STMIA R0,{R2-R3}
MOV PC,R14
.ldm_svc%
SWI "OS_EnterOS"
ADR R1, ldm_buf%
LDMIA R0,{R2-R3}
STMIA R1,{R2-R3}
SWI "OS_LeaveOS"
MOV PC,R14
.stm_svc%
SWI "OS_EnterOS"
ADR R1, ldm_buf%
LDMIA R1,{R2-R3}
STMIA R0,{R2-R3}
SWI "OS_LeaveOS"
MOV PC,R14
.last_call%
.last_R0% EQUD 0
.last_R1% EQUD 0
.last_R2% EQUD 0
.last_R3% EQUD 0
.last_R4% EQUD 0
.last_R5% EQUD 0
.last_PSR% EQUD 0
.last_R12% EQUD 0
.da_base_ptr% EQUD 0
.ldm_buf% EQUD 0 : EQUD 0
.buffer%
]
NEXT pass
seed%=-TIME
PRINT "seed: ";seed%
A%=RND(seed%)
wp%=RND
da_num%=0
ON ERROR PRINT REPORT$;" at ";ERL : PROCend(1)
PRINT "handler: ";~da_handler%
PRINT "wp: ";~wp%
PRINT "last_call: ";~last_call%
DIM expected% 8
REM Offsets to test:
REM
REM Start of mapped page, middle of mapped page, end of mapped page
DATA 4096-4,4096+2048,8192-4,-1
ap%=0
REPEAT
SYS "OS_Memory",17,ap% TO ,ap%,permissions%
IF ap%=-1 THEN PROCend(0)
PROCtestap
PROCendtest
ap%+=1
UNTIL FALSE
DEF PROCtestap
PRINT "ap ";ap%;" permissions ";~permissions%
REM Must be readable in SVC mode
IF (permissions% AND &20)<>&20 THEN PRINT "Never readable?" : ENDPROC
!last_R0%=0
!last_R1%=0
!last_R2%=0
!last_R3%=0
!last_R4%=0
!last_R5%=0
!last_PSR%=0
!last_R12%=0
REM Create a sparse abortable DA
SYS "OS_DynamicArea",0,-1,0,-1,ap%+(1<<7)+(1<<10)+(1<<16),da_size%,da_handler%,wp%,"ATTest" TO ,da_num%,,da_base%
!da_base_ptr%=da_base%
PRINT "da_base: ";~da_base%
REM Map in the middle page. We'll perform accesses which cross from the mapped
REM page into the unmapped area around it, to check that page access is
REM is checked on a per-page basis instead of sending everything through our
REM AbortTrap handler
SYS "OS_DynamicArea",9,da_num%,da_base%+4096,4096
REM Report memory permissions
PROCcheckvalid
REM Fill sparse page with test data (if writable in SVC, else just use whatever's currently there)
IF permissions% AND &10 THEN PROCfill(da_base%+4096,4096)
REM Fill buffer with test data
PROCfill(buffer%,da_size%)
REM perform the access tests
RESTORE
READ offset%
REPEAT
PROCexpected(offset%,8,4)
A%=da_base%+offset%
CALL ldm_usr%
IF expected%!0<>ldm_buf%!0 OR expected%!4<>ldm_buf%!4 THEN PRINT "USR LDM ERROR @ ";~offset%;" expected ";~(expected%!0);" ";~(expected%!4);" actual ";~(ldm_buf%!0);" ";~(ldm_buf%!4) : PROCend(1)
PROCexpected(offset%,8,32)
A%=da_base%+offset%
CALL ldm_svc%
IF expected%!0<>ldm_buf%!0 OR expected%!4<>ldm_buf%!4 THEN PRINT "SVC LDM ERROR @ ";~offset%;" expected ";~(expected%!0);" ";~(expected%!4);" actual ";~(ldm_buf%!0);" ";~(ldm_buf%!4) : PROCend(1)
ldm_buf%!0=RND
ldm_buf%!4=RND
A%=da_base%+offset%
CALL stm_usr%
PROCexpected(offset%,8,2)
REM "expected" and "actual" are reversed here, because we're using FNexpected to read back what's been written to the memory
IF expected%!0<>ldm_buf%!0 OR expected%!4<>ldm_buf%!4 THEN PRINT "USR STM ERROR @ ";~offset%;" expected ";~(ldm_buf%!0);" ";~(ldm_buf%!4);" actual ";~(expected%!0);" ";~(expected%!4) : PROCend(1)
ldm_buf%!0=RND
ldm_buf%!4=RND
A%=da_base%+offset%
CALL stm_svc%
PROCexpected(offset%,8,16)
REM "expected" and "actual" are reversed here, because we're using FNexpected to read back what's been written to the memory
IF expected%!0<>ldm_buf%!0 OR expected%!4<>ldm_buf%!4 THEN PRINT "SVC STM ERROR @ ";~offset%;" expected ";~(ldm_buf%!0);" ";~(ldm_buf%!4);" actual ";~(expected%!0);" ";~(expected%!4) : PROCend(1)
READ offset%
UNTIL offset%=-1
ENDPROC
DEF PROCendtest
IF da_num%<>0 THEN PROClast : SYS "OS_DynamicArea",1,da_num% : da_num%=0
ENDPROC
DEF PROCend(E%)
PROCendtest
SYS "OS_Module",7,,rma%
IF E% THEN ERROR EXT 0,"Failed"
PRINT "Success"
END
ENDPROC
DEF PROClast
PRINT "last R0 ";~!last_R0%;" R1 ";~!last_R1%;" R2 ";~!last_R2%;" R3 ";~!last_R3%;" R4 ";~!last_R4%;" R5 ";~!last_R5%;" PSR ";~!last_PSR%;" R12 ";~!last_R12%
ENDPROC
DEF PROCfill(base%,len%)
WHILE len%>0
A%=base%
B%=RND
CALL writew_svc%
base%+=4
len%-=4
ENDWHILE
ENDPROC
DEF FNexpected(addr%,access%)
IF addr%<da_base% OR addr%>=da_base%+da_size% THEN PRINT "Bad addr ";~addr% : PROCend(1)
addr%-=da_base%
IF (permissions% AND access%)=access% AND addr%>=4096 AND addr%<8192 THEN A%=da_base%+addr% : =USR readb_svc%
=buffer%?addr%
DEF PROCcheckvalid
PROCcheckpage("Low",0)
PROCcheckpage("Mid",4096)
PROCcheckpage("High",8192)
ENDPROC
DEF PROCcheckpage(name$,offset%)
LOCAL flags%,access%
SYS "OS_ValidateAddress",da_base%+offset%,da_base%+offset%+4096 TO ;flags%
SYS "OS_Memory",24,da_base%+offset%,da_base%+offset%+4096 TO ,access%
PRINT name$;" valid: ";((NOT flags%) AND 2);" ";~access%
ENDPROC
DEF PROCexpected(offset%,len%,access%)
WHILE len%>0
len%-=1
expected%?len%=FNexpected(da_base%+offset%+len%,access%)
ENDWHILE
ENDPROC
REM Copyright (c) 2021, RISC OS Open Ltd
REM All rights reserved.
REM
REM Redistribution and use in source and binary forms, with or without
REM modification, are permitted provided that the following conditions are met:
REM * Redistributions of source code must retain the above copyright
REM notice, this list of conditions and the following disclaimer.
REM * Redistributions in binary form must reproduce the above copyright
REM notice, this list of conditions and the following disclaimer in the
REM documentation and/or other materials provided with the distribution.
REM * Neither the name of RISC OS Open Ltd nor the names of its contributors
REM may be used to endorse or promote products derived from this software
REM without specific prior written permission.
REM
REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
REM AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
REM IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
REM ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
REM LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
REM CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
REM SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
REM INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
REM CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
REM ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
REM POSSIBILITY OF SUCH DAMAGE.
REM Test AbortTrap serious error handling (data aborts, via an abortable DA)
ON ERROR ERROR EXT 0,REPORT$+" at "+STR$(ERL)
da_size%=4096
SYS "OS_Module",6,,,1024 TO ,,rma%
FOR pass=0 TO 2 STEP 2
P%=rma%
[ OPT pass
.da_handler%
; R0 = DA handler reason, 5 = aborttrap
; R1 = flags
; R2 = buffer
; R3 = required address
; R4 = length
; R5 = current end of DA (nonsense for sparse DAs)
; R12 = param
STMFD R13!,{R0-R6,R14}
ADR R14,last_call%
MRS R6,CPSR
STMIA R14,{R0-R5,R6,R12}
CMP R0,#4 ; Normal DA resize op?
LDMLOFD R13!,{R0-R6,PC} ; Allow it
CMP R0,#5 ; Abort op?
BNE return_bad_reason
LDMFD R13!,{R0-R6,R14}
MSR CPSR_f,#(1<<28)
ADR R0,serious_error
MOV PC,LR
.return_bad_reason
LDMFD R13!,{R0-R6,R14}
MSR CPSR_f,#(1<<28)
ADR R0,bad_reason
MOV PC,LR
.bad_reason
EQUD 0
EQUS "Bad reason" : EQUB 0
ALIGN
.serious_error
EQUD &80000000
EQUS "Test serious error" : EQUB 0
ALIGN
.testcode%
STR R13,temp_r13%
STR R14,temp_r14%
ADR R0,in_regs%
LDMIA R0,{R0-R14}
.testinstr%
LDR R0,[R0]
; If we're still here, the abort didn't trigger
LDR R13,temp_r13%
LDR PC,temp_r14%
.temp_r13% EQUD 0
.temp_r14% EQUD 0
.last_call%
.last_R0% EQUD 0
.last_R1% EQUD 0
.last_R2% EQUD 0
.last_R3% EQUD 0
.last_R4% EQUD 0
.last_R5% EQUD 0
.last_PSR% EQUD 0
.last_R12% EQUD 0
.in_regs%
]
NEXT pass
seed%=-TIME
PRINT "seed: ";seed%
A%=RND(seed%)
wp%=RND
da_num%=0
ON ERROR PROCerror
PRINT "handler: ";~da_handler%
PRINT "wp: ";~wp%
PRINT "last_call: ";~last_call%
!last_R0%=0
!last_R1%=0
!last_R2%=0
!last_R3%=0
!last_R4%=0
!last_R5%=0
!last_PSR%=0
!last_R12%=0
REM Create an abortable DA
SYS "OS_DynamicArea",0,-1,0,-1,(1<<16),da_size%,da_handler%,wp%,"ATTest" TO ,da_num%,,da_base%
PRINT "da_base: ";~da_base%
REM Randomise input regs
FOR A=1 TO 13
in_regs%!(A*4)=RND
NEXT A
in_regs%!0=(RND AND &FFC)+da_base%
REM Do the test
CALL testcode%
REM We shouldn't be here!
PRINT "Failed"
PROCend(1)
DEF PROCerror
IF REPORT$="Test serious error" THEN
PROCcheckresult
ELSE
PRINT REPORT$;" at ";ERL : PROCend(1)
ENDIF
ENDPROC
DEF PROCcheckresult
REM Check registers match
in_regs%!(15*4)=testinstr%+8
SYS "OS_ChangeEnvironment",13 TO ,exc_regs%
FOR A=0 TO 15
IF in_regs%!(A*4)<>exc_regs%!(A*4) THEN PRINT "Wrong reg R";A;" expected ";~(in_regs%!(A*4));" actual ";~(exc_regs%!(A*4)) : PROCend(1)
NEXT A
REM Check recorded fault info
SYS "OS_ReadSysInfo",7 TO ,exc_pc%,exc_psr%,exc_far%
IF exc_pc%<>testinstr%+8 THEN PRINT "Wrong exception PC expected ";~(testinstr%+8);" actual ";~exc_pc% : PROCend(1)
IF (exc_psr% AND &1F)<>&10 THEN PRINT "Wrong exception PSR ";~exc_psr% : PROCend(1)
IF exc_far%<>!in_regs% THEN PRINT "Wrong exception FAR expected ";~(!in_regs%);" actual ";~exc_far% : PROCend(1)
PROCend(0)
ENDPROC
DEF PROCendtest
IF da_num%<>0 THEN PROClast : SYS "OS_DynamicArea",1,da_num% : da_num%=0
ENDPROC
DEF PROCend(E%)
PROCendtest
SYS "OS_Module",7,,rma%
IF E% THEN ERROR EXT 0,"Failed"
PRINT "Success"
END
ENDPROC
DEF PROClast
PRINT "last R0 ";~!last_R0%;" R1 ";~!last_R1%;" R2 ";~!last_R2%;" R3 ";~!last_R3%;" R4 ";~!last_R4%;" R5 ";~!last_R5%;" PSR ";~!last_PSR%;" R12 ";~!last_R12%
ENDPROC
......@@ -81,7 +81,8 @@ DynAreaFlags_PiersBinding * 1 :SHL: 11 ; whether area is bound to clien
DynAreaFlags_CPBits * 7 :SHL: 12 ; cache policy variant for NotBufferable and NotCacheable bits
DynAreaFlags_CPShift * 12 ; shift amount for the above
DynAreaFlags_NeedsDMA * 1 :SHL: 15 ; only allocate from DMAable memory
; Bits 16-19 are used by RISCOS Ltd. We can reuse them for internal flags, but
DynAreaFlags_Abortable * 1 :SHL: 16 ; "Abortable" DA which is integrated with the AbortTrap system
; Bits 17-19 are used by RISCOS Ltd. We can reuse them for internal flags, but
; should probably avoid allocating any public flags.
DynAreaFlags_PMP * 1 :SHL: 20 ; DA is backed by PMP/page is member of PMP
......
......@@ -724,7 +724,8 @@ DynArea_ReturnError
; not implemented yet, but declared in public API for Ursula
; bits 12..14 => cache policy (if bits 4 or 5 set)
; bit 15 = 1 => area requires DMA capable pages (is bit 12 in ROL's OS!)
; bits 16-19 used by ROL
; bit 16 = 1 => "abortable" DA which is integrated with the AbortTrap system
; bits 17-19 used by ROL
; bit 20 = 1 => area is backed by physical memory pool
; other bits reserved for future expansion + internal page flags (should be 0)
;
......@@ -847,6 +848,14 @@ DynArea_ErrorTranslateAndExit
BNE DynArea_ErrorTranslateAndExit
22
; Abortable DAs must have a handler
TST r4, #DynAreaFlags_Abortable
BEQ %FT23
CMP r6, #0
ADREQ r0, ErrorBlock_BadDynamicAreaOptions
BEQ DynArea_ErrorTranslateAndExit
23
; now validate maximum size of area
[ DynArea_QuickHandles
......@@ -941,12 +950,14 @@ DAC_roundup
STR r3, [r2, #DANode_Base]
[ DA_Batman
;disallow some awkward flag options if SparseMap set (no error), and temporarily create as not sparse
;also disallow a DA handler
;also disallow a DA handler (unless it's an Abortable DA)
TST r4, #DynAreaFlags_SparseMap
STREQ r4, [r2, #DANode_Flags]
BICNE r7, r4, #DynAreaFlags_DoublyMapped + DynAreaFlags_NeedsSpecificPages + DynAreaFlags_Shrinkable + DynAreaFlags_SparseMap
ORRNE r7, r7, #DynAreaFlags_NotUserDraggable
STRNE r7, [r2, #DANode_Flags]
BICNE lr, r4, #DynAreaFlags_DoublyMapped + DynAreaFlags_NeedsSpecificPages + DynAreaFlags_Shrinkable + DynAreaFlags_SparseMap
ORRNE lr, lr, #DynAreaFlags_NotUserDraggable
STRNE lr, [r2, #DANode_Flags]
ANDNE lr, r4, #DynAreaFlags_Abortable
TEQNE lr, #DynAreaFlags_Abortable
MOVNE r6, #0
MOVNE r7, #0
|
......@@ -1187,6 +1198,34 @@ DAC_roundup
80
] ;DynArea_QuickHandles
; Register the AbortTrap handler for any abortable DA
LDR r10, [r2, #DANode_Flags]
TST r10, #DynAreaFlags_Abortable
BEQ %FT85
Push "r0-r4"
MOV r4, r2
MOV r0, #OSAbortTrap_Register
LDR r1, [r2, #DANode_Base]
LDR r2, [r2, #DANode_MaxSize]
TST r10, #DynAreaFlags_DoublyMapped
SUBNE r1, r1, r2
ADDNE r2, r1, r2, LSL #1
ADDEQ r2, r1, r2
ADRL r3, DynArea_AbortTrapHandler
SWI XOS_AbortTrap
Pull "r0-r4",VC
BVC %FT85
; Yuck, something went wrong. Kill the area.
BIC r10, r10, #DynAreaFlags_Abortable
STR r10, [r2, #DANode_Flags]
STR r0, [sp]
MOV r0, #DAReason_Remove
LDR r1, [sp, #4]
SWI XOS_DynamicArea
Pull "r0-r4"
PullEnv
B DynArea_ReturnError
85
; Now issue service to tell TaskManager about it
......@@ -1292,6 +1331,29 @@ DynArea_Remove Entry
BL CheckAreaNumber ; check that area is there
BCC UnknownDyn ; [not found]
; Deregister any AbortTrap handler first, to (hopefully) stop things
; messing with the area while we're trying to remove it
LDR r12, [r10, #DANode_Flags]
TST r12, #DynAreaFlags_Abortable
BEQ DAR_notabortable
Push "r0-r4"
MOV r4, r10
MOV r0, #OSAbortTrap_Deregister
LDR r1, [r10, #DANode_Base]
LDR r2, [r10, #DANode_MaxSize]
TST r12, #DynAreaFlags_DoublyMapped
SUBNE r1, r1, r2
ADDNE r2, r1, r2, LSL #1
ADDEQ r2, r1, r2
ADRL r3, DynArea_AbortTrapHandler
SWI XOS_AbortTrap
STRVS r0, [sp]
Pull "r0-r4"
EXIT VS
BIC r12, r12, #DynAreaFlags_Abortable
STR r12, [r10, #DANode_Flags]
DAR_notabortable
[ DA_Batman
LDR lr,[r10,#DANode_Flags]
TST lr,#DynAreaFlags_SparseMap
......@@ -6342,6 +6404,10 @@ CallPreShrink Entry "r0,r3,r4, r12"
CMP r0, #0 ; if none (V=0)
EXIT EQ ; then exit
LDR r4, [r11, #DANode_Flags] ; Sparse DAs shouldn't
TST r4, #DynAreaFlags_SparseMap ; get grow/shrink calls
EXIT NE
MOV r0, #DAHandler_PreShrink ; r0 = reason code
MOV r3, r1 ; r3 = amount shrinking by
LDR r4, [r11, #DANode_Size] ; r4 = current size
......@@ -6380,6 +6446,10 @@ CallPostShrink Entry "r0,r3,r4, r12"
CMP r0, #0 ; if none (V=0)
EXIT EQ ; then exit
LDR r4, [r11, #DANode_Flags] ; Sparse DAs shouldn't
TST r4, #DynAreaFlags_SparseMap ; get grow/shrink calls
EXIT NE
MOV r0, #DAHandler_PostShrink ; r0 = reason code
MOV r3, r2 ; r3 = amount shrunk by