CFE Bare Bones

From OSDev.wiki
Jump to navigation Jump to search

This example shows how to boot a simple "Hello world!" kernel using Broadcom's Common Firmware Environment (CFE). I have tested it on a Clarke-Tech ET9000 DVB-S receiver, which is based on a BCM7405 MIPS CPU. To compile this example, you need a GCC / Binutils cross toolchain for a mipsel-elf target - and a basic knowledge of the MIPS architecture.

Entry.S

The entry point of the kernel is written in assembler language. It sets up a small stack (4 kB), calls the C entry point and puts the CPU in an endless loop.

.global start
.extern main

.set noreorder

.set STACKSIZE, 0x1000

.section .text

start:
	la $sp, stack
	addiu $sp, STACKSIZE - 32
	jal main
	nop
	b .
	nop

.section .bss
stack:
	.space STACKSIZE

Assemble this code using:

mipsel-elf-as -o Entry.o Entry.S

Main.c

The C part of the kernel contains a simple putchar function that outputs a character to the serial console, a printk function for printing a string, and the main function. Note that putchar directly accesses some hardware registers, which may be at a different address on other CPUs. This example prints the output to UART0 (16550A compatible) of the BCM7405, which is connected to the serial port of the ET9000.

void putchar(char c)
{
	volatile int* lsr = (volatile int*)0xb0400b14; // Line status register.
	volatile int* thr = (volatile int*)0xb0400b00; // Transmitter holding register.

	while(((*lsr) & 0x20) == 0) ; // Wait until THR is empty.

	*thr = c;
}

void printk(const char* s)
{
	while(*s)
	{
		putchar(*s);
		s++;
	}
}

int main(void)
{
	printk("Hello world!\n");
	return 0;
}

Compile this code using:

mipsel-elf-gcc -o Main.o -c Main.c -Wall -Wextra -Werror \
	-nostdlib -fno-builtin -nostartfiles -nodefaultlibs -O2

ldscript

Finally, we need to link our kernel. In this case we link the kernel to virtual address 0x80001000, which is the start of available RAM on the ET9000, accessed through the cached kseg0 window of the MIPS architecture.

OUTPUT_FORMAT(elf32-littlemips)
OUTPUT_ARCH(mips:isa32)
ENTRY(start)

SECTIONS
{
	.text (0x80001000) :
	{
		*(.text)
		*(.text.*)
		*(.stub)
		*(.gnu.linkonce.t.*)
	}

	.rodata ALIGN(4K) :
	{
		*(.rodata*)
		*(.gnu.linkonce.r.*)
	}

	.data ALIGN(4K) :
	{
		*(.data*)
		*(.gnu.linkonce.d.*)
	}

	.bss ALIGN(4K) :
	{
		*(.common)
		*(.bss*)
		*(.gnu.linkonce.b.*)
	}

	/DISCARD/ :
	{
		*(.gcc_except_table)
		*(.eh_frame)
		*(.note)
		*(.comment)
		*(.rel.*)
		*(.rela.*)
	}
}

Finally, link the kernel:

mipsel-elf-ld -T ldscript -o Kernel.elf Entry.o Main.o

Booting the kernel

These instructions are again hardware dependent. I have tested them on an ET9000 DVB-S receiver, but booting other devices may be very different.

  • Switch off the ET9000.
  • Put the kernel file Kernel.elf on a FAT32 formatted USB stick and plug it into the ET9000.
  • Connect the serial port of the ET9000 to your computer.
  • Open a serial console program.
  • Switch on the ET9000.
  • Press Ctrl + C to prevent an automatic startup and boot into the CFE console.
  • Enter the command "show devices" at the CFE prompt to see whether the USB stick has been found.
  • Enter "dir usbdisk0" (or whatever name has been assigned to the USB stick by CFE) to see whether your kernel is in place.
  • Enter "boot -elf usbdisk0:Kernel.elf" to boot the kernel.

If you see something like this, your kernel works:

CFE> boot -elf usbdisk0:Kernel.elf
Loader:elf Filesys:fat Dev:usbdisk0 File:Kernel.elf Options:(null)
Loading: 0x80001000/4112 0x80003000/4096 Entry address is 0x80001000
TP1 Entry Address at 0x80000ffc = 80001000
Starting program at 0x80001000

Hello world!