Commit 4a30b643 authored by Jeffrey Lee's avatar Jeffrey Lee
Browse files

Improve heap manager. Add heap testbed. Add dummy implementation of some...

Improve heap manager. Add heap testbed. Add dummy implementation of some OS_ScreenMode reason codes.

Detail:
  s/HeapMan, hdr/KernelWS - Heap manager improvements:
    - Errors generated by interrupted heap operations that are forced to complete by a OS_Heap call from the background are now cached in kernel workspace until the foreground task is resumed. This prevents them from being potentially overwritten by MessageTrans running out of background error buffers.
    - Added new OS_Heap reason code, #7 - Get area aligned. This allows areas of memory to be allocated at specific (power-of-2) alignments, and optionally without crossing a given (power-of-2) boundary. Alignment & boundary calculations are performed using logical addresses.
    - Removed the limitation that all free and allocated blocks must be a multiple of 8 bytes in length. This change was required in order to allow OS_Heap 7 to function correctly. Now the only requirements are that blocks must be multiples of 4 bytes in length, at 4 byte alignment, with a minimum length of 8 bytes. 4 extra padding bytes may still be added to the end of allocations in order to avoid creating 4-byte free blocks.
  s/HeapMan, TestSrc/HeapTest/Makefile, TestSrc/HeapTest/c/testbed, TestSrc/HeapTest/s/asm - Added heap testbed program. Can either use the OS_Heap SWI or directly include a copy of the Kernel's heap manager sources.
  s/vdudecl, s/vduswis - Added dummy implementations of OS_ScreenMode 4, 5 and 6. This prevents the Wimp generating lots of "Unknown OS_ScreenMode reason code" errors when redrawing the screen.
  s/Arthur3, s/Oscli - Moved dotstring closer to where it's used to avoid "ADRL out of range" errors in Tungsten build
Admin:
  Tested in OMAP3 ROM & Tungsten ROM softload.
  Heap testbed successfully performed over 400 million heap ops, so there shouldn't be any serious bugs in the new code (touch wood)


Version 5.35, 4.79.2.128. Tagged as 'Kernel-5_35-4_79_2_128'
parent 189b92c1
# Copyright 2011 Castle Technology Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
COMPONENT = HeapTest
OBJS = asm testbed
include CApp
# Dynamic dependencies:
/* Copyright 2011 Castle Technology Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* OS_Heap testbed code. Creates a heap and randomly allocates/deallocates/
resizes blocks of memory to test for any bugs. Tests can either be performed
using the OS_Heap SWI or by compiling a special version of the heap code
from the kernel source folder (see USE_LOCAL_OSHEAP #define and s.asm)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include "kernel.h"
#include "swis.h"
#include "Global/Heap.h"
#include "Global/NewErrors.h"
/* Whether to use the XOS_Heap SWI or the local copy of the heap code */
#define USE_LOCAL_OSHEAP
/* Maximum number of allocations to make */
#define MAX_ALLOCS 1024
#define VBIT (1<<28)
#ifdef USE_LOCAL_OSHEAP
extern _kernel_oserror *CallHeap(_kernel_swi_regs *r);
#else
#define CallHeap(R) _kernel_swi(OS_Heap,R,R)
#endif
/* Workspace */
static uint32_t *heap=NULL; /* Main heap */
static uint32_t *backup=NULL; /* Backup copy of heap */
static uint32_t allocsize=0; /* Heap memory block size */
static uint32_t *usedspace=NULL; /* Bitmap of used space; 1 bit per word */
static uint32_t seed=0; /* RNG seed */
static uint32_t sequence=0; /* Number of ops performed */
static uint32_t numblocks=0; /* Number of blocks currently allocated */
static uint32_t blocks[MAX_ALLOCS]; /* Offsets of blocks within heap */
static uint32_t currentop = 0; /* Current heap operation */
static uint32_t opsleft = 0; /* Number of ops left */
static _kernel_swi_regs r;
static _kernel_swi_regs last;
/* Utility functions */
static void init(void)
{
srand(seed);
printf("Seed %08x alloc size %08x\n",seed,allocsize);
/* Make heap 4K aligned */
heap = (uint32_t *) (((uint32_t) malloc(allocsize+4096)+4095)&0xfffff000);
/* Same for backup */
backup = (uint32_t *) (((uint32_t) malloc(allocsize+4096)+4095)&0xfffff000);
/* Used space map */
usedspace = (uint32_t *) malloc(((allocsize+31*4)>>7)<<2);
memset(usedspace,0,((allocsize+31*4)>>7)<<2);
memset(heap,0,allocsize);
memset(backup,0,allocsize);
}
static uint32_t getrand(uint32_t max)
{
uint64_t temp = ((uint64_t) max)*rand();
return (uint32_t) (temp/RAND_MAX);
}
static void dumpheap(uint32_t *h)
{
fprintf(stderr,"heap @ %p:\nmag %x\nfree %x\nbase %x\nend %x\n",h,h[0],h[1],h[2],h[3]);
uint32_t free = h[1];
uint32_t next = 16;
if(free)
free += 4;
while(free)
{
if(free > next)
{
fprintf(stderr,"allocs between %x and %x:\n",next,free);
do {
fprintf(stderr,"%x: alloc size %x\n",next,h[next>>2]);
if((h[next>>2] > h[2]) || (h[next>>2]+next > h[2]) || (h[next>>2]&3) || !h[next>>2])
{
fprintf(stderr,"bad block, skipping rest\n");
break;
}
next += h[next>>2];
} while(free>next);
if(free!=next)
fprintf(stderr,"alloc mismatch! next=%x\n",next);
}
fprintf(stderr,"%x: free size %x next %x\n",free,h[(free+4)>>2],h[free>>2]);
if(h[(free+4)>>2] == h[free>>2])
fprintf(stderr,"consecutive free blocks!\n");
next = free+h[(free+4)>>2];
if((h[free>>2] & 3) || (h[free>>2] >= h[2]) || (h[free>>2]+free >= h[2]))
{
fprintf(stderr,"bad next ptr\n");
return;
}
if((h[(free+4)>>2] & 3) || (h[(free+4)>>2] >= h[2]) || (h[(free+4)>>2]+free >= h[2]))
{
fprintf(stderr,"bad size\n");
return;
}
if(!h[free>>2])
{
fprintf(stderr,"end of free list\n");
break;
}
free = free+h[free>>2];
if(free<next)
{
fprintf(stderr,"next free is inside current?\n");
return;
}
}
if(free > h[2])
{
fprintf(stderr,"free list extends beyond heap end\n");
}
if(next > h[2])
{
fprintf(stderr,"next ptr beyond heap end\n");
}
fprintf(stderr,"end allocs:\n");
while(next < h[2])
{
fprintf(stderr,"%x: alloc size %x\n",next,h[next>>2]);
if((h[next>>2] > h[2]) || (h[next>>2]+next > h[2]) || (h[next>>2]&3) || !h[next>>2])
{
fprintf(stderr,"bad block, skipping rest\n");
return;
}
next += h[next>>2];
}
fprintf(stderr,"end\n");
}
static bool heapvalid(uint32_t *h)
{
uint32_t free = h[1];
uint32_t next = 16;
if(free)
free += 4;
while(free)
{
if(free > next)
{
do {
if((h[next>>2] > h[2]) || (h[next>>2]+next > h[2]) || (h[next>>2]&3) || !h[next>>2])
{
return false;
}
next += h[next>>2];
} while(free>next);
if(free!=next)
return false;
}
if(h[(free+4)>>2] == h[free>>2])
return false;
next = free+h[(free+4)>>2];
if((h[free>>2] & 3) || (h[free>>2] >= h[2]) || (h[free>>2]+free >= h[2]))
{
return false;
}
if((h[(free+4)>>2] & 3) || (h[(free+4)>>2] >= h[2]) || (h[(free+4)>>2]+free >= h[2]))
{
return false;
}
if(!h[free>>2])
{
break;
}
free = free+h[free>>2];
if(free<next)
{
return false;
}
}
if(free > h[2])
{
return false;
}
if(next > h[2])
{
return false;
}
while(next < h[2])
{
if((h[next>>2] > h[2]) || (h[next>>2]+next > h[2]) || (h[next>>2]&3) || !h[next>>2])
{
return false;
}
next += h[next>>2];
}
return true;
}
static void fail(void)
{
fprintf(stderr,"Failed on sequence %d\n",sequence);
fprintf(stderr,"Last op registers:\n");
for(int i=0;i<5;i++)
fprintf(stderr,"r%d = %08x\n",i,last.r[i]);
fprintf(stderr,"Result registers:\n");
for(int i=0;i<5;i++)
fprintf(stderr,"r%d = %08x\n",i,r.r[i]);
fprintf(stderr,"Heap before op:\n");
dumpheap(backup);
fprintf(stderr,"Heap after op:\n");
dumpheap(heap);
fprintf(stderr,"Allocated blocks:\n");
for(uint32_t i=0;i<numblocks;i++)
{
fprintf(stderr,"%08x\n",blocks[i]);
}
exit(1);
}
static uint32_t blocksize(uint32_t offset)
{
return heap[(offset-4)>>2];
}
static void tryuse(uint32_t offset)
{
uint32_t len = blocksize(offset);
if((len-4 > allocsize-offset) || (len & 3) || (len<4))
{
fprintf(stderr,"tryuse: Bad block at %08x\n",offset);
fail();
}
offset >>= 2;
while(len)
{
if(usedspace[offset>>5] & (1<<(offset&31)))
{
fprintf(stderr,"tryuse: Overlapping block at %08x\n",offset<<2);
fail();
}
usedspace[offset>>5] |= 1<<(offset&31);
offset++;
len -= 4;
}
}
static void tryfree(uint32_t offset)
{
uint32_t len = blocksize(offset);
if((len-4 > allocsize-offset) || (len & 3) || (len<4))
{
fprintf(stderr,"tryfree: Bad block at %08x\n",offset);
fail();
}
offset >>= 2;
while(len)
{
if(!(usedspace[offset>>5] & (1<<(offset&31))))
{
fprintf(stderr,"tryfree: Block at %08x already freed\n",offset<<2);
fail();
}
usedspace[offset>>5] -= 1<<(offset&31);
offset++;
len -= 4;
}
}
/* Main function */
int main(int argc,char **argv)
{
_kernel_oserror *err;
/* TODO - Take parameters from command line */
_swix(OS_ReadMonotonicTime,_OUT(0),&seed)
allocsize = 8*1024;
init();
/* Initialise heap */
r.r[0] = HeapReason_Init;
r.r[1] = (int) heap;
r.r[3] = allocsize;
err = CallHeap(&r);
if(err)
{
fprintf(stderr,"Heap initialise failed! %s\n",err->errmess);
exit(1);
}
usedspace[0] = 0xf;
/* Begin tests */
uint32_t temp,temp2,temp3,temp4;
while(heapvalid(heap))
{
if(!opsleft)
{
opsleft = getrand(128);
switch(getrand(4))
{
case 0:
currentop = HeapReason_Get;
break;
case 1:
currentop = HeapReason_Free;
break;
case 2:
currentop = HeapReason_ExtendBlock;
break;
default:
currentop = HeapReason_GetAligned;
break;
}
}
if(!(sequence&0xffff))
{
// printf(".");
dumpheap(heap);
}
sequence++;
r.r[0] = currentop;
memcpy(backup,heap,allocsize);
switch(currentop)
{
case HeapReason_Get:
if(numblocks == MAX_ALLOCS)
{
opsleft = 0;
break;
}
r.r[3] = temp = getrand(allocsize>>5)+1;
last = r;
err = CallHeap(&r);
if(err)
{
if(err->errnum != ErrorNumber_HeapFail_Alloc)
{
fprintf(stderr,"Failed allocating %08x bytes: %s\n",temp,err->errmess);
fail();
}
}
else
{
temp2 = blocks[numblocks++] = r.r[2]-((uint32_t)heap);
if(blocksize(temp2) < temp+4)
{
fprintf(stderr,"Failed to allocate requested block size: %08x bytes at %08x\n",temp,temp2);
fail();
}
tryuse(temp2);
}
break;
case HeapReason_Free:
if(!numblocks)
{
opsleft = 0;
break;
}
temp = getrand(numblocks);
r.r[2] = blocks[temp]+((uint32_t) heap);
tryfree(blocks[temp]); /* Must free beforehand */
last = r;
err = CallHeap(&r);
if(err)
{
fprintf(stderr,"Failed freeing block at %08x: %s\n",blocks[temp],err->errmess);
fail();
}
blocks[temp] = blocks[--numblocks];
break;
case HeapReason_ExtendBlock:
if(!numblocks)
{
opsleft = 0;
break;
}
temp = getrand(numblocks);
r.r[2] = blocks[temp]+((uint32_t) heap);
temp2 = getrand(allocsize>>4)-(allocsize>>5);
r.r[3] = temp2;
temp3 = blocksize(blocks[temp]);
tryfree(blocks[temp]); /* Must free beforehand */
last = r;
err = CallHeap(&r);
if(err)
{
if(err->errnum != ErrorNumber_HeapFail_Alloc)
{
fprintf(stderr,"Failed resizing block at %08x by %08x bytes: %s\n",blocks[temp],(int) temp2,err->errmess);
fail();
}
if(blocksize(blocks[temp]) != temp3)
{
fprintf(stderr,"Resize failed but block size changed\n");
fail();
}
tryuse(blocks[temp]);
}
else
{
if(r.r[2] && (r.r[2] != 0xffffffff))
{
if((int) (temp3+temp2) <= 4)
{
fprintf(stderr,"Resized block was kept when it should have been freed: block %08x by %08x\n",blocks[temp],(int) temp2);
fail();
}
blocks[temp] = r.r[2]-((uint32_t)heap);
tryuse(blocks[temp]);
if((blocksize(blocks[temp])-(temp3+temp2)) > 7)
{
fprintf(stderr,"Failed to resize block by required amount: block %08x by %08x\n",blocks[temp],(int) temp2);
fail();
}
}
else
{
if((int) (temp3+temp2) > 4)
{
fprintf(stderr,"Resized block was freed when it should have remained: block %08x by %08x\n",blocks[temp],(int) temp2);
fail();
}
blocks[temp] = blocks[--numblocks];
}
}
break;
case HeapReason_GetAligned:
if(numblocks == MAX_ALLOCS)
{
opsleft = 0;
break;
}
r.r[3] = temp = getrand(allocsize>>4)+1;
temp2 = 4<<getrand(9); /* Max 2K alignment (heap 4K aligned) */
temp3 = temp2*(1<<getrand(5));
if(temp3 > 4096) /* Max 2K boundary (heap 4K aligned) */
temp3 = 2048;
if(temp3 < temp)
temp3 = 0;
r.r[2] = temp2;
r.r[4] = temp3;
last = r;
err = CallHeap(&r);
if(err)
{
if(err->errnum != ErrorNumber_HeapFail_Alloc)
{
fprintf(stderr,"Failed allocating %08x bytes at alignment %08x boundary %08x: %s\n",temp,temp2,temp3,err->errmess);
fail();
}
}
else
{
temp4 = blocks[numblocks++] = r.r[2]-((uint32_t) heap);
if(blocksize(temp4) < temp+4)
{
fprintf(stderr,"Failed to allocate requested block size: %08x bytes at alignment %08x boundary %08x at %08x\n",temp,temp2,temp3,temp4);
fail();
}
if(temp4 & (temp2-1))
{
fprintf(stderr,"Block allocated at wrong alignment: %08x bytes at alignment %08x boundary %08x at %08x\n",temp,temp2,temp3,temp4);
fail();
}
if(temp3 && ((temp4 & ~(temp3-1)) != ((temp4+temp-1) & ~(temp3-1))))
{
fprintf(stderr,"Block crosses boundary: %08x bytes at alignment %08x boundary %08x at %08x\n",temp,temp2,temp3,temp4);
fail();
}
tryuse(temp4);
}
break;
}
if(opsleft)
opsleft--;
}
fprintf(stderr,"Heap corruption detected!\n");
fail();
return 0;
}
; Copyright 2011 Castle Technology Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
; http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; Assembler gubbins to allow a local copy of the heap manager to be used by
; the testbed application
GET Hdr:ListOpts
GET Hdr:Macros
GET Hdr:System
GET Hdr:CPU.Arch
GET Hdr:Machine.<Machine>
GET Hdr:Heap
GET Hdr:Proc
GET Hdr:FSNumbers
GET Hdr:HighFSI
GET Hdr:NewErrors
; Disable internationalisation
GBLL International
International SETL {FALSE}
; Indicate we're compiling the testbed
GBLL HeapTestbed
HeapTestbed SETL {TRUE}
; Heap debugging disabled for now
GBLL DebugHeaps
DebugHeaps SETL {FALSE}
; Fake zero page workspace
^ 0
IRQsema # 4
HeapSavedReg_R0 # 4
HeapSavedReg_R1 # 4
HeapSavedReg_R2 # 4
HeapSavedReg_R3 # 4
HeapSavedReg_R4 # 4
HeapSavedReg_R13 # 4
HeapReturnedReg_R0 # 4
HeapReturnedReg_R1 # 4
HeapReturnedReg_R2 # 4
HeapReturnedReg_R3 # 4
HeapReturnedReg_R4 # 4
HeapReturnedReg_R13 # 4
HeapReturnedReg_PSR # 4
ZeroPageSize * @
; Macros and other bits
MACRO
assert $condition
[ :LNOT: ($condition)
! 1,"Assert failed: $condition"
]
MEND
SVC2632 * SVC32_mode
AREA testbeddata, DATA
ZeroPage % ZeroPageSize
HeapBackgroundError % 256
AREA testbedcode, CODE
; C interface
EXPORT CallHeap
CallHeap ROUT
; r0 = _kernel_swi_regs ptr for input/output
; Returns error ptr in r0
Push "r0,r4-r11,lr"
LDMIA r0,{r0-r9}
SWI OS_EnterOS ; Call in SVC mode?
BL DoCallXOSHeap
MOVVS r10,r0
MOVVC r10,#0
SWI OS_LeaveOS
Pull "r11"
STMIA r11,{r0-r9}
MOV r0,r10
Pull "r4-r11,pc"
; Assembler bits for use by heap code
DoCallXOSHeap
; Fake an XOS_Heap SWI
; Preserve r10-r12 and enter with PSR in lr
Push "r10-r12,lr"
MRS lr,CPSR
B HeapEntry
; Main heap manager code
GET ^.^.s.HeapMan
END
......@@ -13,11 +13,11 @@
GBLS Module_ComponentPath
Module_MajorVersion SETS "5.35"
Module_Version SETA 535
Module_MinorVersion SETS "4.79.2.127"
Module_Date SETS "27 Nov 2011"
Module_ApplicationDate SETS "27-Nov-11"
Module_MinorVersion SETS "4.79.2.128"
Module_Date SETS "10 Dec 2011"
Module_ApplicationDate SETS "10-Dec-11"
Module_ComponentName SETS "Kernel"
Module_ComponentPath SETS "castle/RiscOS/Sources/Kernel"
Module_FullVersion SETS "5.35 (4.79.2.127)"
Module_HelpVersion SETS "5.35 (27 Nov 2011) 4.79.2.127"
Module_FullVersion SETS "5.35 (4.79.2.128)"
Module_HelpVersion SETS "5.35 (10 Dec 2011) 4.79.2.128"
END
......@@ -5,19 +5,19 @@
*
*/
#define Module_MajorVersion_CMHG 5.35
#define Module_MinorVersion_CMHG 4.79.2.127
#define Module_Date_CMHG 27 Nov 2011
#define Module_MinorVersion_CMHG 4.79.2.128
#define Module_Date_CMHG 10 Dec 2011
#define Module_MajorVersion "5.35"
#define Module_Version 535
#define Module_MinorVersion "4.79.2.127"
#define Module_Date "27 Nov 2011"
#define Module_MinorVersion "4.79.2.128"
#define Module_Date "10 Dec 2011"
#define Module_ApplicationDate "27-Nov-11"
#define Module_ApplicationDate "10-Dec-11"
#define Module_ComponentName "Kernel"
#define Module_ComponentPath "castle/RiscOS/Sources/Kernel"
#define Module_FullVersion "5.35 (4.79.2.127)"
#define Module_HelpVersion "5.35 (27 Nov 2011) 4.79.2.127"
#define Module_FullVersion "5.35 (4.79.2.128)"
#define Module_HelpVersion "5.35 (10 Dec 2011) 4.79.2.128"
#define Module_LibraryVersionInfo "5:35"
......@@ -1980,6 +1980,8 @@ ROMBuildDate # 128
NewFX0Error # 64
]
HeapBackgroundError # 256 ; For storing errors generated in the background by the forced completion of a foreground heap op
KbuffsEnd # 0
KbuffsSize * KbuffsEnd - KbuffsBaseAddress ;size of Kernel buffers area
......
......@@ -1545,6 +1545,8 @@ ReadNumAuto Entry "r1,r3,r4"
AutoString
= "Auto", 0
dotstring
= ".", 0
ALIGN
ReadSizeParm ROUT
......
......@@ -33,6 +33,11 @@ TubeInfo SETL {FALSE}
GBLL debheap
debheap SETL 1=0
[ :LNOT: :DEF: HeapTestbed
GBLL HeapTestbed
HeapTestbed SETL {FALSE}
]
[ DebugHeaps
FreeSpaceDebugMask * &04000000
UsedSpaceDebugMask * &08000000
......@@ -43,11 +48,11 @@ Nil * 0
hpd RN r1 ; The punter sees these
addr RN r2
size RN r3
work RN r4
HpTemp RN r10 ; But not these
tp RN r11
bp RN r12
work RN r4 ; This is the only one we have to save.
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; + H E A P O R G A N I S A T I O N +
......@@ -103,7 +108,7 @@ freblksize # 0
; The link field is Nil (0) for the last block in the list
; Block sizes must be forced to a multiple of 8 bytes for subsequent link and
; Block sizes must be forced to a minimum of 8 bytes for subsequent link and
; size information to be stored in them if they are disposed of by the user.
; They must also be capable of storing a 4 byte size field while allocated.
......@@ -124,6 +129,17 @@ $label BL ValidateHpdSubr
MEND
; Call XOS_Heap SWI
MACRO
CallXOSHeap
[ HeapTestbed
BL DoCallXOSHeap
|
SWI XOS_Heap
]
MEND
;****************************************************************************
; These bits of ExtendBlock are outside the IRQ HeapOp range because they
......@@ -157,7 +173,7 @@ ReallocateInSafeZone
Push addr ; save for later freeing
MOV R0, #HeapReason_Get
SWI XOS_Heap
CallXOSHeap
Pull addr, VS
BVS SafeNaffExtension
......@@ -192,11 +208,11 @@ CopyForExtension
MOV R0, #HeapReason_Free
Pull addr ; heap block addr
SWI XOS_Heap
CallXOSHeap
MOV R0, #HeapReason_ExtendBlock
MOVVC R0, #HeapReason_ExtendBlock
WritePSRc SVC_mode + I_bit,work ; disable IRQs before we venture back
B GoodExtension ; into danger zone
BVC GoodExtension ; into danger zone
SafeNaffExtension
WritePSRc SVC_mode + I_bit,work ; disable IRQs before we venture back
......@@ -236,6 +252,14 @@ heapopdoneinbackground ROUT
; clear the interlock
TST R11, #V_bit ; look at returned error
BEQ GoodHeapExit
; Recover the error from our buffer
LDR R0,=HeapBackgroundError
LDR R10,[R0]
SWI XMessageTrans_CopyError
; Check that it worked - MessageTrans may be dead
LDR R11,[R0]
TEQ R10,R11
LDRNE R0,=HeapBackgroundError ; Just return our internal buffer if MessageTrans couldn't provide one
B NaffHeapExit
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
......@@ -247,8 +271,9 @@ heapopdoneinbackground ROUT
; In r0 = heap action requested
; r1(hpd) -> heap block
; r2(addr) -> start of block
; r2(addr) -> start of block, or required alignment
; r3(size) = size of block
; r4(work) = boundary limitation
; Out VClear -> Action performed
; VSet -> Something terrible has happened, error set
......@@ -282,7 +307,7 @@ inspect_IRQ_stack
ORR R10, R10, #I32_bit:OR:SVC2632
STR R10, [R11, #4*6] ; return into SVC26/32 mode with IRQs disabled
Push "R0-R3, lr"
Push "R0-R4, lr"
LDR R10, =ZeroPage+HeapSavedReg_R0
......@@ -292,15 +317,30 @@ inspect_IRQ_stack
; CMP R12, #0
; BNE HeapInUse
LDMIA R10, {R0-R3, R10, R11}
LDMIA R10, {R0-R4, R11}
SWI XOS_Heap ; with interrupts off!
LDR R12, =ZeroPage+HeapReturnedReg_R0
; Could we poke these into the IRQ stack too...?
; would allow interruptible IRQ processes to do heap ops!!!
MRS lr, CPSR
STMIA R12, {R0-R3, R10, R11, lr}
Pull "R0-R3, lr"
STMIA R12, {R0-R4, R11, lr}
; Any errors that were generated by the foreground operation may have ended up
; using one of MessageTrans' IRQ buffers. Trouble is, any number of IRQ errors
; could occur between now and when the foreground task gets the error. Avoid
; the error getting clobbered by copying it into a special kernel buffer, and
; then copy it back to a MessageTrans buffer once we're back in the foreground.
BVC noheapbackgrounderror
LDR R1,=HeapBackgroundError
MOV LR,#256
heapbackgrounderrorloop
LDMIA R0!,{R2-R4,R12}
SUBS LR,LR,#16
STMIA R1!,{R2-R4,R12}
BNE heapbackgrounderrorloop
noheapbackgrounderror
Pull "R0-R4, lr"
iis_end ; store the registers in the info block
LDR R12, =ZeroPage+HeapSavedReg_R0
......@@ -334,6 +374,8 @@ HeapJumpTable ; Check reason codes against Hdr:Heap defs
B ExtendHeap
assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_ReadBlockSize
B ReadBlockSize
assert ((.-HeapJumpTable) :SHR: 2) = HeapReason_GetAligned
B GetAreaAligned
[ debheap
B ShowHeap
]
......@@ -352,7 +394,12 @@ GoodHeapExit ; V cleared on entry to SWI dispatch
Pull lr
ORRVS lr, lr, #V_bit ; VSet Exit
[ HeapTestbed
MSR CPSR_cxsf, lr ; Fake exit for testbed
Pull "r10-r12,pc"
|
ExitSWIHandler ; Like all good SWI handlers
]
;HeapInUse
; $HeapBadAsModuleBRA
......@@ -592,8 +639,8 @@ GetArea ROUT
BLE garfailed_zero ; And -ve is invalid as well!
; note sizes of many megabytes thrown out by looking.
ADD size, size, #(freblksize-1)+4 ; Make block size granular
BIC size, size, #(freblksize-1) ; with size field added
ADD size, size, #3+4 ; Make block size multiple of 4
BIC size, size, #3 ; including header
ADR addr, hpdfree-frelink ; addr:= @(hpd!free)-frelink
......@@ -610,7 +657,11 @@ garloop
; If we have an exact fit (or as close as the granularity of the free list will
; allow), unlink this block and return it
BNE SplitFreeBlock
CMP HpTemp, #freblksize
BGE SplitFreeBlock
; Increase allocation size if there wasn't enough space to split the free block
ADD size, size, HpTemp
[ debheap
LDR HpTemp, hpddebug
......@@ -660,6 +711,7 @@ ResultIsAddrPlus4
STR size, [addr], #4 ; Store block size and increment addr
Pull "size" ; Return original value to the punter
; Note : real size got would be an option!
CLRV
B GoodHeapExit ; RESULTIS addr
......@@ -689,7 +741,7 @@ garmore
]
garfailed
ADR R0, ErrorBlock_HeapFail_Alloc
ADRL R0, ErrorBlock_HeapFail_Alloc
[ International
BL TranslateError
]
......@@ -705,7 +757,7 @@ garfailed_badhpd
[ debheap
STRIM "Invalid heap descriptor"
]
ADR R0, ErrorBlock_HeapFail_BadDesc
ADRL R0, ErrorBlock_HeapFail_BadDesc
[ International
BL TranslateError
]
......@@ -719,6 +771,298 @@ garfailed_zero
garfailed_zero * garfailed
]
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; GetAreaAligned. Top level HeapEntry
; ==============
;
; Allocate an aligned block of memory from the heap
; This is the same as GetArea, except it will only allocate areas with the given
; (power-of-two) alignment.
; Fails if requesting size = 0
; In : hpd -> heap pointer
; size = size of block required
; addr = alignment (power of 2)
; work = boundary (power of 2, 0 for none)
; Out : VClear : addr -> got a block
; VSet : addr = 0, couldn't get block
; Rest of universe ok
GetAreaAligned ROUT
Push "size,work"
ValidateHpd garafailed
[ debheap
; HpTemp not critical
LDR HpTemp, hpddebug
CMP HpTemp, #0
BEQ %FT00
Push "r0, link"
MOV r0, size
DREG r0, "GetAreaAligned "
MOV r0, addr
DREG r0, "alignment "
MOV r0, work
DREG r0, "boundary "
BL iShowHeap
Pull "r0, link"
00
]
CMP size, #0 ; Can't deallocate 0, so there!
BLE garafailed_zero ; And -ve is invalid as well!
; note sizes of many megabytes thrown out by looking.
ADD size, size, #3 ; Make block size multiple of 4
BIC size, size, #3 ; excluding header
SUB bp, addr, #1 ; Store alignment-1 in bp
TST bp, addr
BNE garafailed_align ; Must be power of 2!
CMP bp, #3
MOVLT bp, #3 ; Minimum alignment is 4
SUB r0, work, #1 ; Store boundary-1 in r0
TST r0, work
BNE garafailed_boundary ; Must be power of 2!
ADR addr, hpdfree-frelink ; addr:= @(hpd!free)-frelink
; If we have a boundary, it must be >= alignment, and >= size
CMP r0, #-1
BEQ garaloop
CMP r0, bp
CMPHS work, size
BLO garafailed_boundary2
garaloop
LDR tp, [addr, #frelink] ; tp := addr!fre.link
CMP tp, #Nil ; Is this the end of the chain ?
BEQ garamore ; - so try main blk
ADD addr, addr, tp ; convert offset
LDR HpTemp, [addr, #fresize]
; Calculate start and end addresses as if we were to allocate from this block
ADD work,addr,#4 ; 4 bytes for storing block size
ADD HpTemp,HpTemp,addr ; End of free block
ADD work,work,bp
garaloop2
BIC work,work,bp ; work = start of user block
SUB lr,work,addr
CMP lr,#4
BEQ garastartok ; Start alignment is exact
CMP lr,#freblksize+4
BGE garastartok ; Enough space to fit a free block at the start
; We need a free block, but there isn't enough space for it.
; Shift 'work' up by one unit of alignment and try again.
ADD work,work,bp,LSL #1
B garaloop2
garastartok
; Calculate block end address
ADD lr,work,size ; End of user block
SUBS lr,HpTemp,lr ; Gap after user block
BLO garaloop ; Not big enough
; Check boundary requirement
CMP r0,#-1
BEQ garaboundaryok
AND lr,work,r0 ; Start offset within boundary
ADD lr,lr,size
SUB lr,lr,#1 ; Last byte of allocation
CMP lr,r0
BLS garaboundaryok
; This allocation crosses a boundary. Shift 'work' up to be boundary aligned.
ADD work,work,r0
BIC work,work,r0
B garaloop2 ; Loop back round to recheck everything (with small boundary sizes, we may have created a situation where we can't fit an initial free block)
garaboundaryok
; We have a suitable space to allocate from.
ADD size,size,#4 ; Correct size to store
SUB work,work,#4 ; Correct block start
[ debheap
LDR lr, hpddebug
CMP lr, #0
BEQ %FT60
WRLN "Using existing free block"
60
]
; Note: bp now being used as scratch
ADD bp,work,size ; End of user block
SUB bp,HpTemp,bp ; Gap after user block
WritePSRc SVC_mode+I_bit, lr
; Work out if we need a new free block afterwards
CMP bp, #freblksize
ADDLT size, size, bp ; Not enough space, so enlarge allocated block
BLT %FT10
; Create a new free block that will lie after our allocated block
SUB HpTemp, HpTemp, bp
STR bp, [HpTemp, #fresize] ; Write size
LDR bp, [addr, #frelink]
CMP bp, #Nil
ADDNE bp, bp, addr
SUBNE bp, bp, HpTemp
STR bp, [HpTemp, #frelink] ; Write next ptr
SUB HpTemp, HpTemp, addr
STR HpTemp, [addr, #frelink] ; Fix up link from previous block
10
; Shrink this free block to take up the space preceeding the allocated block.
SUBS bp,work,addr
STRNE bp, [addr, #fresize]
BNE ResultIsWorkPlus4
; No space for an initial free block. Get rid of it.
ASSERT frelink=0 ; otherwise LDR bp,[addr,#frelink]!
LDR bp, [addr]
CMP bp, #0
ADDNE bp, bp, tp
STR bp, [addr, -tp]
B ResultIsWorkPlus4
; Got no more free blocks of length >= size, so try to allocate more heap space
; out of the block described by hpd
garamore
[ debheap
LDR work, hpddebug
CMP work, #0
BEQ %FT80
WRLN "Trying to get more from main block"
80
]
LDR work, hpdbase
ADD work, work, hpd
ADD tp, work, #4
ADD tp, tp, bp
garamoreloop
BIC tp, tp, bp ; tp = pointer to return to user
; Make sure there's enough space for a free block if necessary
SUB HpTemp, tp, work ; HpTemp = tp-(hpd+hpdbase)
CMP HpTemp, #4
BEQ garamoreok
CMP HpTemp, #freblksize+4
ADDLT tp, tp, bp, LSL #1 ; Not enough space for free block
BLT garamoreloop
garamoreok
; Boundary check
CMP r0, #-1
BEQ garamoreboundaryok
AND HpTemp, tp, r0
ADD HpTemp, HpTemp, size
SUB HpTemp, HpTemp, #1
CMP HpTemp, r0
BLS garamoreboundaryok
; Shift 'tp' up to be boundary aligned
ADD tp, tp, r0
BIC tp, tp, r0
B garamoreloop
garamoreboundaryok
ADD HpTemp, tp, size ; New heap end
SUB HpTemp, HpTemp, hpd ; New heap size
LDR lr, hpdend
CMP HpTemp, lr
BGT garafailed
WritePSRc SVC_mode+I_bit, lr
; Set up the block to return to the user
ADD size, size, #4
STR size, [tp, #-4]!
; Grow the heap
STR HpTemp, hpdbase
; Create preceeding free block if necessary
SUBS HpTemp, tp, work
BEQ ResultIsTpPlus4
; Write the free block
STR HpTemp, [work, #fresize]
MOV HpTemp, #Nil
STR HpTemp, [work, #frelink]
; Patch up the preceeding block
SUB HpTemp, work, addr
STR HpTemp, [addr, #frelink]
ResultIsTpPlus4
; Block size is already stored
ADD addr, tp, #4
Pull "size,work"
MOV r0,#HeapReason_GetAligned
CLRV
B GoodHeapExit
ResultIsWorkPlus4
STR size, [work] ; Store block size
ADD addr, work, #4 ; Move to correct return reg & add offset
Pull "size,work"
MOV r0,#HeapReason_GetAligned
CLRV
B GoodHeapExit
garafailed
ADRL R0, ErrorBlock_HeapFail_Alloc
[ International
BL TranslateError
]
[ debheap
WRLN " : GetAreaAligned failed"
]
garafail_common
MOV addr, #0 ; addr := 0 if we couldn't allocate
Pull "size,work" ; RESULTIS 0
B NaffHeapExit ; VSet Exit
garafailed_badhpd
[ debheap
STRIM "Invalid heap descriptor"
]
ADRL R0, ErrorBlock_HeapFail_BadDesc
[ International
BL TranslateError
]
B garafail_common
[ debheap
garafailed_zero
STRIM "Can't allocate 0 or less bytes"
B garafailed
garafailed_align
STRIM "Alignment not power of 2"
B garafailed
garafailed_boundary
STRIM "Boundary not power of 2"
B garafailed
garafailed_boundary2
STRIM "Boundary too small"
B garafailed
|
garafailed_zero * garafailed
garafailed_align * garafailed
garafailed_boundary * garafailed
garafailed_boundary2 * garafailed
]
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; FreeArea. Top level HeapEntry
......@@ -787,7 +1131,7 @@ ExtendBlock
CMP HpTemp, #0
BEQ %FT00
Push "r0, link"
DREG size, "ExtendBlock by ",concat
DREG size, "ExtendBlock by ",cc
STRIM " block at "
SUB r0, addr, hpd
SUB r0, r0, #4
......@@ -799,8 +1143,8 @@ ExtendBlock
BL FindHeapBlock
BVS NaffExtension
ADD size, size, #freblksize-1 ; round size as appropriate :
BICS size, size, #freblksize-1 ; round up to nearest 8
ADD size, size, #3 ; round size as appropriate :
BICS size, size, #3 ; round up to nearest 4
BEQ GoodExtension ; get the easy case done.
BPL MakeBlockBigger
......@@ -808,7 +1152,8 @@ ExtendBlock
RSB size, size, #0
LDR bp, [addr, hpd] ; get block size
WritePSRc SVC_mode+I_bit, R14
SUBS bp, bp, size ; size of block left
SUB bp, bp, size ; size of block left
CMP bp, #4
[ debheap
; HpTemp not critical, GE/LT critical
......@@ -818,22 +1163,40 @@ ExtendBlock
BEQ %FT01
WRLN "Freeing part of block"
01
CMP bp, #0 ; restore GE/Lt
CMP bp, #4 ; restore GE/Lt
]
MOVLE HpTemp, #-1 ; if discarding block, then
STRLE HpTemp, [stack] ; make pointer really naff.
BLE GoodShrink
STRGT bp, [addr, hpd] ; update size of block left
ADDGT addr, addr, bp ; offset of block to free
STRGT size, [addr, hpd] ; construct block for freeing
; If we're only shrinking 4 bytes, only allow the shrink to go ahead
; if there's a free block (or hpdbase) after us
CMP size, #4
BGT DoShrink
LDR HpTemp, [hpd, tp]
CMP HpTemp, #Nil
ADDNE HpTemp, HpTemp, tp
LDREQ HpTemp, hpdbase
ADD HpTemp, HpTemp, hpd ; Next free block ptr
SUB HpTemp, HpTemp, addr ; Offset from start of this block
SUB HpTemp, HpTemp, size ; Apply shrink amount to match bp
CMP HpTemp, bp
MOVGT size, #0 ; Used block after us. Deny shrink.
BGT GoodExtension
BLT CorruptExtension ; Heap corrupt!
; Else there's a free block (or hpdbase) directly after us
DoShrink
STR bp, [addr, hpd] ; update size of block left
ADD addr, addr, bp ; offset of block to free
STR size, [addr, hpd] ; construct block for freeing
GoodShrink
BL FreeChunkWithConcatenation ; work still set from block lookup
GoodExtension
Pull "addr, size, work"
[ DebugHeaps
ADD lr, size, #freblksize-1 ; work out how much we actually extended by
BICS lr, lr, #freblksize-1
MOVS lr, size ; work out how much we actually extended by
BEQ %FT99 ; if zero or negative
BMI %FT99 ; then nothing to do
LDR HpTemp, [addr, #-4] ; get new block size
......@@ -847,6 +1210,7 @@ GoodExtension
BNE %BT98
99
]
CLRV
B GoodHeapExit
MakeBlockBigger
......@@ -871,7 +1235,7 @@ MakeBlockBigger
LDRNE HpTemp, [HpTemp, #fresize]
LDREQ HpTemp, hpdend
SUBEQ HpTemp, HpTemp, bp
BICEQ HpTemp, HpTemp, #(freblksize-1)
BICEQ HpTemp, HpTemp, #3
; force it to a sensible blocksize
MRS lr, CPSR ; save EQ/NE state
......@@ -890,7 +1254,7 @@ MakeBlockBigger
STRIM "Extending block into "
02
Pull "HpTemp,lr"
msr ,CPSR_f, lr
msr CPSR_f, lr
]
LDR work, [addr, hpd] ; get size back
......@@ -925,8 +1289,13 @@ IntoFreeEntry
Pull HpTemp
]
CMP HpTemp, size
BNE SplitFreeBlockForExtend
SUB HpTemp, HpTemp, size ; new freblk size
CMP HpTemp, #4
BGT SplitFreeBlockForExtend
; Not enough space for a free block. Increase the grow amount a bit.
ADDEQ work, work, #4
STREQ work, [addr, hpd]
; free entry just right size : remove from free list
LDR HpTemp, [bp, hpd] ; free link
......@@ -942,7 +1311,6 @@ SplitFreeBlockForExtend
STR work, [tp, hpd] ; prevnode points at right place
ADD work, work, tp ; offset of new free entry
ADD work, work, hpd
SUB HpTemp, HpTemp, size ; new freblk size
STR HpTemp, [work, #fresize]
LDR HpTemp, [bp, hpd]
CMP HpTemp, #Nil
......@@ -992,13 +1360,15 @@ hack_preceder
; work is prevprevfree offset
; size is amount block grows by
; addr is block offset
CMP bp, #0
ADDNE HpTemp, tp, hpd
STRNE bp, [HpTemp, #fresize] ; prevblock shrunk
BNE copy_backwards
CMP bp, #freblksize
ADDGE HpTemp, tp, hpd
STRGE bp, [HpTemp, #fresize] ; prevblock shrunk
BGE copy_backwards
; free freblk: work is still prevprevblk pointer
LDR HpTemp, [tp, hpd]
ADDNE size, size, bp ; Increase grow amount by any remainder
MOVNE bp, #0 ; And make sure the block does die
CMP HpTemp, #Nil
ADDNE HpTemp, HpTemp, tp ; offset from heap start
SUBNE HpTemp, HpTemp, work
......@@ -1015,7 +1385,7 @@ copy_backwards
LDR r0, hpddebug
CMP r0, #0
BEQ %FT06
DREG HpTemp, "copying -4+",concat
DREG HpTemp, "copying -4+",cc
STRIM " from "
SUB R0, addr, hpd
BL PrintOffset
......@@ -1074,7 +1444,7 @@ try_add_preceding_block
BNE ext_delink
LDR work, hpdend
SUB work, work, bp ; get back real size
BIC work, work, #(freblksize-1)
BIC work, work, #3
ADD work, work, bp
STR work, hpdbase ; all free allocated
B ext_hack
......@@ -1111,6 +1481,12 @@ got_to_reallocate
B ReallocateInSafeZone
CorruptExtension
ADRL R0,ErrorBlock_HeapFail_BadLink
[ International
BL TranslateError
]
NaffExtension
Pull "addr, size, work"
B NaffHeapExit
......@@ -1522,7 +1898,7 @@ iShowHeap ROUT ; Internal entry point for debugging heap
BL PrintOffsetLine
SUB r0, work, bp ; hpdend-hpdbase
DREG r0,"Bytes free: ",concat, Word
DREG r0,"Bytes free: ",cc, Word
SUB r0, bp, addr ; hpdbase-hpdsize
DREG r0,", bytes used: ",, Word
SWI XOS_NewLine
......@@ -1605,7 +1981,7 @@ PrintOffset
DREG r0
CMP R0, #0
ADDNE R0, R0, hpd
DREG r0," (",concat
DREG r0," (",cc
STRIM ")"
GRAB "R0, PC"
......
......@@ -547,8 +547,7 @@ AliasOscliTooLong
AliasStr_QA = "ALIAS$", 0
]
AliasStr = "Alias$*", 0
AliasDot = "Alias$"
dotstring = ".", 0
AliasDot = "Alias$.", 0
ALIGN
[ Oscli_QuickAliases
......
......@@ -68,7 +68,10 @@ ScreenModeReason_SelectMode * 0
ScreenModeReason_ReturnMode * 1
ScreenModeReason_EnumerateModes * 2
ScreenModeReason_SelectMonitorType * 3
ScreenModeReason_Limit * 4
ScreenModeReason_ConfigureAcceleration * 4
ScreenModeReason_CleanCache * 5
ScreenModeReason_ForceCleanCache * 6
ScreenModeReason_Limit * 7
; Mode selector format
......
......@@ -1933,7 +1933,10 @@ ScreenModeSWI Entry
ASSERT ScreenModeReason_ReturnMode = 1
ASSERT ScreenModeReason_EnumerateModes = 2
ASSERT ScreenModeReason_SelectMonitorType = 3
ASSERT ScreenModeReason_Limit = 4
ASSERT ScreenModeReason_ConfigureAcceleration = 4
ASSERT ScreenModeReason_CleanCache = 5
ASSERT ScreenModeReason_ForceCleanCache = 6
ASSERT ScreenModeReason_Limit = 7
ScreenModeSub
CMP r0, #ScreenModeReason_Limit
......@@ -1943,6 +1946,9 @@ ScreenModeSub
B ScreenMode_ReturnMode
B ScreenMode_EnumerateModes
B ScreenMode_SelectMonitorType
B ScreenMode_ConfigureAcceleration
B ScreenMode_CleanCache
B ScreenMode_ForceCleanCache
; unknown OS_ScreenMode reason code
......@@ -2063,6 +2069,52 @@ ScreenMode_SelectMonitorType Entry "r0"
STR r1, [WsPtr, #CurrentMonitorType] ; update current value
EXIT
;**************************************************************************
;
; ScreenMode_ConfigureAcceleration - Configure screen memory cacheability
;
; Internal routine called by ScreenModeSWI
;
; in: r0 = reason code (4)
; r1 = flags:
; bit 0 : set to suspend cached screen until mode change
; bit 1 : set to suspend screen cleaning
; bit 2 : set to disable hardware acceleration
; other : reserved, must be 0
; or -1 to read current value
; r2 = number of VSyncs between automatic screen cleaning (1-3), or -1
; to read current value
;
; out: r1 = new flag state
; r2 = new number of VSyncs between automatic screen cleaning
; r10-r12 may be corrupted
; All other registers preserved
;
ScreenMode_ConfigureAcceleration
; Screen caching isn't supported yet. Just return dummy values.
MOV r1,#1
MOV r2,#1
MOV pc,lr
;**************************************************************************
;
; ScreenMode_CleanCache - Clean screen memory from cache, if cache enabled
; ScreenMode_ForceCleanCache - Force clean of screen memory from cache
;
; Internal routine called by ScreenModeSWI
;
; in: r0 = reason code (5 or 6)
;
; out: r10-r12 may be corrupted
; All other registers preserved
;
ScreenMode_CleanCache
ScreenMode_ForceCleanCache
; Screen caching isn't supported yet. Just do nothing.
MOV pc,lr
;;;mjsHAL - VIDCDividerSWI is horrible VIDC specific API, compiled out
;;;
; Should not cause any problems on any machine. STB flag just to be safe though.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment