Wednesday, 18 February 2009

writing an operating system in eclipse

I've been reading a websites about writing an Operating System in various IDE's such as Visual Studio. For my operating system I've been using Eclipse because it and GCC are both cross platform. This means I can write and simulate the PowerPC on a Windows box or Linux box.

1/ General - Windows/Linux

Regardless of the Operating System you'll need some basic tools to build the below. In this case we're developing for a different platform. I.e. We're writing a PowerPC Operating System on an x86 machine. We can still do this, but we need to create a cross-compiler - one that runs on an x86 machine but generates for PowerPC - for example.

With almost all Linux machines these tools, like make, automake, bison etc come as default. With Ubuntu you might want to get the buildessentials package.

Until Windows, you'll need to grab Cygwin. Install the basic build tools like GCC, make, automake and bison. You'll know if you need anything else when we try to build the Tools below and they fail.

2/ Tools - BinUtils and GCC

We now need to build the cross-compiler and tools. Initially we'll build a very basic version of the GCC compiler.

2.1/ binutils

Once you've downloaded GNU binutils extract the tar and load up a command line (or Cygwin) and execute the following.

./configure --target=powerpc-elf --prefix=/home/user/tools_ppc32 --disable-nls


then

make all && make install


powerpc-elf is the target - the machine we're going to be compiling code for.
The --prefix option sets the directory all the tools will be compiled to. We add this so this version of GCC doesn't over-write the one we have installed.

2.2/ GCC

Download GCC and extract the tar and goto the extracted directory and execute:

./configure --target=powerpc-elf --prefix=/home/user/tools_ppc32 --with-gnu-as --with-gnu-ld --disable-nls --without-headers -enable-languages=c,c++


make all-gcc && make install-gcc


Again, we specify the powerpc-elf target and add the --prefix argument. We say which tools we need and specify no headers (there's no C Library yet!) and the languages the compiler should be able to compile.

Once they're all built add the /home/user/tools_ppc32/bin directory to your Environment variables. This'll let Eclipse find the tools we've created for the PowerPC.

3/ IDE - Eclipse

Now we're in Eclipse create a C or C++ project. We then need to update the projects build settings for our new cross-compiling tools.

Under the "C/C++ Build" tab moved to the "Settings" tab. Under the "GCC C Compiler" change "gcc" to "powerpc-elf-gcc" and set the following settings.

For "Preprocessor" tick "Do not search System Directories".

Under the "GCC C Linker" set the command to "powerpc-elf-ld". Under "General" tick "Do not use standard start files", "Do not use default libraries", "No startup or default libs" and "No shared libraries".

This'll make sure we only use the libraries and headers in our Tools directory.

Under "GCC Assembler" set the command to "powerpc-elf-as".

Now when we compile our project we'll just be using our powerpc-elf-* tools. These'll generate code for a PowerPC processor even if we're on a Windows box with an Intel processor. We could also create tools for an ARM processor and then the name of the tools would be different, i.e. arm7-elf-*.

Because we're not using default start libraries we'll need to tell the compiler how to build our project. With GCC this is done with Linker Scripts.

Below is an example, it's what I've been using.

ENTRY (__kstart)

STACK_LENGTH = 0xF000;
HEAP_LENGTH = 0x400000; /* 4MB */

MEMORY
{
RAM : ORIGIN = 0x00000000, LENGTH = 0x4000000 /* 64MB */
}

SECTIONS{

/* code */
.text 0x2000 : {
*(.text)
}>RAM

/* read only data */
.rodata ALIGN (0x4) : {
*(.rodata)
*(.rodata*)
}>RAM

/* static data */
.sdata ALIGN (0x4) : {
*(.sdata)
}>RAM

/* data */
.data ALIGN (0x4) : {
*(.data)
}>RAM

/* Small Block Started by Symbol */
.sbss ALIGN (0x4): {
*(.sbss)
}>RAM

/* Block Started by Symbol */
.bss ALIGN (0x4) : {
*(COMMON)
*(.bss)
}>RAM

.stack : {
__kernel_stack_start = .;
. += STACK_LENGTH;
. = ALIGN(4);
__kernel_stack_top = . ;
}>RAM

.heap ALIGN (0x4): {
heap_start_addr = .;
. += HEAP_LENGTH;
heap_end_addr = .;
}>RAM

}


You'll notice that __kstart is the operation to execute first when the Kernel (Operating System) starts. Linker scripts are fairly complex so I won't document too much, take a read on the link instead.

Once it's created, go back the "GCC C Linker" tab and open the "Misc." tab and add the following text:

-T linker.ld


Linker.ld is the name of the file - above.

4/ Debugging - GDB

Now we've got a program that compiles and links with our linker script we should have an ELF program. This is an executable that'll run on the target but we might not have access to a target so we'll need a simulator.

Download GNU GDB and extract it and navigate to the path. Once there execute the following:

CC=gcc ./configure \
--enable-sim-powerpc \
--enable-sim-inline \
--disable-sim-smp \
--enable-sim-duplicate \
--enable-sum-endian=big \
--disable-sim-xor-endian \
--enable-sim-env=operating \
--disable-sim-reserved-bits \
--disable-sim-assert \
--disable-sim-trace \
--disable-sim-icache \
--target=powerpc-elf \
--prefix=/home/user/tools_ppc32 \
--disable-nls \
--with-cpu=750 \
--with-cpu=603e \
--with-cpu=7450 \
--enable-altivec \


make CC=gcc


make CC=gcc install


We now have a version of GDB that will let us simulate a PowerPC processor and execute the ELF file we've created. Of course we need to tell it what our image is and how to run the simulator.

We need two files, gdbinit and a device_tree.

gdbinit is a file that tells GDB how to start up. An example is given below:

target sim -e bug -E big -r 134217728 -f debug_ppc32\\device_tree
set architecture powerpc:750
file c:\\development\\samos\\debug_ppc32\\samos
load


This creates a simulator in PowerPC OEA mode (-e bug) in Big Endian mode with 128MB of RAM. -f Specifies the device_tree which we'll discuss later.

We set the architecture and load the file to debug.

The device_tree file contains information about the target hardware to simulate.

An example is given below:

/#address-cells 0x1

#device te cpu
/cpus/cpu@0
./cpu-nr 0

#setup the initial MSR
/openprom/init/register/msr 0x3000
/openprom/options/oea-interrupt-prefix 0x0
/openprom/options/oea-memory-size 0x8000000

#ensure we're in real mode
/options/real-mode? true

# Set up the initial stack pointer
/openprom/init/register/sp 0x51722

# define the stack type
/openprom/init/stack/stack-type "chirp"

# print out the device tree once it's loaded
/openprom/trace
/openprom/trace/print-device-tree 0x1

/chosen
/chosen/memory */memory

#
# I/O Bus
#
/iobus@0x80000000
./name psim-iobus
./reg 0x80000000 0x20000000

#
# OpenPIC
# hz == 10 ^3/ms, us = 10^6/ms
# 1Hz = 1s
# 10Hz = 100ms
# 100Hz = 10ms
# 1000Hz = 1ms
# 10000Hz = 100us
# 100000Hz = 10us
# 1000000Hz = 1us
/iobus@0x80000000/opic@0x80000000
./name interrupt-controller
./device_type open-pic
./compatible psim,open-pic
./reg 0x80000000 0x40000
./timer-frequency 1
./interrupt-ranges 0 256

# map the interrupt controller to the cpou external interrupt
/iobus/opic > intr0 int /cpus/cpu@0x0

/iobus@0xf00002F8
./name psim-iobus2
./reg 0xf00002F8 0x108
/iobus@0xf00002F8/com@0xf00002F8
./reg 0xf00002F8 8
/iobus@0xf00003F8/com@0xf00003F8
./reg 0xf00003F8 8

/nvram@0xf4008000
./reg 0xf4008000 0x8008

/eeprom@0xfff00000/reg 0xfff00000 0x80000'
/eeprom@0xfff00000/nr-sectors 8'
/eeprom@0xfff00000/sector-size 0x10000'
/eeprom@0xfff00000/byte-write-delay 1000'
/eeprom@0xfff00000/sector-start-delay 100'
/eeprom@0xfff00000/erase-delay 1000'
/eeprom@0xfff00000/manufacture-code 0x01'
/eeprom@0xfff00000/device-code 0xa4'


The format of the file is quite complex and there's little documentation on it. Lots more detail about PSIM - the PowerPC simulator can be found here.

Finally, we create a debug configuration in Eclipse. Do so for an application and set the program as the one we've linked together.

We set the "GDB Debugger" option to "powerpc-elf-gdb" and "GDB Command Line" option to "debug_ppc32\gdbinit" (depending on where you've stored the file!). We can also update the start procedure to our Kernel's entry point - the case above "__kstart".

Hopefully you can now get going!

3 comments:

  1. Thank you very much for this post. We have been doing a similar project, followed your steps... Now our debugger is working...

    ReplyDelete
  2. Great post. But I am having some trouble with building GDB. Using the Cygwin distribution of GDB, there seem to be so many gtk include file dependencies due to the inclusion of Insight, that I gave up trying to figure out all of the other cygwin packages I needed to have in order to get the gdb build to complete.

    However, trying an Insight free GDB distribution results in a different and very strange problem. 2 of the object files that the build links in creating libsim.a

    ar rc libsim.a .. semantics.o idecode.o ....

    have a multiple definition conflict

    gcc -g -O2 -Wl,--stack,8388608 -o psim.exe main.o libsim.a ../../bfd/libbfd.a ../../libiberty/libiberty.a
    libsim.a(idecode.o): In function `endian_t2h_8':
    /usr/src/gdb-7.2/sim/ppc/sim-endian-n.h:42: multiple definition of `_option_mpc860c0'
    libsim.a(semantics.o):/usr/src/gdb-7.2/sim/ppc/bits.c:71: first defined here
    collect2: ld returned 1 exit status

    seems that idecode.c is actually constructed from multiple instances of semantics.c. This brings in multiple instances of a statement at the global level of

    int option_mpc860c0 = 0;

    Has anyone else run into this ? and have any suggestions as to the proper correction ?

    ReplyDelete
  3. Firstly I find it odd that you'd need any GTK files. However, that problem with the simulator I did see and I believe it was to do with the version of binutils I was using.

    Of course since then I've moved my toolset on.

    Firstly, I now use MinGw to ensure probably Windows compatability and a little bit of extra performance.

    Secondly, I updated to all the latest versions. I now use:
    * binutils-2.21
    * gcc-4.6.0
    * gdb-7.2
    * newlib-1.19.0

    If you're targetting powerpc as it seems you are, I'd use the following switches for configure.

    ../gdb-7.2/configure --target=powerpc-eabi --prefix=/home/sam/sos2 --enable-sim --disable-nls --disable-werror --enable-sim-env=operating --enable-sim-hostbitsize=32 --enable-sim-trace=yes --enable-sim-assert=yes --enable-sim-inlines=2 --enable-sim-monitor=yes --enable-sim-smp=1 --enable-sim-stdio=yes --enable-sim-timebsae=yes --enable-sim-icache=yes

    Obviously the --prefix option you'll need to replace with whatever your system is using and where your toolchain will be configured.

    This should still work with cygwin too. I just prefer MinGW as it isn't using a POSIX layer, it's all build for Windows. Your ethos there of which you want/prefer is down to you but as I said you should be able to build the tools on either.

    Hope that helps.

    ReplyDelete