Journal

Let's Get Started: Writing an Operating System (1)

2011·07·18

Machine-translated from Chinese.  ·  Read original

We Need to Do

The first thing we need to do is set up an operating system development environment on our own computer. Then, we write an operating system that can display “Hello World”.

Preparation

The following are the things we need to prepare:

  1. An editor that you are familiar with, such as Vim
  2. gcc and binutils for editing C language code
  3. GNU Make for automated compilation and linking
  4. A virtual machine to run our operating system, recommended to use Bochs.

Installation

Installing the above software is very easy. On my Arch Linux system, I only need one line of command

yaourt -S vim gcc binutils make bochs

to install all the necessary software instantly. For Ubuntu and Fedora systems, installing these tools should also be very easy. If you encounter any problems, you can search for solutions using Baidu or Google.

Analysis

After installing all the necessary software, we need to analyze what we need to do next. We already have Bochs as a virtual machine, which can simulate the insertion of a floppy disk or hard disk and then boot up. We can think of Bochs as a computer - in fact, we can really use a computer to replace it, but in the early stages of development, using Bochs is more convenient for development and debugging. With the “computer” in place, the first thing we need to develop is the boot loader, which is the program that allows the computer to run our operating system after it is powered on. However, developing a boot loader is very complex and requires a lot of assembly language knowledge. Here, I want to introduce the UIUC SigOps uBoot System. This uBoot system is a powerful tool for beginners, which allows us to skip the tedious development of the boot loader and directly enter the next stage. Its usage is very simple, using a bootmaker program to compile elf or other files and create a SBBB (SigOps Bitchin’ Boot Block) boot image. Then, we can use our Bochs to run this SBBB boot image - this is really a very powerful tool!

Process

Next, I will describe the process. First, download the following file

http://os-quake.googlecode.com/files/kernel_0_0_f.tar.gz

After decompressing, we see that there are 8 files inside. Among them, main.c is the main program that writes “hello world”, bloader.h is the corresponding header file, Makefile is the corresponding make file, helloworld.ini is the configuration file. bochsrc is the configuration file for our virtual machine Bochs, and bootmaker is what I introduced earlier. kernel.elf and helloworld.img are files that will be generated during compilation, and I packed them together when I first packaged them. You can delete them and let them regenerate.

Before proceeding to the next step, let’s familiarize ourselves with these files. First, let’s take a look at main.c

#include "bloader.h"
int main();

/* Global Variables BAD BAD:) */
unsigned int oldEBP;
struct boot_dir *viewableDirectory;
int totalMem;
char * passedParams;
/* end global vars */

void _start(int memSize, char *parms, struct boot_dir *loadedfiles)
{
    asm("mov %%ebp, %0": "=m"(oldEBP));
    viewableDirectory = loadedfiles; /* make file mem locations global */
    totalMem = memSize; /* make mem of system global */
    passedParams = parms; /* make paramaters passed to system global */
    main();

    asm("hlt");        /* this halts the machine, solving the problem of triple-faults on 
                            some machines, but also making it impossible to return to DOS */
}

int main()
{
    char *vidmem = (char *) 0xb8000;

    /* "Hello " */
    vidmem[0] = 'H';
    vidmem[1] = 0x7;
    vidmem[2] = 'e';
    vidmem[3] = 0x7;
    vidmem[4] = 'l';
    vidmem[5] = 0x7;
    vidmem[6] = 'l';
    vidmem[7] = 0x7;
    vidmem[8] = 'o';
    vidmem[9] = 0x7;

    /* "World " */
    vidmem[12] = 'W';
    vidmem[13] = 0x7;
    vidmem[14] = 'o';
    vidmem[15] = 0x7;
    vidmem[16] = 'r';
    vidmem[17] = 0x7;
    vidmem[18] = 'l';
    vidmem[19] = 0x7;
    vidmem[20] = 'd';
    vidmem[21] = 0x7;
    vidmem[22] = ' ';
    vidmem[23] = 0x7;

    /* "OS" */
    vidmem[24] = 'O';
    vidmem[25] = 0x7;
    vidmem[26] = 'S';
    vidmem[27] = 0x7;

    return 0;
}

main.c has only over 60 lines, let’s take a close look. The void _start() function should be the bridge function between our C program and the SigOps-provided bootloader, which is the function that will be called when we use bootmaker to add a bootloader shell to our program. In fact, implementing this is not difficult. We can implement a similar kernel main function and link assembly and C code together using code similar to the following.

kernel_asm.asm

[bits 32] ; hey, we’re in PMode

[global start] [extern kernel_main] ; always add a "" in front of a C function to call it

start: call _kernel_main jmp $ ; halt

kernel_c.c

kernel_main()
{
     k_init();
     k_sayhello();
     ...
};

The void _start() function does some initialization work at the beginning, and then directly jumps to the main() function. char *vidmem = (char *)0xb8000 is the address of the CGA display card’s display memory (more precisely, 0XB800-0XBC00), and most IBM PCs use a unified addressing method in this part. Therefore, to display a colored character on the screen, we can directly use memory operation instructions to write to this memory region. 0x7 is a control character used to set the font color to black background and white text. You can refer to relevant materials to replace it with other colors. (Relevant materials can be found in “Linux Kernel Completely Analyzed — Based on 0.12 Kernel” by Zhao Jiong, published by Mechanical Industry Press, 2009.1, page 24, section 2.4.6 “Display Control”).

The Makefile is not unusual, using gcc to compile main.c into main.o, and then using bootmaker to read the configuration file helloworld.ini, generating a kernel.elf containing the bootloader, and then using the ld command to link main.o and kernel.elf together to generate the helloworld.img file.

Now that we understand our program, what we need to do is start the Bochs virtual machine, load the helloworld.img image file, and then start it. Below are the steps to operate, first input the command make to generate the helloworld.img image file, and then use Bochs to load it.

$ make $ bochs -f bochsrc

After that, we follow the prompts and press Enter to see our “Hello world”! It’s at the top left of the screen, look carefully :)

If there are problems running it, please check if the contents of the virtual machine’s configuration file are correct. Ensure that the directories of romimage and vgaromimage are in the corresponding directories.

Now that our “hello world” is complete, what we need to do next is add some basic functions to it, such as the printf() function that beginners love.

留 · 言