Traditionally, 32 bit ARM CPUs have had a 32 bit address bus. ARMv6 was the first architecture version to break this limit, by adding support for supersections – page table entries that can support a physical address up to 40 bits wide, but with the restriction that the mapping granularity is 16MB. This was fine for IO devices, but awkward for RAM, where the OS will typically want to the freedom to use 4KB pages. To improve on this ARM devised a new page table format (the so-called “Long descriptor” format), and released it as part of the ARMv7 Large Physical Address Extensions. Amongst other changes, this new format allows all page sizes to be used across the whole address space. For AArch32, the supported page sizes are 4KB, 2MB and 1GB, with a maximum physical address space of 40 bits. For AArch64 the physical address space can reach a size of 52 bits.
The Raspberry Pi 4B can support up to 8GB of RAM, with half of that RAM being located above the 32 bit physical address space barrier supported by the older “Short descriptor” page table format that RISC OS 5 has previously been limited to. Similarly, the 4GB version of the IGEPv5 also places half of its RAM above the 32 bit barrier. On current versions of RISC OS 5 this means that for both machines, only half of the fitted RAM can be used.
The goals of the changes are therefore:
Low RAM – RAM which can be accessed using a 32 bit physical address
High RAM – RAM which cannot be accessed using a 32 bit physical address (because it needs a 33 bit or larger address)
Soft CAM – Originally this was a soft copy of the MEMC page tables (which were stored in “Content Addressable Memory” that was write-only from the perspective of the CPU). On ARMv3+ CPUs a completely different page table scheme is used by the integrated MMUs, but the “Soft CAM” is still an important bookkeeping data structure to help the OS to keep track of where RAM is mapped, who owns it, and its access permissions.
This historic batch of work comprised the following notable changes:
Preparation for long descriptor page table support
Initial long descriptor page table support
8GB RAM support for BCM2835 & 4GB RAM support for OMAP5
Also note that current kernel builds do not enable the long descriptor page table support.
This kernel merge request comprised several major changes to the kernel and memory-related APIs:
CPUFlag_HighRAM
), which will be set if any high RAM is present, and thus whether software should prefer to use APIs which support 64 bit physical addresses over the older APIs that are limited to 32 bit addresses. The older APIs may fail with an error or have sub-optimal support when used with high RAM locations.The above API changes apply to all kernel builds, regardless of whether long descriptor page tables are supported/enabled or not.
Also note that current kernel builds do not enable the long descriptor page table support.
This set of changes (with the correct kernel build options) allow for the full amount of RAM on the 8GB Pi 4 and 4GB IGEPv5 to be used by the OS.
SATADriver was changed to use OS_Memory 19 if (a) high RAM is being used by the OS and (b) the SATA controller supports 64 bit physical addresses, to ensure that transfers aren’t needlessly performed via bounce buffers.
However in practice this doesn’t yield any improvements for current devices, because the bus/interconnect architecture on the OMAP5/IGEPv5 means that the high RAM is only accessible to the CPU.
The short descriptor page table format supported a “user read-only, privileged read-write” access permission (aka AP 1) which RISC OS has made heavy use of. The long descriptor page table format lacks support for this access mode, and the temporary workaround employed by earlier changes of widening the permissions to “user read-write, privileged read-write” is clearly a step backwards in terms of security.
A review of memory areas revealed that the ROM makes use of this permission in the following areas:
To deal with the kernel/HAL areas, and the wider problem of dynamic areas created by third-party software, the decision was taken to have the kernel map the areas as “user none, privileged read/write” and to employ an abort handler to emulate any usermode read operations. A natural extension of this was to provide a full implementation of the OS_AbortTrap mechanism that was first appeared in RISC OS Select:
Once AbortTrap was implemented it was fairly easy to have the kernel emulate usermode access to AP 1 areas. Apart from emulating usermode reads when the long descriptor page table format is in use, this also removes the usermode execute permission, so it’s possible that this may break some third-party software.
The ARMEABISupport module used by GCC currently uses OS_Memory 0 to attempt to generate a unique identifier for the running task, based on the physical address of the first page of application space. This is unsuitable for a couple of reasons:
A modified version of the module was developed which instead uses the Wimp task handle to identify the task, with the help of Wimp pre-poll and post-poll filters to ensure the code always knows what the active task is (the module contains an abort handler that needs to be know the active task). However pre-poll and post-poll filters were found to be insufficient for this task.
Some other solutions for identifying the active task were suggested on the forums, but as yet no decision has been made on which approach to take.
The decision to restrict the page replacement strategy (used by Service_PagesUnsafe etc.) so that low RAM pages can only ever be replaced by other low RAM pages can result in OS functionality being seriously impaired if all the low RAM pages have already been allocated to DAs, by preventing other programs from which need to use those pages from being able to use them.
The page allocation strategy can also have a negative impact on the performance of device drivers which are only capable of performing DMA to/from low RAM. E.g. on IGEPv5, creating a 2GB RAM disc on startup within Predesk will severely impair SATADriver’s performance.
Solutions to these problems need to be investigated (for SATADriver, simply increasing the bounce buffer size may be sufficient to adequately reduce the performance impact).
No current system requires this, but it’s a useful future-proofing step for future systems.
The DMA controllers on the Pi 4 are able to use high RAM addresses, so would be an obvious choice to use when testing the changes.
The softload tool needs significant changes in order to allow it to work on systems with long descriptor page tables, and to fix a number of historic flaws which somehow haven’t caused any problems with existing platforms where the tool is supported:
Flaws in the current code (startnew.s):
It should be possible to fix these problems by changing the code to take the following approach:
By performing the steps in the above order, it should minimise the number of memory regions which could conflict with the flat-mapped page, making it easier to find a page that will work (and decreasing the chances of us missing a dependency and then having the code break due to an address clash)
When disabling the caches it may also be necessary to disable cacheable pagetables, however the kernel routine that’s responsible for this (MakePageTablesNonCacheable) doesn’t seem to be reachable any more.
It’s also worth considering making the kernel responsible for performing the last few steps itself, as this will provide the best protection against future changes or device/implementation-specific quirks.
Around the time Physical Memory Pools were implemented, a number of memory-related APIs were updated to clamp the max amount of memory they’d report to 2GB-4KB, to try and avoid any 32bit signed number overflow bugs.
Now that we have machines with 4GB or more of RAM (notably 4GB & 8GB Raspberry Pi 4B’s, which will have 4GB of RAM available in short descriptor versions of RISC OS), it’s been discovered that the 2GB-4KB limit is insufficient to avoid some software from failing. Some of this software has already been fixed, but if bugs in other software persist then it may be worth investigating reducing the limits further to try and avoid further problems (e.g. to match how much memory would typically be available on a 2GB system with no boot sequence)
Phase | Status | Completion | Latest updates |
---|---|---|---|
Conceptual design | In progress | 70% | 26-Dec-2021 Document created 16-Jan-2023 Document updated with mostly-complete historic information |
Mock ups/visualisation | - | - | - |
Prototype coding | - | - | - |
Final implementation | In progress | 70% | 16-Jan-2023 Most of the required changes have already been implemented & merged many months ago |
Testing/integration | In progress | 30% | 16-Jan-2023 API & short descriptor code changes have been in the wild for many months. Long descriptor & high RAM testing has seen minimal end-user testing due to not being enabled in nightly ROMs |
v1.00 – 26-Dec-2021
v1.01 – 16-Jan-2023
v1.02 – 17-Jan-2023