beyondsociety wrote:
What steps do I need to take in order to write a basic module loader?
Knowing what you have to load: your module will have to include information about the size of the code to be loaded and the amount of space your module requires (e.g. don't forget the BSS section)
Knowing where you will load it: the easiest is to build your module so that it has a 'target address' that will be free at run-time. If you have enough virtual address, you can easily slice your kernel space so that each module has its own slice. However, this is not very eay-to-upgrade-later and thus i'd rather advocate for a relocation list, here. Check out the "Linkers and Loaders", it's not such a complex concept.
Knowing what you can use: your module is likely to need access to kernel (or other modules) functions. This is usually the
symbol table. You can decide that there are N 'symbols' pointing towards structures/functions and pass the address of the table where those symbols are stored to your module's initialization function.
Code:
#define kprint kernelptrs[0]
#define kalloc kernelptrs[1]
...
function *kernelptrs=NULL;
status init(function *imports) {
kernelptrs=imports;
}
That table could also be an associative array of name=>pointer that the module loader will use to patch the executable.
Telling what you can do: the last step is to be able to export features (such as open/close/read/write for a device). You can for instance return an "object" structure that will contain pointers to functions that will perform what is expected, etc.
Code:
//-- module.h
struct itable {
int id;
void* vtable;
};
#define END_OF_INTERFACES 0
void* search_itable_for_id(int id);
//-- blockdev.h
struct block_device_interface {
status (*open)(options);
status (*close)();
status (*read)(address, size, buffer);
status (*write)(address, size, buffer);
};
#define BLOCK_DEVICE_INTERFACE 0xb10c
//-- module.c
status myOpen(...) {...}
...
struct block_device_interface myBlockDevice {
open: myOpen,
close: myClose,
read: myRead,
write: myWrite,
};
itable myInterfaces[]={
{ BLOCK_DEVICE_INTERFACE, &myBlockDevice },
{ END_OF_INTERFACES, NULL}
};
itable* init(functions* import) {
... do stuff ...
return myInterfaces;
}