I have now managed to port the Gnu Assembler, as, to IanOS. I consider this a major achievement as it shows that the system is capable of running real, useful programs. The next step will be to port the linker, ld, at which point the OS can be considered to be self-hosting. But true independence will call for a port of GCC also. Having managed to port as, I think this is a real possibility. At that stage it should be possible for the system to compile itself (and then it will be time to look at getting it running on real hardware).
Apart from being a very useful step in its own right, this task has been extremely useful in ferreting out various bugs, particularly in the filesystem task. Whatever tests one runs the real trial is when you apply these routines in earnest. I’ve uploaded all the changes to the OS to GitHub and at some stage I’ll document the procedures necessary for these ports; but I’m too keen to press on to devote much time to that right now.
And it’s a nice sunny day, with the Monaco Grand Prix on TV soon. Perfect!
I’ve come to something of a dilemma. The original intent of my website was to provide a simple, easily understood, example of a basic operating system. To keep this simplicity I have written my own versions of library routines such as printf, strcpy, strlen, etc., and my own utility programs such as ls, pwd, etc. I could continue this way, getting ever more complicated and reinventing many wheels, or I can attempt to make the system more comprehensive by making use of the many GNU programs that are available. In particular I would like to have an assembler and C compiler running on IanOS to make it self hosting.
Writing an assembler (relatively easy) and a C compiler (much harder) would be interesting projects, but it’s not really what I want to concentrate on right now. I’d rather look into making the basic OS more robust and look into subjects such as networking, supporting PCI and USB devices, and other core enhancements. Perhaps even getting the system to run on a real physical machine. I don’t mind reinventing some wheels (after all, that’s what writing your own OS is all about) but there is a limit to what one person can reasonably do; and, certainly at the moment, I want to keep this as a one-person project.
To facilitate this, I’ve ported newlib (a version of the standard C library) to IanOS, and it seems to be working quite well. This is a first step towards porting binutils and gcc (but quite a bit more work to be done yet) and provides many useful, well debugged, functions. Unfortunately this adds a level of complexity that detracts from the original aim. I have decided that, at least for the time being, I am going to freeze the version presented on my web site, with perhaps minor changes as I discover bugs, to the pre-newlib version. This is the “master” branch on github. I have now produced another branch, “newlib”, which contains the changes that I have made to aid porting and new versions of the tasks rewritten using the standard C library.
If anyone wants to experiment with newlib I suggest they download the “newlib” branch and then look at OSDevWiki (you should look at that website anyway if you have any interest in home-brew OSs), where instructions are given for producing a toolchain aimed at your own OS and for porting newlib. Should there be enough interest in this I may, in time, produce a detailed set of instructions for doing this.
I’ve been sidetracked a bit of late looking at porting newlib (newlib is a standard C library – implementing it provides many useful functions and opens the possibility of porting GNU software). One little potential pitfall arose as a result of this.
By default gcc aligns the elements of structs to provide best performance. Depending upon the layout of the variables in the struct, this may lead to some empty padding bytes being inserted between variables (with the result that sizeof(struct x) will give a different result to adding the sizes of the elements of the struct). Previously I had been using the -fpack-structs flag to ensure that there were no such gaps, but this lead to problems with newlib, so I removed the flag. And then the compiled system would not run.
The reason is that some structs need to be packed; examples are the structs that represent the physical layout of disk structures, such as the MBR, partition tables, boot sectors, and directory entries. The simple solution is to add “__attribute__((packed))” at the end of the struct definition:
I have done this so now structs are not packed by default, but particular structs – that need to be – are. I have also rearranged the order of variables in most structs to put the longer ones first and the shorter ones last (although this, obviously, can’t be done with the previously mentioned disk structures). This reduces, or even eliminates, the need for padding bytes.
I’ll post more details about the implementation of newlib in a while. At the same time I’ll update github with the new code.
A clock is a very useful feature of any operating system, allowing files to be stamped with their creation and modification dates. (Without this utilities such as make wouldn’t work.) I thought this would be difficult to implement, but it turned out to be really easy.
So there is now a clock which reads the time from the CMOS real-time clock on startup and then keeps time via the timer interrupt. Once a day, when midnight rolls over, it will read the RTC again to make sure it is still synchronized. File functions now implement the last modified date and the ls function displays this information. The RTC routine isn;t absolutely bullet-proof, so I’ll improve it in due course.
As part of this I expanded the printf() (and kprintf()) function so that it recognizes the basic formatting flags and minimum width specification.
When working on an (not entirely successful) attempt to improve the efficiency of the routines managing the PageMap table it became apparent that there were problems when reusing memory pages. In particular, if the page represented one of the various paging table entries it might be interpreted as valid when reused; obviously this is wrong.
I have changed the routine in the dummy task which releases pages so that it also zero-fills them. As mentioned in my last post, zero-filling a new page is always a good idea. In this particular case it ensures that the contents can’t be interpreted as valid page table entries. For efficiency, the zero-filling is done by a small assembler routine (in tasking.s)
At the same time I improved the efficiency of the zero-filling routines in startup.s (not that it particularly matters with code that is only executed the one time, but it makes the code neater too. I like neat code).
I’ve made a few changes to the memory map to tidy things up a bit. The main change is to move fixed areas, such as ROM and video RAM, to virtual addresses so that the space from 0x1100 to 0xFD000 is contiguous for use as a heap by the kernel. This should be plenty of space (don’t they say that about all limits) as kernel heap usage is not that much, and tends to be transitory.
The big consumers of heap memory are the disk buffers, but these use addresses in the data area of the FS task which has plenty of free space. As far as possible all of these fixed limits are specified in memory.h (and memory.inc – I’d love to do away with this file but haven’t yet thought of a way to use the same header for C- and assembler-code) and can be easily redefined there if necessary. There are probably a few absolute references to addresses in the code – I’ll change them if and when I catch them.
I’ve also changed the initial code, in startup.s, to zero all uninitialized kernel data before its first use. It makes sense to know the state of variables before they are used – at least it gives a consistent setup – and zero is a good value to choose. Null pointers show up more readily than ones containing arbitrary values.
All of these changes are reflected in the source code on github, and will make their way onto the website code when I have time. (You can take this sentence to be true for every change that I post about from now on.)
Up until now I have been using Subversion for version control, but I am persuaded that git may be a better choice. As a result I have created a git repository of the source code which is available here. I’ll try to keep this repository updated with any changes that I mention here (and any others that I make).
I’m very new to git, so forgive me if I don’t get it right first time.