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 handling of different access permissions REM REM It iterates through all AP values, testing that reading & writing REM triggers the abort handler correctly (abort handler should only REM be called for accesses where the code doesn't have permission to REM perform the access directly) ON ERROR ERROR EXT 0,REPORT$+" at "+STR$(ERL) da_size%=3*4096 SYS "OS_Module",6,,,4096+da_size% TO ,,rma% swi$="OS_AbortTrap" FOR pass=0 TO 2 STEP 2 P%=rma% [ OPT pass .handler% ; R0 = flags ; R1 = buffer ; R2 = required address ; R3 = length ; R12 = param STMFD R13!,{R0-R5,R14} ADR R4,last_call% MRS R5,CPSR STMIA R4,{R0-R3,R5,R12} AND R0,R0,#15 CMP R0,#2 LDMHSFD R13!,{R0-R5,R14} MSRHS CPSR_f,#(1<<28)+(1<<29) ADRHS R0,bad_reason MOVHS PC,LR ADR R4,buffer% LDR R5,da_base_ptr% SUB R2,R2,R5 ADD R2,R2,R4 TST R0,#1 EORNE R1,R1,R2 ; R1^R2, R2 EORNE R2,R2,R1 ; R1^R2, R1 EORNE R1,R1,R2 ; R2, R1 .loop% SUBS R3,R3,#1 LDRB R5,[R1],#1 STRB R5,[R2],#1 BNE loop% LDMFD R13!,{R0-R5,PC} .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_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 at_registered%=FALSE ON ERROR PRINT REPORT$;" at ";ERL : PROCend(1) PRINT "handler: ";~handler% PRINT "wp: ";~wp% 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 REM Create a sparse DA SYS "OS_DynamicArea",0,-1,0,-1,ap%+(1<<7)+(1<<10),da_size%,0,0,"ATTest" TO ,da_num%,,da_base% !da_base_ptr%=da_base% PRINT "da_base: ";~da_base% !last_R0%=0 !last_R1%=0 !last_R2%=0 !last_R3%=0 !last_PSR%=0 !last_R12%=0 SYS swi$,0,da_base%,da_base%+da_size%,handler%,wp% at_registered%=TRUE 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 at_registered% THEN PROClast at_registered%=FALSE SYS swi$,1,da_base%,da_base%+da_size%,handler%,wp% ENDIF IF da_num%<>0 THEN 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%;" 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