I figure you know this, but just in case:
Remember the calling conventions of your C++ compiler when interfacing with the all-required Misc. ASM code.
N00b trying to write a kernel in C++
Re:N00b trying to write kernel module handler in C++
by now to make things easier I compiled the modules seperately but linked them all together into one kernel image.
While working on the interfaces between them, I came by accident across the GRUB manual again. This time I fully recogized its module support and thought it is a good thing and would buy me some time to think about the interfacing problem. Well, after some time I threw all I had away (the concepts, not the code in favour of the following: I use GRUB (or any other multiboot compilant boot loader would do it as well) not only as a bootloader but as an actual part of my system to load the few kernel modules I have at boot time.
Grub first loads the kernel and then the modules at subsequent page aligned locations above the kernel, passing me an array with (among other things) all the start addresses of the modules.
A module may need to initialize itself before it can be used (register signal handers, interrupts, ...) so I need to call something like a constructor at startup. Because at this stage my module is just a big bunch of code to the kernel, the easiest to find location where to place the initialization code is at the beginning of the module (the start address).
To get the module address called like a function I tried the following:
- try to convert the data pointer into a function pointer and call it -> forbidden by ISO C (see below)
- define a pointer to a function pointer and let the first one point to where my data pointer(I don't like that word) is (which btw is unsigned long) -> either my brain is too small to get it done or it is impossible
- using
takes me where i want to be but it trashes the stack and makes returning impossible, even when I override the functions entry and exit code with some black magic ( asm("/*"); ... asm("*/"); I compiled with -s to check that only the right parts are commented out)
- encapsulating the module like the kernel image with ASM (I tried several different ways) -> the same, trashed stack, no way back
- someone at http://public.activestate.com/gsar/APC/ ... ent/perl.h suggests:
-> doesn't compile
Additionally I just noted that GRUB doesn't enter the kernel at 0x100000 but at 0x10000c. That can be an(-other?) explanation why all of the above that compiles doesn't really work. how can I find out where I need to enter the module code?
Can anyone provide a pointer?
Thanks to you all for taking the time to answer my questions!
While working on the interfaces between them, I came by accident across the GRUB manual again. This time I fully recogized its module support and thought it is a good thing and would buy me some time to think about the interfacing problem. Well, after some time I threw all I had away (the concepts, not the code in favour of the following: I use GRUB (or any other multiboot compilant boot loader would do it as well) not only as a bootloader but as an actual part of my system to load the few kernel modules I have at boot time.
Grub first loads the kernel and then the modules at subsequent page aligned locations above the kernel, passing me an array with (among other things) all the start addresses of the modules.
A module may need to initialize itself before it can be used (register signal handers, interrupts, ...) so I need to call something like a constructor at startup. Because at this stage my module is just a big bunch of code to the kernel, the easiest to find location where to place the initialization code is at the beginning of the module (the start address).
To get the module address called like a function I tried the following:
- try to convert the data pointer into a function pointer and call it -> forbidden by ISO C (see below)
- define a pointer to a function pointer and let the first one point to where my data pointer(I don't like that word) is (which btw is unsigned long) -> either my brain is too small to get it done or it is impossible
- using
Code: Select all
asm("call %%eax" : : "a"(modi->mod_start) );
- encapsulating the module like the kernel image with ASM (I tried several different ways) -> the same, trashed stack, no way back
- someone at http://public.activestate.com/gsar/APC/ ... ent/perl.h suggests:
Code: Select all
#if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE)
# define PTRV UV
# define INT2PTR(any,d) (any)(d)
#else
# if PTRSIZE == LONGSIZE
# define PTRV unsigned long
# define PTR2ul(p) (unsigned long)(p)
# else
# define PTRV unsigned
# endif
#endif
...
#define PTR2nat(p) (PTRV)(p) /* pointer to integer of PTRSIZE */
...
/* According to strict ANSI C89 one cannot freely cast between
* data pointers and function (code) pointers. There are at least
* two ways around this. One (used below) is to do two casts,
* first the other pointer to an (unsigned) integer, and then
* the integer to the other pointer. The other way would be
* to use unions to "overlay" the pointers. For an example of
* the latter technique, see union dirpu in struct xpvio in sv.h.
* The only feasible use is probably temporarily storing
* function pointers in a data pointer (such as a void pointer). */
#define DPTR2FPTR(t,p) ((t)PTR2nat(p)) /* data pointer to function pointer */
#define FPTR2DPTR(t,p) ((t)PTR2nat(p)) /* function pointer to data pointer */
Additionally I just noted that GRUB doesn't enter the kernel at 0x100000 but at 0x10000c. That can be an(-other?) explanation why all of the above that compiles doesn't really work. how can I find out where I need to enter the module code?
Can anyone provide a pointer?
Thanks to you all for taking the time to answer my questions!
Re:N00b trying to write a kernel in C++
The obvious problem here is that GRUB doesn't care what the modules are or what order they are in, it will therefore not take the base link address into account when it loads them into memory.
The start address can be found by reading the executable's headers, in flat binary executables the start address is at the start of the file. Make sure you are actually linking the modules, object files cannot be executed. You will most likely want to make the modules into executables (rather than libraries) and make them relocatable, you will then be able to relocate the module wherever you like, you will need an interface for the executable to give you its function table though.
You can produce libraries if you want but you will need to write a dynamic linker to link the modules against the kernel (although you would have probably needed this anyway) and get it to record the export addresses in the kernel's link table. IIRC, libraries do not have entry points but that isn't a problem as ELF libraries can have .init (initaliser) and .fini (finaliser/destructor) which can fulfill those roles. The libraries will still need to be relocatable as well, you are going to have to write some reasonably complex code to get all this stuff to work...
The start address can be found by reading the executable's headers, in flat binary executables the start address is at the start of the file. Make sure you are actually linking the modules, object files cannot be executed. You will most likely want to make the modules into executables (rather than libraries) and make them relocatable, you will then be able to relocate the module wherever you like, you will need an interface for the executable to give you its function table though.
You can produce libraries if you want but you will need to write a dynamic linker to link the modules against the kernel (although you would have probably needed this anyway) and get it to record the export addresses in the kernel's link table. IIRC, libraries do not have entry points but that isn't a problem as ELF libraries can have .init (initaliser) and .fini (finaliser/destructor) which can fulfill those roles. The libraries will still need to be relocatable as well, you are going to have to write some reasonably complex code to get all this stuff to work...
Re:N00b trying to write a kernel in C++
Hi all!
Since my last post I discovered the need to have something like malloc and decided to do that first, delaying the interfacing problem again (and the dynamic linker).
Currently I'm using a bitmap to keep track of which pages are used and which are free. This works slow but sufficient for now. Now I need to find out how much memory is usable and how much of that is already occupied. I could piece that together from what I know about my modules and my kernel and probe the memory at every page but that seems to be a quite bad idea because GRUB provides me with a "memory map". So I did a loop to show me the contents of that memory map (type, the start and end addresses and the length(in brackets) of each entry):
Now I have three questions:
1) Because my test machine only has 32M (0x2000000), there is no physical memory at 0xFFFE0000. The multiboot specs only say:
2) Can I safely omit the high part on an only-32-bit-system?
3) GRUB is supposed to load my kernel to 0x100000 but why is this location marked as free(0x1)?? It seems like GRUB doesn't mark the memory occupied by the kernel/modules and I need to complete the above list myself anyway?
Since my last post I discovered the need to have something like malloc and decided to do that first, delaying the interfacing problem again (and the dynamic linker).
Currently I'm using a bitmap to keep track of which pages are used and which are free. This works slow but sufficient for now. Now I need to find out how much memory is usable and how much of that is already occupied. I could piece that together from what I know about my modules and my kernel and probe the memory at every page but that seems to be a quite bad idea because GRUB provides me with a "memory map". So I did a loop to show me the contents of that memory map (type, the start and end addresses and the length(in brackets) of each entry):
Code: Select all
MEMORY MAP: 0x2bde8->0x2bef0
type=0x01; L:0x0->0x9f7ff(0x9f800)
type=0x02; L:0x9f800->0x9ffff(0x800)
type=0x02; L:0xdc000->0xdffff(0x4000)
type=0x02; L:0xe4000->0xfffff(0x1c000)
type=0x01; L:0x100000->0x1eeffff(0x1df0000)
type=0x03; L:0x1ef0000->0x1efefff(0xf000)
type=0x04; L:0x1eff000->0x1efffff(0x1000)
type=0x01; L:0x1f00000->0x1ffffff(0x100000)
type=0x02; L:0xfec00000->0xfec0ffff(0x10000)
type=0x02; L:0xfee00000->0xfee00fff(0x1000)
type=0x02; L:0xfffe0000->0xffffffff(0x20000)
1) Because my test machine only has 32M (0x2000000), there is no physical memory at 0xFFFE0000. The multiboot specs only say:
Where can I find a list of type numbers and their describtion?type is the variety of address range represented, where a value of 1 indicates available RAM, and all other values currently indicated a reserved area.
The map provided is guaranteed to list all standard RAM that should be available for normal use.
2) Can I safely omit the high part on an only-32-bit-system?
3) GRUB is supposed to load my kernel to 0x100000 but why is this location marked as free(0x1)?? It seems like GRUB doesn't mark the memory occupied by the kernel/modules and I need to complete the above list myself anyway?
Re:N00b trying to write a kernel in C++
Yes there is.amee2000 wrote: 1) Because my test machine only has 32M (0x2000000), there is no physical memory at 0xFFFE0000.
Who ever said that all memory has to be RAM? There's BIOS, video mem, memory-mapped devices...
And the RAM you have doesn't have to be one continuous block starting at lowest possible address, either.
The multiboot specs only say:
Where can I find a list of type numbers and their describtion?type is the variety of address range represented, where a value of 1 indicates available RAM, and all other values currently indicated a reserved area.
You have it, above. 0x01 is available, all other is reserved (read: hands off unless you already know what you're doing).
Your kernel is of course free to overwrite itself. (Imagine it's only a third-stage bootloader?) Thus the RAM isn't reserved.3) GRUB is supposed to load my kernel to 0x100000 but why is this location marked as free(0x1)?? It seems like GRUB doesn't mark the memory occupied by the kernel/modules and I need to complete the above list myself anyway?
Every good solution is obvious once you've found it.
Re:N00b trying to write a kernel in C++
To know what I can do with it, I need to know what it is reserved for and the type in the GRUB memory map seems to give me at least a hint.all other is reserved (read: hands off unless you already know what you're doing)
GRUB doesn't consider it reserved but overwriting my kernel is quite not what I want. So I need to mark all memory from 0x100000 to (the last module's address + it's length) as 'used' to prevent it being allocated by my memory manager. Correct?Thus the RAM isn't reserved.
Re:N00b trying to write a kernel in C++
Reserved = Reserved. It means the memory is not RAM and cannot be used for storage (ie. there is no memory there and it's merely an empty gap or there is a memory mapped device there [where writing will change the devices registers which will probably screw up the system if you write random values like program code to it]). The ACPI stuff can be interpreted if your kernel supports ACPI and then those spaces can be overridden.amee2000 wrote:To know what I can do with it, I need to know what it is reserved for and the type in the GRUB memory map seems to give me at least a hint.all other is reserved (read: hands off unless you already know what you're doing)
Yes, marking the kernel reserved is very easy since the link script can be used to store the kernel's position and length. Modules are more of a problem though.amee2000 wrote:GRUB doesn't consider it reserved but overwriting my kernel is quite not what I want. So I need to mark all memory from 0x100000 to (the last module's address + it's length) as 'used' to prevent it being allocated by my memory manager. Correct?Thus the RAM isn't reserved.
Re:N00b trying to write a kernel in C++
Modules aren't that much of a problem since GRUB gives the starting addresses of each, and your linker scripts can give you their sizes.
Full ACK on the "reserved" though. Either you're writing a driver for whatever is located there (in which case the hardware documentation of the corresponding device / feature will tell you what it is), or you don't touch it.
Full ACK on the "reserved" though. Either you're writing a driver for whatever is located there (in which case the hardware documentation of the corresponding device / feature will tell you what it is), or you don't touch it.
Every good solution is obvious once you've found it.