Geniux is a 32bit, multi-tasking operate system kernel, based on but not limited to the design of linux-0.12.
The purpose of the project is to learn:
- mechanism of OS
- programming and debugging skills of C and assembly language
- how linux works
- fun of kernel hacking
Kernel version | New features | Lines of code |
---|---|---|
v0.01 | Bootsector loaded 16-bit kernel | 315 |
v0.02 | Bootsector loaded 32-bit kernel and switched to protect mode | 467 |
v0.03 | Keyboard driver, timer and task switching | 1059 |
v0.04 | Sound blaster 16 driver, music playing | 1578 |
v0.05 | Deleted irrelevant drivers, implemented fork() system call | 1381 |
v0.06 | MINIX file system implementation | 2468 |
v0.07 | Implemented exec() system call, new OS could load and run ELF format executable files | 2989 |
v0.08 | Implemented library functions such as malloc(), in support of some user programs | 4111 |
Make sure in 32bit host Linux, open a terminal and go to geniux directory, type the following command if you want to build the system:
$ make geniux
If something goes wrong, make sure you have installed all necessary building tools. There should be messages, such as “make: as86: Command not found” if you haven't installed bin86. If everything goes OK, you should have a geniux.img generated.
Once the system finished booting, it will load a user space program called elf_child from its file system called fs/minix.img. The minix.img already contains an excutable file, you don't have to do this step, but if you'd like to build your own program, type the following command to build one:
$ sudo mount fs/minix /mnt
$ cd app && make
$ sudo cp elf_child /mnt/binary
$ sudo umount /mnt
If you don't want to build the system, you can try the previously built geniux.img located in the directory of geniux. Let's try to boot the new system by typing the following command:
$ qemu -m 8 -fda geniux.img
or:
$ qemu-system-i386 -m 8 -fda geniux.img
depending on the version of qemu installed on your computer. Of course you have to install qemu at first.
Once you have booted into geniux, you should see a window like this, which indicates geniux is running successfully.
Figure 1: Screen shot of geniux
Figure 2: Geniux system structure
Geniux kernel is a monolithic kernel, I mainly referred to fake-linux-0.00 and linux-0.12 to build. I think monolithic kernel is easier to learn and understand comparing to microkernel such as minix3. User tasks run in user mode, cpu privilege ring3, getting all services needed through system call, interrupt 0x80, same as linux; kernel runs in kernel mode, cpu privilege ring0, providing system calls and error handling routine.
As for IBM compatible computers, there is POST(Power-on self-test) right after powered on. I don't plan to go too far on this, in short, there are a series of testing and initial sequences. After these, BIOS(Basic Input Output System) load the boot sector(512 Bytes) into memory started at address 0x7c00 and jump to that address, this is the very place our system starts. As for geniux, boot sector is the first 512 bytes of geniux.img. Figure 3 shows the map of geniux.img.
Figure 3: Map of geniux.img
The function of boot sector is to load
-
- kernel
-
- minix file system
-
- switch CPU mode from Real Mode to Protected Mode
In real mode of CPU, and in purpose of no damage on BIOS area, the size of available memory is (640k-31.5k=608.5k), kernel takes 100k, the maximum size of minix file system is 508.5k. So I leave it to be 500k, see File system.
BIOS loads boot sector to memory address 0x7c00:
Figure 4: Map of memory, step 1
Boot sector program copies itself to memory address 0x9dc00(631k), then continues itself from the new address:
Figure 5: Map of memory, step 2
Use BIOS services to get extended memory size, load kernel from floppy disk to memory address 0x7c00 and minix file system to memory address 0x20c00:
Figure 6: Map of memory, step 3
BIOS services are no longer needed, so we move kernel and file system to memory address 0:
Figure 7: Map of memory, step 4
Set up gdt and ldt, switch CPU mode to protected mode, jump to kernel entry and execute from there.
Kernel.s is the main kernel, I mainly referred to a fake-linux-0.00's head.s to build. Its structure is very simple:
Figure 8: Structure of kernel.s
Like any other operate systems, geniux contains 4 parts:
- process management
- memory management
- I/O management
- filesystem
The following 4 parts will discuss those parts in detail.
The maximum process quantity is 64, timer interrupts in every 10ms, which invokes the process scheduling routing. Since the system is very simple, there is no task priorities, so scheduling is implemented by judging whether a task status is ready, then switch to the ready-status task.
Figure 9: Process scheduling
Task 0 is created by hand (tss, ldt, gdt are done manually, see kernel.s), then task n is created by syscall fork(). Also, there is exec() syscall to replace the current process image with a new process image.
In geniux, physical addresses 1MB~3MB are reserved for ramdisk, 3MB above are dynamically allocated memory area for applications.
Figure 10: Physical memory map
Meanwhile, the maximum supported virtual address for each task is 4GB.
Figure 11: Virtual memory map
Geniux now only supports read from ramdisk.
When invoking syscall int read(int fd, unsigned char * buf, int b_size), the position of specific data block is calculated by b_size and f_pos, the latter is known as the current file offset. Then geniux copies the data in that block to address *buf, without the standard routine copy_to_user() in linux.
Actually it's a known bug in geniux, the user space and kernel space isn't separated well, so in kernel space you can directly read or write user space, that's why copy_to_user() is not needed. This bug should be fixed later.
Geniux now only supports MINIX filesystem version 1.
File fs/minix.img is a normal 1.44MB minix filesystem image, but been tampered with during geniux.img making. Since floppy disk driver is not implemented, in order to load disk data into memory, I have to load disk data at boot time by BIOS services. The size of available memory space at boot time is 640KB-31.5KB-100KB=508.5KB, so there is almost only 500KB available, that is, 500KB data of minix.img is really loaded into memory, 500KB above is NOT loaded in.
You can rewrite files located in minix.img by mounting minix.img to your system:
$ sudo mount fs/minix.img /mnt
Minix.img now contains an executable file “binary/elf_child”, which is a userspace program interprets an assembly-language script. The script is locate in “source/source.s”, which prints the Fibonacci sequence. See screen shot.
Details about interpreter and assembly-language script can be found in https://github.com/liutgnu/asm-interpreter