Ada Bare Bones: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
No edit summary
Line 28: Line 28:
We need a place to structure this kernel,
We need a place to structure this kernel,
<source lang="bash">
<source lang="bash">
mkdir -p bare_bones/src
mkdir -p bare_bones/src/pc
cd bare_bones
cd bare_bones
mkdir -p rts/boards/i386
mkdir -p rts/boards/i386

Revision as of 13:47, 13 June 2012

Difficulty level

Medium
Kernel Designs
Models
Other Concepts

In this tutorial we will compile a simple Ada kernel and boot it.

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

Preface

This tutorial is based on my multiboot kernel which I developed some time ago and placed on my site [1] and will also be the basis for my own kernel TAMP.

One of the first things people ask on the Ada IRC channel on Freenode is "Can Ada be used for OS development?" to which the answer is a resounding yes. But there are 2 problems:

  1. The people asking this question are new to Ada, and
  2. GNAT is not the easiest compiler to build.

Therefore these users don't understand what it takes to get the compiler into a useable state.

As you may have seen from other bare bones tutorials on this site, they state that you must have a compiler built which can handle ELF files, the usual way is by building GCC which targets i386-elf or some other similar architecture. The problem here is that GNAT will not build for these targets out of the box without messing with it's makefile. You have to do this as the makefile builds the RTS and then the gnat tools (gnatmake, gnatbind, et al).

So, for this tutorial, we will use the system GNAT compiler to build for i386 and later I will show how to build an arm-elf compiler and tools. I am on Debian testing 64-bit with GNAT 4.6.

GNAT and the Ada runtime system (RTS)

For this kernel we will be configuring a zero footprint RTS profile. This basically means, we have a compiler, tools and not much else.

Directory structure

We need a place to structure this kernel,

mkdir -p bare_bones/src/pc
cd bare_bones
mkdir -p rts/boards/i386

Files

gnat.adc

This file in the root directory of the build tells GNAT there are some configuration pragmas to apply to the build. These pragmas can also be placed at the start of your custom sytem.ads (see below), but we'll place them here for now.

pragma Discard_Names;
pragma Normalize_Scalars;
pragma Restrictions (No_Exception_Propagation);
pragma Restrictions (No_Finalization);
--  Use pragma Restrictions (No_Tasking) instead?
pragma Restrictions (Max_Tasks => 0);
pragma Restrictions (No_Protected_Types);
pragma Restrictions (No_Delay);
--  pragma Restrictions (No_Floating_Point);
pragma Restrictions (No_Recursion);
pragma Restrictions (No_Allocators);
pragma Restrictions (No_Dispatch);
pragma Restrictions (No_Implicit_Dynamic_Code);
pragma Restrictions (No_Secondary_Stack);

Discard_Names

In Ada, the compiler generates strings for various data types, e.g. enumerations, these strings can then be used in I/O.

type Fruit is (Orange, Banana, Apple);
--  Ada defines the following strings, "Orange", "Banana" and "Apple" in an array.

--  These strings can be accessed using the 'Image attribute, as in
Put (Fruit'Image (Orange));
--  Prints "Orange" to the console.

Normalize_Scalars

Forces all scalars to be initialised, se the latest GNAT RM:Normalize_Scalars for more information.

No_Exception_Propagation

No_Finalization

Max_Tasks

No_Protected_Types

No_Delay

No_Recursion

No_Allocators

No_Dispatch

No_Implicit_Dynamic_Code

No_Secondary_Stack

system.ads

Every Ada program must have access to the System package, this essentially tells the compiler what kind of hardware we are building for, therefore there will be 1 system.ads file per architecture your kernel supports.

Copy a system.ads from GCC that matches the target you are working with, in our case this is gcc-<version>/gcc/ada/system-linux-x86.ads, name it system.ads and place it into rts/boards/i386/ we need to edit this a bit.

We don't need to change anything from the top as all the integer sizes should be correct. Go to the private part of the spec and change the following values:

  1. Command_Line_Args => False
  2. Configurable_Run_Time => True
  3. Exit_Status_Supported => False
  4. Stack_Check_Probes => False
  5. ZCX_By_Default => False
  6. GCC_ZCX_Support => False

For more information on these options, see gcc-<version>/gcc/ada/targparm.ads.

startup.s

GAS

.global startup                         # making entry point visible to linker

# setting up the Multiboot header - see GRUB docs for details
.set ALIGN,    1<<0                     # align loaded modules on page boundaries
.set MEMINFO,  1<<1                     # provide memory map
.set FLAGS,    ALIGN | MEMINFO          # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002               # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS)         # checksum required

.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

# reserve initial kernel stack space
.set STACKSIZE, 0x4000                  # that is, 16k.
.lcomm stack, STACKSIZE, 32             # reserve 16k stack on a doubleword boundary
.comm  mbd, 4                           # we will use this in kmain
.comm  magic, 4                         # we will use this in kmain

startup:
    movl  $(stack + STACKSIZE), %esp    # set up the stack
    movl  %eax, magic                   # Multiboot magic number
    movl  %ebx, mbd                     # Multiboot data structure

    call  main                          # call main created by gnatbind

    cli
hang:
    hlt                                 # halt machine should kernel return
    jmp   hang

Assemble using:

as -o startup.o startup.s

multiboot.ads

vga_console.ads and vga_console.adb

kernel.adb

linker.ld

ENTRY (startup)

SECTIONS
{
    . = 0x00100000;

    .text ALIGN (0x1000) :
    {
        *(.text)
    }

    .rodata ALIGN (0x1000) :
    {
        *(.rodata*)
    }

    .data ALIGN (0x1000) :
    {
        *(.data)
    }

    .bss :
    {
        sbss = .;
        *(COMMON)
        *(.bss)
        ebss = .;
    }
}

Link using:

i586-elf-ld -T linker.ld -o kernel.bin loader.o kernel.o

Note: again using ld instead of i585-elf-ld may occasionally work, but in most configurations you get an error or an unbootable image.

The file kernel.bin is now your kernel (all other files are no longer needed).

makefile

bare_bones.gpr