Commit 49836a59 authored by Kevin Bracey's avatar Kevin Bracey
Browse files

* Converted to building with ObjAsm (but still a single object file using ORG).

* Added ARM_IMB and ARM_IMBRange SWIs as recommended by ARMv5.
* Some early prototype HAL bits popped in - a lot of source restructuring still
  to come.
* New debug target creates an AIF image with debug information, and translates
  this into an ASCII object file for the 16702B logic analyser.

Version 5.35, 4.79.2.1. Tagged as 'Kernel-5_35-4_79_2_1'
parent 0c77dd31
Here are some notes on my thoughts so far about 32-bit RISC OS.
Any comments appreciated.
GENERAL
=======
My current belief is that RISC OS will either be running 26-bit, or 32-bit,
with no in between state.
RISC OS will either be running 26-bit, or 32-bit, with no in-between state.
When running 32-bit, it will make no use of 26-bit modes, and will call all
routines in a 32-bit mode. There will be no support for 26-bit code, as this
......@@ -37,7 +33,7 @@ that uses MSR which will work on ARM6 onwards.
This way we will continue to be able to test software on our desktop systems -
it can be the same 32-bit binary - it will just run in a 26-bit mode.
RISC OS will be largely unmodified - almost all binary APIs will act the same
RISC OS is initially largely unmodified - almost all binary APIs will act the same
in a 32-bit system as they do now, except that they will be called in a 32-bit
version of the documented processor mode.
......@@ -74,11 +70,16 @@ preserve flags, you will probably be forced to use MRS and MSR instructions.
These are NOPs on pre-ARM 6 ARMs, so you may be able to do clever stuff to
keep ARM2 compatibility.
For example, the recommended code to check whether you're in a 26-bit mode is:
For example, the recommended general-purpose code to check whether you're in
a 26-bit mode is:
MOV R0, #0
MOV R0, #0 ; for ARM 2+3 compatibility only
MRS R0, CPSR ; NOP on 26-bit only ARMs
TST R0, #2_11100 ; EQ if in a 26-bit mode, NE if not
TST R0, #2_11100 ; EQ if in a 26-bit mode, NE if 32-bit
If you know you are in a privileged mode, then you can use:
TEQ PC, PC ; EQ if in a 32-bit mode, NE if 26-bit
Sometimes you may be forced to play with the SPSR registers. Beware: interrupt
code will corrupt SPSR_svc if it calls a SWI. Existing interrupt handlers know
......@@ -133,21 +134,17 @@ before. When running on a 32-bit system, you needn't preserve flags. The
following wrapper around the original SWI entry (converted to be 32-bit safe)
achieves this, assuming you always want NZ preserved on a 26-bit system.
Push R14
BL Original_SWI_Code
Pull R14
[ PreARM6compatibility
MOV R10,#0
]
MRS R10,CPSR ; NOP on pre-ARM6
TST R10,#2_11100 ; EQ if in 26-bit mode - C,V unaltered
MOVNE PC,R14 ; 32-bit exit: NZ corrupted, CV passed back
Push R14
BL Original_SWI_Code
Pull R14
TEQ PC,PC ; are we in a 32-bit mode?
MOVEQ PC,R14 ; 32-bit exit: NZ corrupted, CV passed back
[ PassBackC
BICCC R14,R14,#C_bit ; Extra guff to pass back C as well
ORRCS R14,R14,#C_bit
BICCC R14,R14,#C_bit ; Extra guff to pass back C as well
ORRCS R14,R14,#C_bit
]
MOVVCS PC,R14 ; 26-bit exit: NZC preserved, V clear
ORRVSS PC,R14,#V_bit ; 26-bit exit: NZC preserved, V set
MOVVCS PC,R14 ; 26-bit exit: NZC preserved, V clear
ORRVSS PC,R14,#V_bit ; 26-bit exit: NZC preserved, V set
Yes, this is cumbersome, but it can be removed when backwards compatibility is
no longer desired. The alternative, which would be to pass in caller flags in
......@@ -289,21 +286,21 @@ A few vectors, like RemV, attach significance to entry flags. If not claiming,
you mustn't change those flags for the next callee. In 26-bit mode this might
have been achieved by:
CMP R1,#mybuffer
MOVNES PC,LR
CMP R1,#mybuffer
MOVNES PC,LR
In the 32-bit world, you could change the CMP to a TEQ to preserve C and V, or
you could use something like:
Push R14
MRS R14, CPSR
CMP R1, #maxbuffers
BLS handleit
MSR CPSR_f, R14
Pull PC
Push R14
MRS R14, CPSR
CMP R1, #maxbuffers
BLS handleit
MSR CPSR_f, R14
Pull PC
handleit
...
...
INSIDE THE KERNEL
=================
......@@ -372,6 +369,7 @@ unmapped, to cause an abort.
Their actual locations and sizes are subject to change. In particular, they
may not be in the lower 64MB in future.
MISCELLANEOUS SWIS
==================
......
RISC OS and the "HAL"
=====================
RISC OS currently is tied to the IOMD20 and VIDC20 peripheral set,
descendents of the original IOC, MEMC and VIDC devices designed in parallel
with the original ARM. These devices provide a close fit with RISC OS, and
their functionality is well suited to general purpose and embedded systems,
but the continuing drive to reduce cost requires us to support other
peripheral sets on off-the-shelf ARM system on chips.
First targets for support are L7205/L7210 for Customer L and CL92xx (the new
ARM920T based devices) for Customer A. Enclosed are a summary of their
advantages and disadvantages over the ARM7500FE for our Information
Appliance designs.
L7205 CL92xx
+ Faster (50% or so) + Faster (400%+)
+ SDRAM support + SDRAM support
+ USB + USB
+ EIDE interface
+ Lots of GPIO
- No hardware cursor
- No floating point - Incompatible floating point
- No video DACs - No video DACs
- No PS/2 - No PS/2
- Bizarre MS-Windows video system
To support these devices, and others in the future, a simple HAL is to be
inserted underneath RISC OS. This will provide two functions. Firstly, it
will be responsible for initial system bootstrap, much like a PC BIOS, and
secondly it will provide simple APIs to allow hardware access.
The HAL APIs are a thin veneer on top of the hardware. They are designed to
act as replacements for all the hardware knowledge and manipulation performed
by the RISC OS Kernel, together with some APIs that will allow RISC OS driver
modules to become more hardware independent. No attempt will be made (at this
stage) to perform such tasks as separating the video drivers from the Kernel,
for example.
One tricky design decision is the amount of abstraction to aim for. Too
little, and the system is not flexible enough; too much and HAL design is
needlessly complicated for simple hardware. The present design tries to
err on the side of too little abstraction. Extra, more abstract APIs can
always be added later. So, initially, for example, the serial device API
will just provide discovery, some capability flags and the base address
of the UART register set. This will be sufficient for the vast majority
of devices. If new hardware comes along later that isn't UART compatible,
a new API can be defined. Simple hardware can continue to just report
UART base addresses.
The bulk of device driver implementation remains in RISC OS modules - the
difference is that the HAL will allow many device drivers to no longer
directly access hardware. For example, PS2Driver can now use HAL calls to
send and receive bytes through the PS/2 ports, and thus is no longer tied to
IOMD's PS/2 hardware. Similarly, interrupt masking and unmasking, as
performed by any device vector claimant, is now a HAL call. Note that HAL
calls are normally performed via a Kernel SWI - alternatively the Kernel
can return the address of specific Kernel routines. There is nothing to stop
specific drivers talking to hardware directly, as long as they accept that
this will tie them to specific devices.
This dividing line between the HAL and RISC OS driver modules is crucial. If
the HAL does everything, then we have achieved nothing - we have just as much
hardware dependent code - it's just in a different place. It is important to
place the dividing line as close to the hardware as possible, to make it easy
to design a HAL and to prevent large amounts of code duplication between
HALs for different platforms.
The Kernel remains responsible for the ARM's MMU and all other aspects of the
CPU core. The HAL requires no knowledge of details of ARM implementations,
and thus any HAL implementation should work on any processor from the ARM610
to the ARM940T or XScale.
OS independence
===============
Notionally, the HAL implementation is OS independent. It makes no assumptions
about the virtual memory map of the OS, and only uses the defined HAL->OS
entries. The HAL should not call RISC OS SWIs.
In practice, however, the HALs are unlikely to be used on anything other
than RISC OS, and many HALs are likely to be written. This makes it sensible
to place as much intelligence as possible within RISC OS itself, to prevent
duplicated effort.
Calling standards
=================
RISC OS and the HAL are two separate entities, potentially linked separately.
Thus some simple dynamic linking is required. This occurs via a hybrid of the
RISC OS module header and Shared C Library stubs. Each RISC OS/HAL entry is
given a unique (arbitrary) number, starting at 0. The offset to each entry is
given in an entry table. Calls can be made manually through this table, or
stubs could be created at run-time to allow high-level language calls.
Every entry (up to the declared maximum) must exist. If not implemented, a
failure response must be returned, or the call ignored, as appropriate.
To permit high-level language use in the future, the procedure call standard
in both directions is ATPCS, with no use of floating point, no stack limit
checking, no frame pointers, and no Thumb interworking. HAL code is expected
to be ROPI and RWPI (hence it is called with its static workspace base in
sb). The OS kernel is neither ROPI nor RWPI (except for the pre-MMU calls,
which are ROPI).
The HAL will always be called in a privileged mode - if called in an
interrupt mode, the corresponding interrupts will be disabled. The HAL should
not change mode. HAL code should work in both 26-bit and 32-bit modes (but
should assume 32-bit configuration).
Header formats
==============
The OS is linked to run at a particular base address. At present, the address
will be at <n>MB + 64KB. This allows a HAL of up to 64K to be placed at the
bottom of a ROM below the OS, and the whole thing to be section-mapped.
However, if a different arrangement is used, the system will still work
(albeit slightly less efficiently).
The OS starts with a magic word - this aids probing and location of images.
Following that is a defined header format:
Word 0: Magic word ("OSIm" - &6D49534F)
Word 1: Flags (0)
Word 2: Image size
Word 3: Offset from base to entry table
Word 4: Number of entries available
The HAL itself may have whatever header is required to start the system. For
example on ARM7500 16->32 bit switch code is required, and on the 9500 parts
a special ROM header and checksum must be present. Instead of a header,
a pointer to the HAL descriptor is passed to the OS in the OS_Start call:
Word 0: Flags (0)
Word 1: Offset from descriptor to start of HAL (will be <= 0)
Word 2: HAL size
Word 3: Offset from descriptor to entry table
Word 4: Number of entries available
Word 5: Static workspace required
Eoch of the HAL and the OS must be contiguous within physical memory.
RISC OS entry points from HAL init
==================================
Entry 0:
void RISCOS_InitARM(unsigned int flags)
flags: reserved - sbz
On entry:
SVC mode
MMU and caches off
IRQs and FIQs disabled
No RAM or stack used
On exit:
Instruction cache may be on
Usage:
This routine must be called once very early on in the HAL start-up, to accelerate the
CPU for the rest of HAL initialisation. Typically, it will just enable the instruction
cache (if possible on the ARM in use), and ensure that the processor is in 32-bit
configuration and mode.
Some architecture 4 (and later) ARMs have bits in the control register that affect
the hardware layer - eg the iA and nF bits in the ARM920T. These are the HAL's
responsibility - the OS will not touch them. Conversely, the HAL should not touch the
cache, MMU and core configuration bits (currently bits 0-14).
On architecture 3, the control register is write only - the OS will set bits 11-31 to
zero.
Likewise, such things as the StrongARM 110's register 15 (Test, Clock and Idle Control)
are the HAL's responsibility. The OS does not know about the configuration of the
system, so cannot program such registers.
This entry may not be called after RISCOS_Start.
Entry 1:
void *RISCOS_AddRAM(unsigned int flags, void *start, void *end, uintptr_t sigbits, void *ref)
flags
bit 0: video memory (only first contiguous range will be used)
bits 8-11: speed indicator (arbitrary, higher => faster)
other bits reserved (SBZ)
start
start address of RAM (inclusive) (no alignment requirements)
end
end address of RAM (exclusive) (no alignment requirements, but must be >= start)
sigbits
significant address bit mask (1 => this bit of addr decoded, 0 => this bit ignored)
ref
reference handle (NULL for first call)
Returns ref for next call
On entry:
SVC32 mode
MMU and data cache off
IRQs and FIQs disabled
Other notes:
This entry point must be the first call from the HAL to RISC OS following a hardware
reset. It may be called as many times as necessary to give all enumerate RAM that
is available for general purpose use. It should only be called to declare video
memory if the video memory may be used as normal RAM when in small video modes.
To permit software resets:
The HAL must be non-destructive of any declared RAM outside the first 4K of the first
block.
The stack pointer should be initialised 4K into the first block, or in some non-
declared RAM.
Must present memory in a fixed order on any given system.
Current limitations:
The first block must be at least 256K and 16K aligned. (Yuck)
Block coalescing only works well if RAM banks are added in ascending address order.
RISC OS will use RAM at the start of the first block as initial workspace. Max usage
is 16 bytes per block + 32 (currently 8 per block + 4). This limits the number of
discontiguous blocks (although RISC OS will concatanate contiguous blocks where
possible).
This call must not be made after RISCOS_Start.
Entry 2:
void RISCOS_Start(unsigned int flags, int *riscos_header, int *hal_entry_table, void *ref)
flags
bit 0: power on reset
On entry:
SVC32 mode
MMU and data cache off
IRQs and FIQs disabled
Usage:
This routine must be called after all calls to RISCOS_AddRAM have been completed.
It does not return. Future calls back to the HAL are via the HAL entry table, after
the MMU has been enabled.
Entry 3:
void *RISCOS_MapInIO(unsigned int flags, void *phys, unsigned int size)
flags: bit 2 => make memory bufferable
phys: physical address to map in
size: number of bytes of memory to map in
Usage:
This routine is used to map in IO memory for the HAL's usage. Normally it would
only be called during HAL_Init(). Once mapped in the IO space cannot be released.
It returns the resultant virtual address corresponding to phys, or 0 for failure.
Failure can only occur if no RAM is available for page tables, or if the virtual
address space is exhausted.
void *RISCOS_AccessPhysicalAddress(unsigned int flags, void *phys, void **oldp)
flags: bit 2 => make memory bufferable
other bits must be zero
phys: physical address to access
oldp: pointer to location to store old state (or NULL)
On entry:
Privileged mode
MMU on
FIQs on
Re-entrant
On exit:
Returns logical address corresponding to phys
Usage:
Arranges for the physical address phys to be mapped in to logical memory.
In fact, the whole megabyte containing "phys" is mapped in (ie if phys =
&12345678, then &12300000 to &123FFFFF become available). The memory is
supervisor access only, non-cacheable, non-bufferable by default, and will
remain available until the next call to RISCOS_Release/AccessPhysicalAddress
(although interrupt routines or subroutines may temporarily map in something
else).
When finished, the user should call RISCOS_ReleasePhysicalAddress.
void RISCOS_ReleasePhysicalAddress(void *old)
old: state returned from a previous call to RISCOS_AccessPhysicalAddress
On entry:
MMU on
FIQs on
Re-entrant
Usage:
Call with the a value output from a previous RISCOS_ReleasePhysicalAddress.
Example:
void *old;
unsigned int *addr = (unsigned int *) 0x80005000;
unsigned int *addr2 = (unsigned int *) 0x90005000;
addr = (unsigned int *) RISCOS_AccessPhysicalAddress(addr, &old);
addr[0] = 3; addr[1] = 5;
addr2 = (unsigned int *) RISCOS_AccessPhysicalAddress(addr2, NULL);
*addr2 = 7;
RISCOS_ReleasePhysicalAddress(old);
HAL entries
===========
void HAL_Start(int *riscos_header)
Arrange correct ROM image
POST
Initialise memory system
ROM timings, width
Reset screen
Disable interrupts
Start timers
Size memory
Set up table describing memory layout
Set up video DMA
Time CPU
CONT / CONT_Break
InitMEMC (in: r1 = 0 -> Reset, 1 -> Break)
Check for 7500 vs IOMD
Program CPU, MEM and IO clocks
Set ROM timings
Set ROM width
Set up VRAM refresh
Set up peripheral timings
Set up sound format
Ensure MMU off and caches
Set up VIDC
Disable interrupts in IOC
Start timer 0
MemSize (out: r0 = page size, r1 = memory size, r2 = MEMC CR)
Set up RAM width
Find memory - create a table of (addr,len) pairs (in first memory found)
Find VRAM - if none take from first block
Start filling in page zero (in first block)
Set up video DMA registers
Allocate L1PT, and some L2PT, and soft CAM
Turn on MMU and caches
TimeCPU (out: r0 = peak RAM speed in kHz)
Put in extra pages: cursor, system heap
Start keyboard scanning
If POR or FX 200
Clear memory
Check processor type
Fill in processor vectors
Read CMOS
Fill in SWI dispatch table
Wait for keyboard (up to two seconds)
If (POR AND R/T/Del/Copy)
Reset CMOS
Goto Hard Reset
IF (POR or CannotReset or SysHeapCorrupt or CAM map nonsense or Ctrl pressed)
Clear the CAM
Set it up
InitDynamicAreas
Create system dynamic areas
InitVectors
Clear SWI hash table
Clear POR bit
Else
Do the soft reset stuff
Re-initialise kernel
If (hard reset)
Init variables
Initialise modules
PostInit
Set mode
Print "RISC OS"
Service_Reset
Shut all files
Beep if hard reset
If numpad key down
reconfigure monitor, change mode
print "monitor type reconfigured"
Check shift
Do boot
Else check *
Else enter language
\ No newline at end of file
Initialisation
==============
HAL_Init
Interrupts
==========
The HAL must provide the ability to identify, prioritise and mask IRQs, and the ability
to mask FIQs. RISC OS supplies the ARM's processor vectors, and on an IRQ calls the HAL
to request the identity of the highest priority interrupt.
IRQ and FIQ device numbers are arbitrary, varying from system to system. They should be
arranged to allow quick mappings to and from hardware registers, and should ideally
be packed, starting at 0.
HAL_IRQEnable
HAL_IRQDisable
HAL_IRQClear
HAL_FIQEnable
HAL_FIQDisable
HAL_FIQClear
HAL_GetHighestIRQ
Timers
======
The HAL must supply at least one timer capable of generating periodic interrupts.
Each timer should generate a separate logical interrupt.
HAL_Timer
HAL_TimerEnable
HAL_TimerRead
HAL_TimerSetRate
HAL_TimerDisable
Entry into RISC OS:
POST check (if any) complete
CPU & memory systems at full speed
MMU off, SVC32 mode, IRQs+FIQs disabled
All interrupts masked
I/O timings set up
DRAM refresh running
Video system stabilised (off?)
Information passed:
Table of (addr,len) pairs of RAM
Address + amount of VRAM
Memory speed?
CPU speed?
Entry point to HAL
Questions:
How to clear RAM without logical copy? Do we NEED a logical copy?
Yes we do - but logical copy will NOT be contiguous.
Physical Size Logical - offset
F0000000 01000000 80000000 70000000
F1000000 01000000 81000000 70000000
60000000 00001000 82000000 22000000 - fast SRAM - how to signal?
02000000 00200000 80000000 7FE00000
10000000 01700000 80200000 70200000
11B00000 02500000 81900000 6FE00000
14000000 04000000 83E00000 6FE00000
02000000 00200000 82000000 80000000
10000000 01700000 90000000 80000000
11B00000 02500000 91B00000 80000000
14000000 04000000 94000000 80000000
Memory Map
00000000 16K Kernel workspace