C Sharp Bare Bones
WAIT! Have you read Getting Started, Beginner Mistakes, and some of the related OS theory? |
Difficulty level |
---|
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.
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