C Sharp Bare Bones

From OSDev Wiki
Jump to: navigation, search

WAIT! Have you read Getting Started, Beginner Mistakes, and some of the related OS theory?

Difficulty level
Difficulty 2.png
Medium

This tutorial is intended to help you write a simple 'Hello World' OS in C# which you can then compile to machine code for the IA32 architecture and boot via GRUB. There are a number of tools for compiling C# to CIL, including Microsoft's csc (distributed with Microsoft.NET) and mcs/gmcs/dmcs (distributed with Mono). In addition there are a number of tools for compiling CIL to native machine code in an ahead-of-time manner, including Microsoft's ngen, mono (with the --aot option), Cosmos' IL2CPU, mosacl from the MOSA project and tysila from the tysos project. Given the author's familiarity with tysos, that is what we will focus on here.

Contents

Prerequisites

You will need a binutils which can target the elf_i386 emulation, mono (for the gmcs compiler) or csc from .net, grub and its xorriso dependency (for generating iso images), NASM/YASM/something similar for the assembly stub and of course tysila. For debian-based systems try sudo apt-get install nasm xorriso qemu mono-devel.

For tysila, you can download pre-compiled binaries from http://www.tysos.org/files/tysila/tysila-latest.tar.bz2 (http://web.archive.org/web/20170317154703/http://www.tysos.org/files/tysila/tysila-latest.tar.bz2) and extract them to somewhere in your path. Please note these binaries will not work on 64-bit Windows due to a bug in the current Microsoft CLR (see here) so instead you must build it from source.

Building tysila

This is only required if you have not downloaded the pre-compiled binaries above. Use subversion to get the latest sources 'svn co http://www.tysos.org/svn/trunk tysos' (not in Wayback Machine), or download the latest tar ball from http://www.tysos.org/files/src/tysos-latest.tar.bz2 (http://web.archive.org/web/*/http://www.tysos.org/files/src/tysos-latest.tar.bz2). Tysos is a project developing a full OS kernel and drivers in C#, however we only want the compiler from it therefore we only want to compile part of the build tree. Enter the tysos directory and run

cd tybuild && make && cd ..
cd mono/corlib && make mscorlib.dll && cd ../..
cd tysila2 && make && cd ..

You will need to put the mono/corlib/mscorlib.dll, tysila2/bin/Release/tysila2.exe, tysila2/bin/Release/libsupcs.dll, tysila2/bin/Release/libtysila.dll, tysila2/bin/Release/tydbfile.dll, tysila2/bin/Release/tydisasm.dll and tybuild/bin/Release/tybuild.exe files somewhere in your path.

You may need to enable binfmt_misc support for mono (if trying this on linux). How to do this is outside the scope of this document but the answer is easily obtained via Google.

Directory layout

We will create a directory to build our OS and ISO file in. Something like mkdir -p barebones/iso/boot/grub should suffice. Enter the barebones directory and start creating some files.

loader.asm

This is the assembly stub which will contain a Multiboot header.

global sthrow
 
extern kmain
 
MODULEALIGN       equ     1<<0
MEMINFO           equ     1<<1
FLAGS             equ     MODULEALIGN | MEMINFO
MAGIC             equ     0x1BADB002
CHECKSUM          equ     -(MAGIC + FLAGS)
 
section .text
 
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
 
sthrow:
    hlt
    jmp sthrow

kernel.cs

This is the actual simple kernel - it just prints a message to the screen.

namespace BareBones
{
    class Program
    {
        static int pos = 0;
 
        unsafe static void Main()
        {
            // Clear the screen
            for(int i = 0; i < 80 * 25 * 2; i++)
                *(byte *)(0xb8000 + i) = 0;
 
            // Say hi
            Print("Hello World!");
        }
 
        static void Print(string s)
        {
            foreach(char c in s)
                Print(c);
        }
 
        unsafe static void Print(char c)
        {
            *(byte *)(0xb8000 + pos) = (byte)c;
            *(byte *)(0xb8000 + pos + 1) = 0x0f;
            pos += 2;
        }
    }
}

linker.ld

The linker script

ENTRY (_start)
 
SECTIONS
{
    . = 0x00100000;
 
    .text ALIGN(0x1000) :
    {
        *(.text)
    }
 
    .rodata ALIGN(0x1000) :
    {
        *(.rodata*)
    }
 
    .data ALIGN(0x1000) :
    {
        *(.data)
    }
 
    .bss ALIGN(0x1000) :
    {
        *(COMMON)
        *(.bss)
    }
}

iso/boot/grub/grub.cfg

This is a short file to tell grub where to find our kernel

multiboot /kernel.bin
boot

Building it all

The following commands should build your new C# kernel. First, assemble the multiboot stub:

nasm -felf -o loader.o loader.asm

To compile the .cs file to a .exe you have a choice of three options (depending on your architecture):

gmcs /target:exe /out:kernel.exe /unsafe kernel.cs
csc /target:exe /out:kernel.exe /unsafe kernel.cs
tybuild.exe /unsafe kernel.cs

To compile kernel.exe to machine code we use tysila:

tysila2.exe --arch i586-elf-tysos -fno-rtti -o kernel.o kernel.exe

Here, the -fno-rtti switch disables run-time type information, support for which would greatly enlarge the size of your kernel and require you to provide a great number of run time functions to support this.

To link:

ld -m elf_i386 -T linker.ld -o iso/kernel.bin loader.o kernel.o

Then we make a bootable iso image with:

grub-mkrescue -o barebones.iso iso

And run it on qemu with:

qemu-system-i386 -cdrom barebones.iso
Personal tools
Namespaces
Variants
Actions
Navigation
About
Toolbox