C Sharp Bare Bones: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(Created page with "{{Rating|2}} 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 x86_64 architecture and boot via [...")
 
m (Added c# page link)
Line 1: Line 1:
{{Rating|2}}
{{Rating|2}}


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 x86_64 architecture and boot via [[GRUB]]. There are a number of tools for compiling C# to [[CIL]], including Microsoft's csc (distributed with [http://www.microsoft.com/net Microsoft.NET]) and mcs/gmcs/dmcs (distributed with [http://www.mono-project.com 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), [http://cosmos.codeplex.com/ Cosmos'] IL2CPU, mosacl from the [http://www.mosa-project.org/projects/mosa MOSA project] and tysila from the [http://www.tysos.org/redmine/projects/tysos/ tysos] project. Given the author's familiarity with tysos, that is what we will focus on here.
This tutorial is intended to help you write a simple 'Hello World' OS in [[C Sharp | C#]] which you can then compile to machine code for the x86_64 architecture and boot via [[GRUB]]. There are a number of tools for compiling C# to [[CIL]], including Microsoft's csc (distributed with [http://www.microsoft.com/net Microsoft.NET]) and mcs/gmcs/dmcs (distributed with [http://www.mono-project.com 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), [http://cosmos.codeplex.com/ Cosmos'] IL2CPU, mosacl from the [http://www.mosa-project.org/projects/mosa MOSA project] and tysila from the [http://www.tysos.org/redmine/projects/tysos/ tysos] project. Given the author's familiarity with tysos, that is what we will focus on here.


==Prerequisites==
==Prerequisites==

Revision as of 17:55, 15 June 2012

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 x86_64 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 x86_64-elf target, 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 tysos. For debian-based systems try sudo apt-get install nasm xorriso qemu mono-devel.

For tysos, use subversion to get the latest sources: svn co http://www.tysos.org/svn/trunk tysos, or download the latest tar ball from http://www.tysos.org/files/src/tysos-latest.tar.bz2.

Building tysila

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 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 and stub code for switching to long mode.

global loader
global sthrow

extern kmain

MODULEALIGN       equ     1<<0
MEMINFO           equ     1<<1
FLAGS             equ     MODULEALIGN | MEMINFO
MAGIC             equ     0x1BADB002
CHECKSUM          equ     -(MAGIC + FLAGS)

IA32_EFER         equ     0xC0000080
LME               equ     0x100

STACKSIZE         equ     0x1000

section .text

align 4
dd MAGIC
dd FLAGS
dd CHECKSUM

[BITS 32]
loader:
    mov         esp, stack + STACKSIZE

    ; set PAE in CR4
    mov         eax, cr4
    or          eax, 0x20
    mov         cr4, eax

    ; clear paging tables
    lea         edi, [pml4t]
    mov         ecx, 1024
    rep stosd

    lea         edi, [pdpt]
    mov         ecx, 1024
    rep stosd

    lea         edi, [pd]
    mov         ecx, 1024
    rep stosd

    ; identity map first 2 MiB
    lea         edi, [pml4t]
    lea         eax, [pdpt]
    or          eax, 3
    mov         [edi], eax

    lea         edi, [pdpt]
    lea         eax, [pd]
    or          eax, 3
    mov         [edi], eax

    mov dword   [pd], 0x83

    ; set up cr3 with the pml4t
    lea         eax, [pml4t]
    mov         cr3, eax

    ; enable long mode
    mov         ecx, IA32_EFER
    rdmsr
    or          eax, LME
    wrmsr

    ; enable paging and write-back cacheing
    mov         eax, cr0
    or          eax, 0x80000000
    and         eax, 0x9fffffff
    mov         cr0, eax

    ; load a 64 bit gdt
    lgdt        [gdt64info]
    jmp         0x8:.owngdt

.owngdt:
[BITS 64]
    call kmain

    cli
.halt:
    hlt
    jmp .halt

sthrow:
    hlt:
    jmp sthrow

section .data
align 32
gdt64info:
    dw          gdt64_end - gdt64 - 1
    dd          gdt64

align 32
gdt64 dd        0, 0    ; null descriptor
code  db        0xff, 0xff, 0x00, 0x00, 0x00, 10011010b, 10101111b, 0x00
data  db        0xff, 0xff, 0x00, 0x00, 0x00, 10010010b, 11001111b, 0x00
gdt64_end:

section .bss
stack: resb STACKSIZE
pml4t: resb 0x1000
pdpt:  resb 0x1000
pd:    resb 0x1000

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 (loader)

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 -felf64 -o loader 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
/path/to/tysos/tybuild/bin/Release/tybuild.exe /unsafe kernel.cs

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

/path/to/tysos/tysila2/bin/Release/tysila2.exe -fno-rtti --rename-epoint kmain -L/path/to/tysos/mono/corlib -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. The --rename-epoint switch renames the entry point to anything we like.

To link:

x86_64-elf-ld -T linker.ld -z max-page-size=0x1000 -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-x86_64 -cdrom barebones.iso