2013-06-21

2013-06-21 - How Programs Talk to the Kernel

So I've decided how I'm going to implement the mechanism by which the newly created executables find the kernel library.

The original Linux kernel used software interupts for system calls which, while being a bit slow, were easily accessible from everywhere.  Later on, the linux kernel moved to using memory mapped kernel functions which are faster.

What I'm going to do is have a single interrupt based system call which provides the (process-specific?) address for a FindSymbol() function.  Using this mechanism, a new process can find this function, then use it to search for other symbols such as "write".  This way, as the program starts up, it can find the addresses of symbols it requires and set itself up.  This may cause an overhead on programs which are frequently started and stopped such as console tools, but this should be acceptable for now.

In the meantime, I still haven't actually put the interrupt handling back in yet.  Should probably do that, and may as well use the List structure to combine the handlers.  I guess I should create a list for each interrupt number, then just do an AddTail() for each handler in turn.  I should also put in the kmalloc() and kfree() using a slightly upgraded technology from last time.

2013-06-18

2013-06-18 - Hello World ELF

As well as pulling in sections of code from previous iterations of the OS, I've been thinking more and more about the library, driver, and executable system.  Between the system calls from Linux, the POSIX specification and the functions required by NewLib, I've been able to whittle down a short list of system calls which I need to develop, and a rough order in which to do them.

My plan is to create a Hello World! program, but this program will not have a crt0.o, and will not include a LibC.  Instead it will use the OS method of getting a handle on the kernel.library (which will provide methods for opening libraries and finding symbol locations), from there it will find the "write" symbol, and call it to write the string (for the moment, it can assume that the stdout is at a specific file number).

Once this program is ready, I'll need to finish all the kernel code to underpin this.  For the moment, the following simplifications will be made:

  1. The program binary will be loaded into memory by Grub (to save the filesystem code yet)
  2. The program will be linked to start at 4Mb, and will be loaded into the kernel memory space at this address.
  3. The process model will not create a separate process for the program.


The workflow needed in order to achieve this, including required components is:

  • Create enough of the process table to store the open file list.
  • Create the Open File list (including file operations).
  • Create a basic console as previous versions, except using the stdin and stdout streams via read and write.  The read() call will spinlock inside the process subsystem until keys are pressed.
  • Get the kernel process to call into the console.
  • Configure an exit command from the console to return to the kernel.
  • Define the method of finding the kernel library and then finding other symbols.
  • Reimplement the 
  • Pull in the ELF loader


Distracted again, but Bochs has implemented a basic beep capability, and I've put the start of a "music" player based loosely on the the ZX Spectrum +2 PLAY command in so that I can program Greensleeves ...

2013-06-14

2013-06-14 - Bootloader Progress

Ok, so I have persuaded NetBeans to start a new project, and I've rewritten the example OS's multiboot header code, and wired in my Grub-Bochs image.
It now compiles the (noddy) kernel, links it into an ELF binary, and loads Bochs so Grub can load the kernel.

I'm considering how modules, libraries and executables will load as I want to take this opportunity to separate them from the kernel completely.

I think I understand now what crt0.o does in the grand scheme of things.  When you write a program, you start your program with a "void main()" and stuff like "stdout" already exists and stuff like "printf" works.  When your executable is first loaded, the OS actually passes control to the crt0.o section in your program (the start() function).  This routine (which is part of the toolchain which builds programs specifically for that platform) then uses some platform-specific voodoo to get references to things, it gets a reference to the environment somehow, it gets some library to power write() and open(), and generally sets up stuff that the C library will need to operate.

Now, I imagine I'm going to end up with two different types of entity, Executables and Libraries.
An Executable will be loaded into memory, a new thread (or process) will be created, and sent in to the start address of the executable.  If this is a C toolchain program, it'll land in crt0.o and start the process.  If it's a seat-of-the-pants assembly marvel, it will do something else.
When the program exists, either by calling an Exit() function or by the main thread leaving the building, the process will end, and the memory for the program will be freed.
This can all be achieved by me creating a crt0.o as part of my C library, and compiling programs as normal.  They'll run and won't know the difference.

A Library on the other hand, is slightly different.  It will be loaded into memory, and will present a set of entry points.  It won't have a main thread or process given to it, and it won't have a defined exit point.
I imagine that all libraries will need some form of Open() entry point so that the C library linked into the library can connect all of its functions to the basic kernel functions.  I guess it must be like a crt0.o, but it won't set up stdin or stdout, and it might not look to call the main() function once it's finished (although it may call a further initialise() in the library code itself, in case it needs to open further libraries or do other set-up).

Beyond that, the libraries could just be ELF .o files (which have no unresolved symbols) and just has symbols exposed which are its entry points.
(I'd have to investigate how to make a linked .o file with specific symbols exposed and others not (public vs. private) )

This all comes down to whether the device drivers should be executables or libraries.  I suspect the answer is that they should be libraries, and will be loaded in the kernel memory space (for the moment) but some of the drivers may launch a new kernel thread in their initialise.

So.  A device driver is a library.  The kernel will have to be told to load it at some point (possibly by a driver list file, possibly by some form of probe program).  The library will be loaded and will have its Open() method called (as all libraries will).  I guess the open code will register the device driver with the kernel, or I guess it could start probing the system for compatible devices.

In the Amiga world, the device driver library would be a normal library but with some specific entry points to make it into a driver and then it's loaded especially as a driver ( http://amigadev.elowar.com/read/ADCD_2.1/Libraries_Manual_guide/node029E.html ).

I think the device drivers are going to work in a similar manner.  The Device driver will be a library (ELF .o file) and will be loaded into the library list,  .... (thought interrupted) ...