OS_Module changes
New reason codes
In order to accommodate some of the extensions to the system which are planned for the future and for the current release, two new OS_Module entry points, changes to existing OS_Module entry points and a service call have been added. One of these OS_Module calls enables enumeration of modules with a pointer to the private word. Previously it was only possible to obtain the value of the private word. Consequently, providing functions which are akin to those provided by the kernel was not possible without some knowledge of the private memory locations. This new call allows some previously internal functions to be delegated to code outside the kernel.
Another of the OS_Module calls provides a counterpart to the *Unplug and *RMInsert commands. This allows modules to be unplugged or inserted without passing through OS_CLI. This may be more desirable where OS_CLI is not recommended (for example, within service calls or other background operations).
Changes to existing reasons
The change to the existing OS_Module entry points is a slight modification of the state of modules returned by OS_Module 19 and 20 (enumerate ROM modules) to allow the indication of modules which are 'unsuitable for this operating system'.
New services
To provide information to clients about modules presently running on the machine, a new service - Service_ModuleStatus - has been created. This is primarily for use of new extension modules, but clients monitoring the module chain may find this information more useful. In particular, for speed certain clients may wish to cache the module chain details in a manner appropriate for their operation rather than using the standard OS_Module calls.
Service_ModulePreInit has been extended to allow it to reject modules. This service was introduced in RISC OS 3.7 to allow load-time patching of module code which was not StrongARM safe. The extension allows checking code to explicitly reject (with an error) any module which is not deemed safe to run on the platform.
SWI name translation
The SWI name to number lookup has been made significantly faster from Kernel 9.54 onward. However, this has a side effect for the SWI name decoding code entry point. This is a rarely used entry point which can be used to translate named SWIs to numbers within the module. This entry point will still be called if the SWI prefix given in the SWI decoding table is matched, or if the module title is matched. However, it may not be called under other circumstances.
Previously the entry point in a module FooDecoder whose SWI prefix was Foo would be called, even though the SWI name being looked up was Bar_PlotBaz. With Kernel 9.54, such entries may not be called if the module title or SWI prefix do not match. In future versions of the operating system this entry point may not be called at all, as it is rarely useful and is not known to be used on any software. As such, the entry point is deprecated.
SWIs
OS_Module 19, 20
Enumerates ROM modules (with version number)
On entry
R0 = 19 or 20
R1 = module number, or 0 to start
R2 = section number (0-8 for podules, -1, -2, etc for ROM)
On exit
R1 = next module number
R2 preserved
R3 = pointer to module name
R4 = state of module:
-2 = module is unsuitable
-1 = module is unplugged
0 = module is inserted but not current in module chain
1 = module is active
2 = module is running
R5 = chunk number of expansion card
R6 = BCD version number (when R0 = 20 on entry only)
This SWI is called to enumerate the ROM system modules. More information can be found on PRM 1-246 and PRM 1-247. The call is extended from that description by returning -2 to indicate that a module is not suitable for the operating system. This is a diagnostic aid for locating non-32bit modules within podules. Future changes to the module format notwithstanding, the -2 value should not be seen on 26bit systems.
OS_Module 22
Enumerate modules with private word pointer.
On entry
R0 = 22
R1 = module number
R2 = incarnation number
On exit
R1, R2 updated
R3 = Pointer to module code
R4 = Pointer to private word, or 0 if module was not correctly unlinked
R5 = Pointer to postfix string
This SWI is equivalent to that for enumerating modules except that instead of the private word being returned, a pointer to the private word is used, as would be used on entry to routines handled by the module.
OS_Module 23
Unplug or insert modules.
On entry
R0 = 23
R1 = module name
R2 = 1 to insert, 0 to unplug
R3 = podule number, or &80000000 for 'all versions'
This SWI performs the same operation as *Unplug and *RMInsert on modules, allowing them to omitted during initialisation.
Services
Service_ModuleStatus (&D8)
On entry
R0 = operation :
0 = module initialisation
1 = module finalisation
2 = preferred instance has changed
3 = instance renamed
R1 = &D8 (service call number)
R2 = Pointer to module base
R3 = Pointer to private word
R4 = Pointer to module title
R5 = Pointer to postfix string
R6 = Pointer to new instance name (for 'instance renamed')
On exit
All registers preserved
This service is issued by the kernel after significant events have occurred to a module.
Module initialisation
Module initialisation is issued immediately after the modules service handler becomes valid, and after the initialisation entry has been called and returned no errors. As a result, a new-born module will see its instance appear at its service handler if it claims this service.
Loading a module, or creating a new instance will generate this service call. The initial module created will also issue the preferred instance service.
Module finalisation
Module finalisation is issued after the modules service handler has been removed and the modules finalisation code has been called. Killing an instance (including the last one) will generate this service call.
Module preferred instance
When the preferred instance changes for a module, this service call is issued. The postfix string and private word pointer will be 0 if there is no preferred instance. This happens when the last instance of a module has been killed.
Service_ModulePreInit (&B9)
On entry
R0 = module base address
R1 = &B9 (service number)
R2 = module length + 4
On exit
R0 = pointer to error block if module has been rejected, preserved otherwise
R1 = 0 if module has been rejected, preserved otherwise
R2 = 0 if module has been rejected, preserved otherwise
This service is issued just before a module is initialised. When a module is *RMLoaded (or equivalent):
- The module is loaded into memory
- The module is unsqueezed if necessary
- Service_ModulePreInit is issued
- OS_SynchroniseCodeAreas is called
- Initialisation code is issued
- Service_ModuleStatus (initialisation) is issued
This service call is intended to allow run-time patching of modules.
Earlier documentation (supplied with RISC OS 3.7) indicated that the service may be claimed by clients. This is discouraged as it prevents other modules from seeing the service and applying their patches. The call should only be claimed if the module's initialisation should be rejected and an error returned. If rejected, R2 should be set to 0 to indicate that this is an error being returned, rather than just a claim as previously documented. R0 should point to a standard error block which will be returned to the caller.
The documentation in the RISC OS 3.7 notes of R2 on entry was in error. The size supplied in R2 is the module length + 4. Patching modules should be aware of this as the module may exist at the end of the RMA and thus have no memory mapped for the apparent last word. This behaviour has been retained in order to ensure consistent use across OS versions.
Module services
From RISC OS 4, the Kernel supports a slightly revised module format in order to allow a much more efficient kernel implementation of service call distribution. The revised format is designed to give explicit information on service calls of interest to a module, while being backward compatible with old RISC OS kernels.
Revised format for service call handler
The meaning and format of the module header is unchanged from that in the Programmer's Reference Manual (PRM 1-205). The entry and exit conditions for the service call handler are unchanged from those in the PRM (PRM 1-210).
A table that specifies the service calls of interest to the module is added to the specification. The presence of this table is indicated by a magic instruction as the first instruction of the service handler code. The table is then referenced by an anchor word that immediately precedes the magic instruction. Hence, the required form of the service handler is as follows (where svc is the address of the handler code, found from offset &0C of the header as usual):
Address | Contents |
svc - 4 | Table offset (from module start) to service table |
svc | &E1A00000 (magic instruction, MOV r0,r0) |
svc + 4... | handler code as recommended in PRM 1-211 ... |
The anchor word at svc-4 can contain 0, meaning that no static service table is specified. This is reserved only for rare cases (see recommended use, below); a service table must be provided where possible. The format of the table referenced by the anchor word at svc-4 is as follows:
Address Contents
Table + 0 Flags word :
bit 0 = if set, call service handler with R1 set to index into table of service call
if clear, call service handler with R1 set to the server number
bits 1-31 reserved, must be 0
Table + 4 Offset (from module start) to handler code
Table + 8 Number of first service call of interest
Table + 12 Number of second service call of interest
... further service call numbers of interest
Table + X Table terminator
The service number fields must be listed in strict ascending order of service call number. RISC OS will reject any module which does not have a sorted list.
The svc_offset should specify an entry for code that directly dispatches a service call known to be in the set of interest. That is, it can skip the code that rejects unrecognised service call numbers as outlined in PRM 1-211. (The rejection code must remain for kernels that do not use the table.)
Requirements for new modules
New modules that have no service call handler are unaffected; they specify 0 at offset &0C of the module header as usual.
All other new modules must use the revised format. The new format is fully backward compatible while allowing newer kernels the chance to compile fast service call distribution chains.
Hence, all new modules must have the magic instruction (at svc) that identifies the new format, and also the table anchor word (at svc-4).
All new modules should have a a properly compiled table (at svc_tab) of all service calls that can be of interest to any of the module's instantiations at any time during the module's residence. The handler code itself should follow the form recommended in PRM 1-211, and the code entry from the table should skip the pre-rejection code, as discussed above.
The requirement allows efficient use of the table by new kernels, while also being efficient on old kernels. (On old kernels it is the time for the handler code to reject many unrecognised service calls, rather the time to dispatch recognised calls, that will typically be most significant; hence the recommended form already in PRM 1-211.)
Only rare exceptions to the requirement for a table are allowed; namely, where the set of service calls is extremely large, or is unbounded in some way. The strong recommendation is that only if modules would require a table of more than 1k bytes (more than approximately 250 service call numbers) should the table be omitted.
Modules that seem to require table omission should be redesigned or eliminated wherever possible. Otherwise, they should still have the magic instruction at svc, and specify an offset of 0 in the anchor word at svc-4. This ensures that old format modules can always be distinguished from new format modules.
The reason for requiring only rare omission of the table is that such non-compliant modules require costly service call distribution. Note that the RISC OS 4 kernel may choose to pass service calls to non-compliant modules only after passing to all relevant compliant modules, regardless of module instantiation order. (This only affects cases where service calls may be claimed.)
Recommendation for old modules
Old modules that have no service call handler are unaffected; they specify 0 at offset &0C of the module header as usual.
All other old modules should be updated to the new format at source where possible. The reason for this is that old format modules require costly service call distribution. Note that the RISC OS kernel may choose to pass service calls to old format modules only after passing to all relevant new format modules, regardless of module instantiation order. (This only affects cases where service calls may be claimed.)
Backward compatibility
The new format should be 100% backward compatible. The only issue is the possibility of an old module whose first instruction of its service call handler happens to be the magic instruction. Such a module would confuse a new kernel (which would most likely reject it as broken). This is an extremely unlikely event, since the magic instruction is a NOP. A simple fix would be to use a different NOP code in such a module, but a binary update as above would be far preferable.
Forward compatibility
RISC OS kernels will support old and new format modules, but all modules should be created as, or moved to new format wherever possible (see discussion above) the new format. A future release of the kernel may refuse to support old format modules.
Module flags word
To differentiate between 32bit and 26bit modules, an extension block and flags word has been defined as part of the module header. The format for this header is described in detail in the document ExecFormats.
|