Copyright (C) Acorn Computers Ltd. 1993 0197,276/FS Issue 2 **Live** Medusa Memory Management Software Functional Specification ========================================================== ----------------------------------------- | Drawing No : 0197,276/FS | | Issue : 2 **Live** | | Date : 15th April 1993 | | Author : Tim Dobson | | Sheets : n | | Last Issue: ? | ----------------------------------------- Contents ======== 1 History 2 Outstanding Issues 3 Overview 4 Technical Background 5 User Interface 5.1 TaskManager and large RAM sizes 6 Programmer Interface 6.1 Logical memory map 6.2 Free pool 6.3 New SWI: OS_DynamicArea 6.3.1 Reason codes 6.3.1.1 Create dynamic area 6.3.1.2 Remove dynamic area 6.3.1.3 Return information on dynamic area 6.3.1.4 Enumerate dynamic areas 6.3.1.5 Renumber dynamic area 6.3.2 Dynamic area handler routine 6.3.2.1 PreGrow 6.3.2.2 PostGrow 6.3.2.3 PreShrink 6.3.2.4 PostShrink 6.3.3 Sequence Of Actions When OS_ChangeDynamicArea Is Called 6.3.3.1 Service_PagesUnsafe 6.3.3.2 Service_PagesSafe 6.3.4 Implementation Notes for OS_ChangeDynamicArea 6.3.5 OS_DynamicArea Service Calls 6.3.5.1 Service_DynamicAreaCreate (&90) 6.3.5.2 Service_DynamicAreaRemove (&91) 6.3.5.2 Service_DynamicAreaRenumber (&92) 6.4 New SWI: OS_Memory 6.4.1 OS_Memory Reasons 0-5: Convert Memory Address 6.4.2 OS_Memory Reasons 6-8: Physical Memory 6.4.2.1 Read Physical Memory Arrangement Table Size 6.4.2.2 Read Physical Memory Arrangement Table 6.4.2.3 Read Amounts Of Various Sorts Of Memory 6.4.3 I/O Space Information 6.4.3.1 Read Controller Presence 6.5 Old SWI OS_SetMemMapEntries (&53) 6.6 Old SWI OS_ReadDynamicArea (&5C) 6.7 New SWI OS_ClaimProcessorVector 7 External Dependancies 8 Development Test Strategy 9 Organisation 10 Future Enhancements 10.1 Abort trappping 10.1.1 Technical background 10.1.2 New SWI: OS_AbortTrap 10.1.2.1 Add abort trap 10.1.2.2 Remove abort trap 1. History ========== TMD 05-Nov-92 Started initial draft TMD 08-Mar-93 More detail added Issue A TMD 21-Apr-93 Released for initial review JSR 11-May-93 Input from review entered JSR 24-May-93 Adjustments to area grow and deletion behaviour. Add 2 controllers to list: VIDC1 and VIDC20 to allow ScreenTricks module to operate. JSR 02-Jun-93 Added section to development test strategy for Wimp and Switcher testing. JSR 04-Jun-93 Comments on Issue 1 spec from DLowdell. 2. Outstanding Issues ===================== What if anything should be done to the task manager's task display to cope with large memory sizes? The handling of Wimp_ClaimFreeMemory. This SWI claims the free pool making it available for data transfers. The Wimp used to implement this by putting the free memory on the end of the current task. This is no longer necessarily going to work as there may not be any free space beyond the end of the current task's memory. Also, it is quite likely that there won't be enough space for all the free memory. 3. Overview =========== This specification covers changes to the memory management system in RISC OS for Medusa platforms. The main features of Medusa platforms which require changes to RISC OS's memory management are as follows:- 1) Ability to cope with RAM sizes up to 256+2Mbytes, rather than the current maximum of 16Mbytes; 2) Ability to control DMA; 3) Requirements for second processor card drivers to claim memory; 4) Physical RAM no longer contiguous; 5) ARM600 MMU requires page table allocation; 6) Logical memory map expansion due to 32-bit address space. 7*) Changes to support emulation of VIDC1; 8*) Changes to support line-doubling; Note: The implementation of the starred items above (VIDC1 emulation, line-doubling) are not part of the Medusa project; however it is an aim of the memory management work that it should be possible to implement these in a soft-loaded module at a later date. 4. Technical Background ======================= In RISC OS 2 and 3 (version 3.10) the memory management task was divided between the kernel and the wimp. The kernel ordered the memory into dynamic areas and an application space, and the wimp then bludgeoned the kernel into not getting in the way of the wimp's model of having a free pool and multiple application spaces, rather than just the one application space which had all spare memory in it. For Medusa the free pool management will be moved into the kernel so that there isn't such a wrestling match between the kernel and the wimp. The wimp will still manage the multiple application spaces - it being the module responsible for constructing and managing tasks in the desktop. The main kernel interface for memory management was OS_ChangeDynamicArea which simply allowed the resizing of dynamic areas. This has been added to for Medusa with the OS_DynamicArea SWI for the control, creation and deletion of dynamic areas. The dynamic area management in RISC OS 2 and 3 (version 3.10) was somewhat tied together with the dynamic area clients. The change dynamic area SWI specifically called other modules depending on which dynamic area was being resized. The result was that there was no flexibility with how dynamic areas were used. Other memory related services were not available. For example it was not possible to find out what memory or hardware was available on the system without knowing a great deal about the platform. Memory is referred to in three ways: physcial address, logical address and physical page number. The first two refer to the address of the memory in the physical address space (what IOMD presents to ARM) and logical address space (what the ARM memory manager presents to the ARM core) respectively. The physical page number is an arbitrary number assigned to RAM pages. It is necessary to have this last abstraction to save space. If it weren't there then the storage and management overheads would be prohibitive on a small memory machine, which, as Medusa is targetted at under 1000 pounds, is likely to be a majority of users. The way physical page numbers are allocated is that all the contiguous RAM section in physical address space are bunched together and the page numbers allocated from 0 upwards. There are several interfaces described here which use Page blocks. These are tables of 3-word records: word use 0 Physical page number 1 logical address 2 physical address That is, a block of memory 12*N bytes big where N is the number of records in the block. They are used to pass round lists of addresses or pages. 5. User Interface ================= 5.1 TaskManager and large RAM sizes ----------------------------------- The TaskManager module's 'Task display' window does not cope well in this area at present. The combination of a 16-fold increase in maximum RAM size and an 8-fold decrease in page size means that the existing linear 1 pixel-per-page model will not be practical on large memory machines. A number of options are possible:- a) Do nothing. It is rare that anyone will want to manually drag an area's size to anything particularly big. Most big programs will set their own Wimp slot size; b) Allow the user to click in the number fields and enter a number (presumably still in K); c) Make the scale exponential (ie at the small sizes it goes in units of 4K, but when the area gets bigger go in larger and larger steps). d) Make the scale a modified exponential (1+log(x)/c). 5.2 *-Command *Cache On/Off --------------------------- A module will be provided to give this functionality. It will switch both caching and write-buffering on and off. The corresponding SWIs will not be supplied as they are inapplicable to an ARM600/700 based system. 6. Programmer Interface ======================= 6.1 Logical Memory Map ---------------------- Address Size Use Public? 00000000 16k System workspace Private 00004000 16k Scratch space Public(1) 00008000 28M-32k Application space Public 01C00000 8k SVC stack Public(2) 01C02000 2M-8k System heap Private 01E00000 8k Undefined stack Public(2) 01E02000 1M-8k Soft CAM map Private 01F00000 32k Cursor/sound etc Private 01F08000 32k "nowhere" Private 01F10000 1M-64k Reserved for fake screen (480k) Private 02000000 12M RMA Public(3) 02C00000 4M Level 2 page tables Private 03000000 4M I/O space Private(4) 03400000 1M Reserved for VIDC1 emulation Private 03500000 1M VIDC20 Private 03600000 1M Reserved for Vinit &c emulation Private 03700000 1M Reserved for 2nd Proc ctrl regs Private 03800000 8M ROM Private 04000000 2G-64M Dynamic areas Public(5) 80000000 512M Copy of physical space Private A0000000 1 1/2 G More dynamic areas Public(5) Notes: 1) This may be used by any module. The rules for its use are: Not in an IRQ routine Not if you're going to call something which might use it while you are Example clients are: FileCore to hold structures whilst working out how to allocate some free space; Filer to hold structures for OS_HeapSort. 2) It may be assumed that these end on a 1M boundary and will exception if accessed beyond that end. However, the exact location of these stacks should not be assumed. 3) The location of RMA and its maximum size may not be assumed. However, it is guaranteed that it will be in low 64MBytes (ie can execute 26 bit code). 4) Except where particular device drivers export hardware addresses. 5) Only in so far as a client may make its own dynamic area. 6.2 Free pool ------------- Large RAM sizes pose a problem for RISC OS, because the existing metaphor that all spare memory lives in application space breaks down. The current limit on application space size is 16MB, whereas the maximum RAM size on Medusa platforms is 258MB. Although this limit can be moved up a little by moving other areas out of the way, it is not possible to extend it enough (eg I/O still has to live at &03000000 onwards). To cope with this a new dynamic area is created, known as the "free pool" (area number 6). The operation of SWI OS_ChangeDynamicArea is modified as follows:- a) When an area (other than the free pool) is grown, memory is taken from the free pool, if any exists (the current application is not notified of this). If having shrunk the free pool to zero size, there is still not enough memory for the growth, the kernel attempts to remove pages from the application space as it does under existing versions of RISC OS. b) When an area (other than the free pool) is shrunk, the pages recovered are added to the free pool. The current application is not consulted. c) If the free pool itself is grown, pages are taken from application space to put in it (and the application consulted beforehand). d) If the free pool is shrunk, the recovered pages are added to application space (the application is consulted beforehand). The WindowManager module also changes, to take this into account. In some ways its operation is simplified, as it no longer needs to maintain its own free pool. However the implementation of the following SWIs needs to change:- Wimp_TransferBlock at present puts all used task memory into application space, and then copies the relevant bits over. It cannot do this any more, as the total used task memory may not fit into application space. Instead it must do the following:- Split the transfer into chunks of no more than half of the maximum application memory size. For each chunk, put the appropriate pages of the source block in at the start of application space, and the appropriate pages of the destination block above these, then perform the transfer. After all chunks have been done, restore the current task's pages in application space. Wimp_ClaimFreeMemory needs to be modified, because the Wimp no longer maintains control of the free pool. 6.3 New SWI: OS_DynamicArea (&66) --------------------------------- This SWI performs miscellaneous operations on dynamic areas. On entry, r0 provides a reason code which determines which operation is performed. Note that as all operations on dynamic areas work in physical page numbers it is not possible to map anything other than RAM pages (DRAM and VRAM) into a dynamic area. In particular EASI space cannot be mapped in. 6.3.1 Reason codes ------------------ 6.3.1.1 Create dynamic area --------------------------- This call creates a new dynamic area. On entry: r0 = 0 (reason code) r1 = new area number (-1 => RISC OS should allocate number) (must not be 128-255 - see section 6.6) r2 = initial size of area (in bytes) r3 = base logical address of area (-1 => OS should allocate base address) r4 = area flags bits 0..3 = access privileges to be given to each page in the area (same format as for OS_Read/SetMemMapEntries) bit 4 = 0 => area is singly mapped = 1 => area is doubly mapped bits 5..31 must be zero r5 = maximum size of area, or -1 if the area should be capable of growing to the total RAM size of the machine r6 -> area handler routine, or 0 for no routine r7 = workspace pointer to be passed in r12 on entry to area handler (should be 0 if r6=0) r8 -> area description string (null terminated), eg "Font cache". This string will be displayed in the TaskManager window. On exit: r1 = allocated area number r3 = specified or allocated base address of area r5 = specified or allocated maximum size of area r0,r2,r4,r6-r8 preserved An error will be returned if: * the given area number clashes with an existing area, or * the logical address space occupied by the area at maximum size would intersect with any other area at maximum size, or * there is not enough contiguous logical address space to create the area, or * there is not enough memory in the free pool to allocate level 2 page tables to cover the area at maximum size. If r1 is -1 on entry the RISC OS will allocate an area number itself. This will be greater than or equal to 256. This means (see section 6.6) that OS_ReadDynamicArea on these areas will always return with r2 being the maximum area size. For singly mapped areas the base logical address is the lowest logical address used by that area. The area grows by adding pages at the high address end. For doubly mapped areas the base logical address is the (fixed) boundary between the two mappings: the first mapping ends at r3-1, and the second starts at r3. When one of these areas grows the pages in the first copy move down to accommodate the new pages at the end, and the second copy simply grows at the end. On entry, r6 points to the area handler routine which gets called with various reason codes when an an area is grown or shrunk. If zero is passed in, then no routine will be called, and any shrink or growth will be allowed. Details of the entry and exit conditions for this routine are given in section 6.3.2 below. The area is created initially with size zero (ie no pages assigned to it), and is then grown to the size specified in size r2, which involves the area handler being called in the same way as if OS_ChangeDynamicArea was called to grow the area. The area is created with a maximum size equal to either the amount given in r5 on entry, or the total RAM size of the machine, if this is smaller. If r5 is -1 on entry, then the maximum size will be set to the total RAM size of the machine. If r3 on entry is -1, then RISC OS allocates a free area of logical address space which is big enough for the maximum size of the area. Once the area has been created Service_DynamicAreaCreate will be issued to inform the rest of the system about this change. Notes for application writers ----------------------------- The following facilities: * the ability to create areas with specific area numbers * the ability to create areas at specific logical addresses * the ability to create doubly-mapped areas are intended for internal system use only. Applications should in general create only singly-mapped areas, and request that RISC OS allocate area numbers and logical addresses. This will prevent clashes of area numbers or addresses. 6.3.1.2 Remove dynamic area --------------------------- This call removes a previously created dynamic area. On entry: r0 = 1 (reason code) r1 = area number On exit: All registers preserved An error is returned if the area was not removed for any reason. Before the area is removed, RISC OS attempts to shrink it to zero size. This is done using ChangeDynamicArea. If the ChangeDynamicArea returns an error then the area will be grown back to its original size using ChangeDynamicArea and the remove dynamic area call will return with an error. If the ChangeDynamicArea to reduce the area to 0 size worked then the area will be removed. Once the area has been removed Service_DynamicAreaRemove will be issued to inform the rest of the system about this change. 6.3.1.3 Return information on dynamic area ------------------------------------------ This call returns various information on a dynamic area. On entry: r0 = 2 (reason code) r1 = area number On exit: r2 = current size of area (in bytes) r3 = base logical address of area r4 = area flags r5 = maximum size of area r6 -> area handler routine r7 = workspace pointer for area handler r8 -> area description string (null terminated) Note that for doubly-mapped areas, r3 on exit from this call returns the address of the boundary between the first and second copies of the area, whereas OS_ReadDynamicArea returns the start address of the first copy (for backwards compatibility). 6.3.1.4 Enumerate dynamic areas ------------------------------- On entry: r0 = 3 (reason code) r1 = area number or -1 On exit: r1 = next area number or -1 This allows an application to find out what dynamic areas are defined. -1 is used to start the enumeration and -1 indicates that the enumeration has finished. 6.3.1.5 Renumber dynamic area ----------------------------- This call renumbers a dynamic area. On entry: r0 = 4 (reason code) r1 = old area number r2 = new area number On exit: All registers preserved An error is returned if the area specified by the old area number does not exist or if the new number clashes with an existing area. This call is intended for system use only. Once the dynamic area has been renumbered Service_DynamicAreaRenumber will be issued to inform the rest of the system about this change. 6.3.1.6 Read size of application space -------------------------------------- This call returns the maximum size of application space On entry: r0 = 5 (reason code) On exit: r5 = maximum size of application space This call is intended for system use only. 6.3.2 Dynamic area handler routine ---------------------------------- This section describes the reason codes passed to the dynamic area handler routine, with their entry and exit conditions. This routine is called when the size of an area is being changed. On entry, r0 contains a reason code, which describes what is happening. It should be noted that when called, OS_ChangeDynamicArea is currently at work and will reject requests to resize dynamic areas. As a consequence any SWIs which might resize a dynamic area should be avoided. Such things as OS_Module to claim some workspace are an example, and hence most file operations should be avoided (although I/O on an existing file is more safe than other operations). The reason codes are as follows:- 6.3.2.1 PreGrow (0) ------------------- This reason code is issued when a call to OS_ChangeDynamicArea results in an area growing. It is called before any pages are actually moved. It allows the handler to specify particular physical pages if it needs them (eg for the screen area), or to object to the size change. On entry: r0 = 0 (reason code) r1 -> Page block The physical page number entries will be set to -1 r2 = number of entries in Page block (= number of pages area is growing by) r3 = number of bytes area is growing by (= r2 * pagesize) r4 = current size of area (bytes) r5 = page size r12 -> workspace On exit: If the growth is OK, then r0 is preserved If particular physical page numbers are required then all the physical page number entries must be filled in with the required pages. The other entries must be left alone. V = 0 else r0 -> error to return, or zero to return generic error V = 1 endif All other registers preseved This call permits the dynamic area handler to request that specific pages be used for growing the area. If this is the case then all pages must be specified. The correspondence between the Page block and memory is that the first entry in the page block corresponds to the lowest memory address of the extension, and the last entry in the Page block the highest memory address. If an error is returned, then the area will not change size. 6.3.2.2 PostGrow (1) -------------------- This reason code is issued when a call to OS_ChangeDynamicArea results in an area growing. It is called after the PreGrow reason code has been issued successfully and the memory pages have been moved. It provides the handler with a list of which physical pages have been moved into the area. On entry: r0 = 1 (reason code) r1 -> Page block Only the physical page number entries are defined r2 = number of entries in Page block (= number of pages area grew by) r3 = number of bytes area grew by r4 = new size of area (bytes) r5 = page size r12 -> workspace On exit: All registers preserved 6.3.2.3 PreShrink (2) --------------------- This reason code is issued when a call to OS_ChangeDynamicArea results in an area shrinking. It is called before any pages are moved. It allows the handler to limit the amount of memory moved out of the area, or to object to the size change altogether. The shrink amount alowed as returned by this reason code is permitted to be a non-page multiple. The ChangeDynamicArea code will ensure the shrink permitted is rounded down to a page multiple before it is actioned. On entry: r0 = 2 (reason code) r3 = number of bytes area is shrinking by r4 = current size of area (bytes) r5 = page size r12 -> workspace On exit: If shrink (even by reduced amount) is OK, then r0 preserved r3 = number of bytes area can shrink by. This must be less than or equal to r3 on entry. V = 0 else r0 -> error block, or zero to return generic error r3 = 0 V = 1 endif All other registers preserved 6.3.2.4 PostShrink (3) ---------------------- This reason code is issued when a call to OS_ChangeDynamicArea results in an area shrinking. It is always called after the PreShrink reason code has been issued successfully even if the memory pages can't be moved. On entry: r0 = 3 (reason code) r3 = number of bytes area shrunk by r4 = new size of area (bytes) r5 = page size r12 -> workspace On exit: All registers preserved 6.3.3 Sequence Of Actions When OS_ChangeDynamicArea Is Called ------------------------------------------------------------- This section has been provided to give an overview of what happens when a dynamic area's size is changed. This is presented as pseudo-code for clarity. Check IRQSemaphore - reject CDA if set Growing free pool: (no check for page availability - do as much as possible!) Application space being shrunk - confirm it's OK: If CAO in application space then UpCall_MovingMemory (asks application if it consents to memory move) If UpCall *not* claimed Then reject CDA Else Service_Memory (asks modules for objectors to the memory move) If Service *is* claimed then reject CDA EndIf Move pages from application space end to free pool Growing other dynamic area: Check for page availability - if not enough available bounce CDA with error Table of pages prepared: Allocates memory Fills in -1s for 'any page' PreGrow is called: replaces -1s if it wants objects about resize amount perhaps Check for unavailable pages: If there is a non -1 which can't be grabbed then reject CDA Check for application space resizing: If free pool < amount needed then If CAO in application space then UpCall_MovingMemory (asks application if it consents to memory move) If UpCall *not* claimed Then reject CDA Else Service_Memory (asks modules for objectors to the memory move) If Service *is* claimed then reject CDA EndIf EndIf Page replacements determined: Work out swap sequences on all non -1s Replace all -1s with actual pages Pages get grabbed first from the free pool, then underflowing into the application space Issue Service_PagesUnsafe (only if PreGrow specified pages) Pages get moved around: Do the page moving/swapping (don't swap if pages requested are in free pool) Issue Service_PagesSafe (only if PreGrow specified pages) PostGrow is called: Sorts out structures for the new size Shrinking free pool: Check if application space OK to grow: If application space < maximum then If CAO in application space then UpCall_MovingMemory (asks application if it consents to memory move) If UpCall *not* claimed Then reject CDA Else Service_Memory (asks modules for objectors to the memory move) If Service *is* claimed then reject CDA EndIf EndIf Move pages from free pool to application space Shrinking other dynamic area: PreShrink is called: objects about resize amount perhaps, or gives larger allowed size Sorts out structures for the new smaller size as the shrink will definitely go ahead. Pages get moved around: Move pages from dynamic area to free pool PostShrink is called: Keep subsystem informed. It should be noted that the system stack is used for the page structure passed to the PreGrow routine. As a consequence there is a limit to the amount that an area can be grown by at one time. To get round this problem an area grow request of a large amount will be performed in several steps. If one of these steps fails then the grow will terminate early with the area grown by however much was achieved, but not by the full amount requested. You will notice two new service calls were used here: Service_PagesUnsafe Service_PagesSafe which are issued around page swapping to inform any DMA subsystems (eg IOMD DMA or second processor) that some pages are being swapped around. Here is the detailed description of these service calls: 6.3.3.1 Service_PagesUnsafe --------------------------- On entry: r1 = Service_PagesUnsafe r2 = Page block filled in by the PreGrow routine with the two address fields filled in too. r3 = number of entries in Page block On exit: All registers preserved The recipient of this service call is being told that the pages specified are about to be swapped around. Direct memory access activities involving the specified pages should be suspended until Service_PagesSafe has been received indicating the pages are safe. 6.3.3.2 Service_PagesSafe ------------------------- On entry: r1 = Service_PagesSafe (&8F) r2 = Number of entries in each Page block r3 -> Page block before move r4 -> Page block after move On exit: All registers preserved The recipient of this service call is being told that the pages specified have been swapped for different pages and what those different pages are. Note that the logical addresses in both Page blocks will match. The 'before' Page block will contain the physical page numbers and physical addresses of the pages which were replaced, and the 'after' block the page numbers and physical addresses of the pages which replaced them. 6.3.4 Implementation Notes for OS_ChangeDynamicArea --------------------------------------------------- There is an issue with OS_ChangeDynamicArea when a particular page is requested by the growing dynamic area which is currently in use by the page tables. The problem is that moving pages that themselves control where the pages are is a tricky operation. This is exacerbated on level 1 page tables even more because these are 16k (4 pages) big and must be 16k aligned. This means that if a level 1 page table needs moving then another 4 page block needs to be found - potentially resulting in more page swapping to make such a gap. Level 2 page tables don't have this problem as they're 4k (1 page) big. Having said this, unless some mobility is permitted the minimum configuration which would permit a '486 second processor to work would be 4MBytes. In a 2MByte machine 1M is left free of page tables to allow the screen to grow, and the 2nd MByte would, as a consequence, be 'contaminated' with page tables and so would be unavailable for the 2nd processor. If the page tables could be moved they could reside in the 'screen' MByte as they could be moved freely about if the screen needed to grow. 6.3.5 OS_DynamicArea Service Calls ---------------------------------- These service calls are designed to keep the rest of the system informed about changes to the dynamic areas. Their primary customer is the task manager, although other modules could make use of them. 6.3.5.1 Service_DynamicAreaCreate (&90) --------------------------------------- On entry: r1 = Service_DynamicAreaCreate (&90) r2 = area number of area just created On exit: All registers preserved This service must not be claimed This service is issued just after the successful creation of a dynamic area. 6.3.5.2 Service_DynamicAreaRemove (&91) --------------------------------------- On entry: r1 = Service_DynamicAreaRemove (&91) r2 = area number of area about to be removed On exit: All registers preserved This service must not be claimed This service is issued just before the removal of a dynamic area. It is issued during a call to OS_DynamicArea(1), after the area has been successfully reduced to zero size, but before it has been removed completely. 6.3.5.2 Service_DynamicAreaRenumber (&92) ----------------------------------------- On entry: r1 = Service_DynamicAreaRenumber (&92) r2 = old area number r3 = new area number On exit: All registers preserved This service must not be claimed This service is issued during a call to OS_DynamicArea(2), ie when an area is being renumbered. 6.4 New SWI: OS_Memory ---------------------- This SWI performs miscellaneous operations for memory management. On entry: r0 = reason code and flags. Bits o-7 are the reason code, bits 8-31 are the flags which may be specific to the reason code. The other registers are specific to the reason code. On exit: The returned values are specific to the reason codes. Here are the defined reason codes: 0-5: Page block Operations 0 - General Page block Operation 1-5 - reserved 6-8 - physical memory: 6 - read physical memory arrangement table size 7 - read physical memory arrangement table 8 - read amounts of various sorts of memory 9-? - I/O space information: 9 - read controller presence The details of these are given below. 6.4.1 OS_Memory Reason 0: General Page block Operation ------------------------------------------------------ This reason code is used to convert between representations of memory addresses. The different memory spaces are logical memory, physical memory and physical pages. On entry: r0 = flags: bit meaning 0-7 reason code (0-5) 8-9 which entry is defined in the Page block: 0 - Physical page number 1 - Logcial address 2 - Physical address 10 Physical page number will be filled in when set 11 Logical address will be filled in when set 12 Physical address will be filled in when set 13-14 Cachability control: 0 - no change 1 - no change 2 - disable caching on these pages 3 - enable caching on these pages 15-31 reserved - set to 0 r1 -> Page block r2 = number of entries in page block On exit: Page block updated as necessary The Page block will be scanned and the specified operations applied to it. It is possible to do address conversions and control the cachability on a per-page basis. If any page is found to be unconvertable or non-existent then an error will be returned and the cachability will be unaffected. Cachability is accumulated for each page. So, for example, if there are 5 clients which need caching turned off on a page then each of them must turn caching back on individually for that page actually to become cached again. Where an ambiguity may occur, for example in doubly-mapped areas such as the screen, one of the possible results will be chosen and filled in. This will only handle RAM addresses. The address fields may be non-page aligned. 6.4.2 OS_Memory Reasons 6-8: Physical Memory -------------------------------------------- These are provided to enable a program to find out what physical memory there is and its arrangement. The first two calls provide complete information on the available memory. The information is provided in the form of a table, with each page of physical memory space having one entry in the table. Due to the large number of pages the table is packed down to only 4 bits per page. In each byte of the table the low order 4 bits correspond to the page before the high order 4 bits, ie it is little-endian. This is the meaning of a nibble in the table: bit meaning 0-2 type of memory: 0 not present 1 DRAM 2 VRAM 3 ROM 4 I/O 5-7 Undefined 3 0 - Page available for allocation 1 - Page not available for allocation The page availability is based on whether it is RAM, and whether it has already been allocated in such a way that it can't be replaced with a different RAM page eg the OS's page tables or screen memory. The third call gives a summary of available memory. 6.4.2.1 Read Physical Memory Arrangement Table Size --------------------------------------------------- On entry: r0 = 6 (bits 8-31 clear) On exit: r1 = table size (bytes) r2 = page size (bytes) This returns information about the memory arrangement table. 6.4.2.2 Read Physical Memory Arrangement Table ----------------------------------------------- On entry: r0 = 7 (bits 8-31 clear) r1 = pointer to table to be filled in On exit: registers preserved This returns the physical memory arrangement table in the block of memory pointed at by r1. Note the information about page availability may well change between this being called before a OS_ChangeDynamicArea and the PreGrow routine being called, in particular it may have been necessary for OS_ChangeDynamicArea to allocate level 2 page tables for the grown area, and these are not available for allocation. Hence, for applications which require, say, all pages from physical address 0 onwards their PreGrow handler must make this call, rather than the information being extracted and held before OS_ChangeDynamicArea being called. 6.4.2.3 Read Amounts Of Various Sorts Of Memory ----------------------------------------------- On entry: r0 = bits meaning 0-7 must be 8 - its the reason code 8-11 the type of memory: 1 - DRAM 2 - VRAM 3 - ROM 4 - I/O 12-31 reserved - set to 0 On exit: r1 = number of pages of that sort of memory r2 = page size (in bytes) 6.4.3 I/O Space Information --------------------------- These give information about the I/O space. Controllers are identified by type and sequence number so that a machine could be constructed with, say, more than one IDE controller in it. 6.4.3.1 Read Controller Presence -------------------------------- On entry: r0 = 9 (bits 8-31 clear) r1 = controller ID bit meaning 0-7 controller sequence number 8-31 controller type: 0 - EASI card access speed control 1 - EASI space 2 - VIDC1 3 - VIDC20 On exit: r1 = controller base address or 0 if not present. This returns the location of a controller on the given machine. For example the EASI space gives the base address of podule N where N is the sequence number given. This reason code is provided for internal use only and is documented here for completeness' sake. In particular you must use the Podule manager to get at this information and to control your podule's EASI space access speed. 6.5 Old SWI OS_SetMemMapEntries (&53) ------------------------------------- As noted in the RISC OS 3 PRMs -1 should be used to indicate that a page should become inaccessible, for future compatibility. In the Medusa kernel the future has arrived - only -1 will work! 6.6 Old SWI OS_ReadDynamicArea (&5C) ------------------------------------ As noted in the RISC OS 3 PRMs if bit 7 of the dynamic area number is set then r2 will be returned with the maximum area size. This is being changed slightly to be that if the dynamic area number passed in is greater than or equal to 128 then r2 will be returned as the dynamic area size. Also, if the dynamic area number passed in is between 128 and 255 inclusive then the information will be returned for the area whose number is 128 less than the passed-in value. The net result is that for old dynamic area numbers (0-5) the functionality is unchanged, but the number-space impact of the interface is minimised - it was prety horrible to have to force bit 7 of all dynamic area numbers to be clear just for this SWI, so we just prohibit a small patch of low numbers instead. 6.7 New SWI OS_ClaimProcessorVector ----------------------------------- In r0=Vector and flags bit meaning 0-7 Vector number: 0 - 'Branch through 0' vector 1 - Undefined instruction 2 - SWI 3 - Prefetch abort 4 - data abort 5 - address exception (only on ARM 2 & 3) 6 - IRQ 7+ - reserved for future use 8 0=release, 1=claim 9-31 reserved, must be 0 r1=replacement value r2=value which should currently be on vector (only needed for release) Out r1=value which has been replaced (only returned on claim) This SWI provides a means whereby a module can attach itself to one of the processor's vectors. This is a direct attachment - you get no environment except what the processor provides. As such, claiming and releasing the vectors is somewhat primitive - the claims and releases must occur in the right order (the release order being the reverse of claim order). On release if the value in r2 doesn't match what is currently on the vector then an error will be returned. This ensures correct chaining of claims and releases. [ Implementation note: On break the 1st ROM instruction gets copied to location 0 and branched to (MOV pc, #0). This used to be a branch direct (B thing) instruction, but will be changed to a branch indirect (LDR pc, [pc, #thing]). This means the indirection vector must be filled in with the correct reset address. Hence, the reset vector must be a different indirection vector to the 'Branch through 0' one. ] 7. External Dependencies ========================= The development of the new memory management system relies upon the availability of ARM600 (or 610) processor boards for A540s. The Window Manager will need modification to cope with the changed memory management system. In particular it will need to: Cope with the free pool being external to the wimp Change how it does TransferBlock operations Change how it gets the pages for new tasks 8. Development Test Strategy ============================= PHTester scripts will be writen to exercise the following SWIs: OS_Memory All reasons (0-9) In range, edge of range, just outside range and wildly out of range values will be tried. Where buffer contents are returned the buffer will be output. OS_AbortTrap Where this implemented then the test strategy would be as follows. Due to the nature of this SWI unsupported PHTester scripts will not be able to do the job properly. A piece of support code will be written which can be loaded into RMA and will monitor calls to the abort trapper. A PHTester script will be written which will load this trapper, add it, exercise it and monitor the results. The trapper will then be removed twice to make sure the correct behaviour results. OS_ChangeDynamicArea Again, this is hard to test with straight PHTester scripts. Under normal circumstance the presence of PHTester running will prohibit dynamic areas growing, however, this can be got around by growing a dynamic area before PHTester starts, starting the script then shrink the dynamic area thus giving the system a free pool (PHTester will refuse to allow the application space to resize and thus the memory will be pushed into the free pool). Support code will be writen which will monitor service calls and upcalls and will exercise the various cases. This act of exercising must be done without PHTester running. OS_DynamicArea Again, support code will be needed for PHTester to be usable. With this support code PHTester scripts will be writen to add (twice) and remove (twice) a dynamic area. This area will then be resized testing success and failure of both grow and shrink, failing because of either not enough memory or unavilable specific pages being requeested. A straight PHTester script will be used to exercise enumeration, readinfo and renumber operations. Testing the Wimp and Switcher Wimp: SWI Wimp_SlotSize The behaviour of this SWI depends on a number of external (To the task and calling parameters) factors: - Environment, particularly MemoryLimit and ApplicationSpace - Machine state, eg. remaining amount of memory - Page size, to compare with ARM 3 systems units of 32K should be used Shrinking a slot: Needs to be tested when MemLimit <> AppSpace, when MemLimit=AppSpace < RealSpace where RealSpace is actual memory mapped in- This feature is used extensively by flex. When a shrink succeedes, the free pool should grow by the shrinkage and the RMA by a roughly proportional ammount (for slot being reduced) Growing a slot: Again Different environments need testing, but also effects near total memory usage: Eg. A large increase is requested, but only a smaller amount is available. Also when the area has been grown, there may not be enough RMA to extend the Memory Map slot and so the slot shrinks by a page. In both cases the SWI should return with the environment set to the real size and a message sent to the switcher. Application testing. As mentioned above, flex makes good use of the features of this SWI. Loading a file into !Draw or !Paint should make the slot grow by a comparable amount to the file size (taking into account the internal storage methods). When the document is discarded the slot should reduce to the value it was before (unless flex/malloc usage makes this impossible) and total RMA/system memory should be roughly what it was, though of course for a first document load the application may claim additional memory. A small application could be written to do similar things repeatedly and could this give a measure to memory leaks etc. Medusa is bound to be slower because of smaller page size, more pages (which need to be scanned when growing a slot) and Log-Phys map (Level 2 table) being in RAM rather than on chip. SWI Wimp_TransferBlock Many possible cases: Should always be possible even if no free memory. For tasks a,b,c (c is current task) a <-> b a <-> c c <-> c For small and large lengths (eg. 1K or 8Meg) For when c takes up all/most of app space For when transfers occur to/from non application space (eg. RMA) For non word aligned/ size transfers For transfers involving over-the-page memory, eg. 34k-38k (32-36,36-40 pages) Application testing. RAM transfer from !Paint to !Draw (this is c->a,small, prob aligned) Use of !SqlFiler and !Faults (this c(RMA)->a,small, prob aligned) Some combinations can only be tested on real Medusa's with 32Meg memory. SWI Wimp_ClaimFreeMemory Only claimable once at a time, memory should be accessible and remain intact until free'd (incidentally can be freed by any task). System testing *copy, *compact ? Mode changing: SWI Wimp_SetMode, *wimpmode numbers 0-255 as before (testable by script which then checks screen memory usage and VDU variables) and new style configuration (again script can work out from config the mem usage and VDU vars). Wimp should shrink screen memory as much as possible and send mode changed message to all tasks Switcher: Dynamic areas used to be scanned (finding addresses for all the system pages) now RDA is used. Dynamic areas (all, including app-created areas) should update when window is open and a task calls CDA successfully. Areas which are non-fixed should be draggable, should reflect/update area as seen by a task. Dragging bars should update memory text as appropriate, on medusa platforms with more than 4Meg this should be logarithmic for large values. Switcher should behave as before on non medusa platforms. 9. Organisation ================ The software described herein resides in the RISC OS ROM. 10. Future Enhancements ======================= Some sort of system to request memory to be freed in cases of memory shortage might be nice. OS_Heap to be extended to allow allocation of anchored moveable blocks. This would be used to prevent heap fragmentation. 10.1 New SWI: OS_AbortTrap (&67) -------------------------------- This has been moved to the future enhancements section as this is not required by the project specification, there is insufficient time to do it and it can be added later if needed. Also, there are some unresolved issues regarding handling aborts of writes to &00-&1f and handling of coprocessor data transfer operations. 10.1.1 Technical background --------------------------- The extra processor modes in the ARM6 and ARM7 processor cores allow for complete recovery from data aborts, even if the abort happens while in privileged modes, such as supervisor mode. This was not possible on earlier processors, because when the abort was taken, the return address (and condition codes) was placed in r14_svc, thereby corrupting it. This allows for (amongst other things) complete software emulation of hardware devices (apart from devices which cause interrupts, or which are very time-critical). In particular, it becomes possible to write a module which emulates VIDC1 on systems which are VIDC20 based (provided that the address map is set up so that the address where VIDC1 is accessed (&03400000) causes a data abort when written to). In order to facilitate this kind of activity, RISC OS allows a module to provide a trap routine for accesses within specified address ranges. This is an additional feature over and above the usual abort handlers which are owned by the current application. When a data abort happens, the OS works out the range of addresses accessed by the instruction. It then looks through its MMU page tables, and splits the range into sub-ranges which correspond to accesses within one page or section, as appropriate. For each sub-range, it then checks the access privileges to see if the access was valid. If so, it performs the access itself. (For a load, it transfers the data into a temporary stack frame from which the register(s) will be subsequently loaded. For a store, the stack frame is already set up with the register(s) to be stored.) If the sub-range corresponds to a page or section which would have caused an abort, the sub-range is then checked against its list of abort traps. Starting with the lowest address in the sub-range, if the address is contained within any of the address ranges specified in the list, then the corresponding routine is called. The registers on entry and on exit are as follows:- On entry: r0 = flags bits 1,0 = 00 undefined 01 store 10 load 11 SWP bit 2 = 0 => user mode access 1 => privileged mode access bits 3..31 undefined r1 -> block of registers to be transferred to/from r2 = lowest address which faulted in the specifed address range r3 = number of transferred bytes which fall within the specified address range r4 -> instruction which aborted r12 = workspace ptr specified in node SVC26 (at the moment - I might change this to SVC32 at some stage) On exit: VC => instruction processed, please complete writeback VS => instruction shouldn't be allowed - generate exception All registers preserved (apart from PSR) If the routine wishes to accept the transfer as valid, it must perform the transfer itself by transferring the data (of length r3 bytes) in or out of the block pointed to by r1. (In the case of the SWP instruction it must transfer data out of, then into the block). It should then exit with V=0. The OS will then advance its address pointer by the number of bytes processed by this routine, and continue with the next sub-range (if there are any more to do). If the routine wishes to fault the transfer after all, it should exit with V=1. This will cause the OS to call the normal data abort handler. (This also happens if an address in a sub-range which faulted does not lie in any address range on the list). Note: this behaviour currently precludes having two or more abort traps on the list which have overlapping address ranges, where each trap actually is interested in a subset of accesses within that range. At the moment, the OS faults if any trap routine exits VS, whereas it could carry on down the list looking for further trap ranges containing the address. But then you would have to have some way of returning saying you had done part of the transfer. In addition to trapping page and section faults, the OS has special code to deal with writes to addresses in the range &00 to &1F inclusive, which cause aborts on ARM6/7 when the processor is executing in a 26-bit PC mode (assuming we are in the 32-bit PC configuration, which we are). If the address range for the transfer includes an address in the range 0-&1F, and the aborting instruction is within the RISC OS "ROM" image, then RISC OS executes the transfer itself (in 32-bit PC mode), as ROM code is considered kosher. This is mainly to allow FIQ claimants such as ADFS floppy drivers or Econet to set up their FIQ code while executing in 26-bit PC mode. It also allows the FPE to set up the undefined instruction vector (although of course the FPE still has to know that it will get called in undef_32 mode). If the aborting instruction is not in the ROM image, then the usual abort list is checked. If however the address is not in the list, or the routine exits with V set, then instead of calling the current data abort handler, the OS calls its default data abort handler (this was so I could then do a *ShowRegs and find out where the program went bang, rather than have the C exception handler swallow it up). Note that if you set up a node to respond to vector poking, and you want to accept the transfer, you'll have to switch to SVC32 to execute it (this is why I may change the entry to be called in SVC32. 10.1.2 New SWI: OS_AbortTrap (&66) ---------------------------------- SWI OS_AbortTrap provides calls to add or remove abort trap routines. On entry, r0 provides a reason code which determines which operation is performed. 10.1.2.1 Add abort trap (0) --------------------------- This reason code adds a trap routine to RISC OS's list. On entry: r0 = 0 (reason code) r1 = lowest trapped address r2 = highest trapped address +1 r3 = address of trap routine r4 = workspace pointer for trap routine On exit: All registers preserved The entry and exit conditions for the trap routine are described in section x.y.z. 10.1.2.2 Remove abort trap (1) ------------------------------ This reason code removes a trap routine from RISC OS's list. On entry: r0 = 1 (reason code) r1 = lowest trapped address r2 = highest trapped address +1 r3 = address of trap routine r4 = workspace pointer for trap routine On exit: All registers preserved Registers r1 to r4 on entry should be identical to those previously passed to reason code 0.