Stage 2. Initial porting

LPC-E2468 boot process.

LPC-E2468 has external memory: 128MB of NAND Flash memory and 16 MB of SDRAM. LPC2468's external memory controller is initialized by uboot bootloader, which is preflashed into LPC2468's on-chip memory by Olimex. Also Uboot initializes clocks and UART for us, which is good news for us =) After Uboot is done loading/checking firmware, it jumps right into 0xA0008000 adress, where our firmware should start.

Im using TFTP to work with device.

You will need to plug the board into your ethernet network. Here is my U-Boot configuration:

lpc-e2468 # print

bootargs=root=/dev/ram initrd=0xa0800000,4000k console=ttyS0,115200N8
baudrate=115200
nand_boot=nand read 0xa0008000 0x0 0x220000;nand read 0xa0800000 0x400000 0x220000;go 0xa0008000
mmc_boot=mmcinit;fatload mmc 0x0 0xa0008000 linux.bin;fatload mmc 0x0 0xa0800000 romfs.bin;go 0xa0008000
update_uboot=tftpboot 0xa0000000 u-boot.bin;protect off 0x0 0x2ffff;erase 0x0 0x2ffff;cp.b 0xa0000000 0x0 $(filesize)
update_nand=nand erase;tftpboot 0xa0000000 linux.bin;nand write 0xa0000000 0x0 0x220000;tftpboot 0xa0000000 romfs.bin;nand write oxa0000000 0x400000 0x220000
netmask=255.255.255.0
ethaddr=00:de:ad:b0:05:02
serverip=10.218.35.27
ipaddr=10.218.35.61
bootfile=iarmtest
bootcmd=run tftp_boot
bootdelay=5
loadaddr=0xa0008000
tfto_boot=tftp; go 0xa0008000
tftp_boot=tftp; go 0xa0008000
stdin=serial
stdout=serial
stderr=serial

Environment size: 827/4092 bytes

To set some parameter:

lpc-e2468 # set serverip 192.168.0.1

To set some parameter with long value with spaces use single quotes:

lpc-e2468 # set bootcmd 'run tftp_boot'

To save parameters to flash (retains the values between the boots):

lpc-e2468 # save

On my ubuntu I use tftpd-hpa. Put your binaries in /var/lib/tftpboot, just dont forget to start server:

sudo /etc/init.d/tftpd-hpa start

Substage 1. Initial file structure

First of all lets examine how porting is done by others. Lets go to os/ipaq1110 where port for some Samsung ARM is situated. This directory contains quite a few files. The files of interest for us for now are:

  • mkfile - pretty much self explanatory. Mkfile for Inferno's mk utility.
  • l.s - this is the startup code, written in assembly. Plan9 assembly to be clear.
  • ipaq - is sort of config file for rootfs.
  • main.c - everybody knows what this one is for. Inferno's main procedure contains hardware and os initialization code.
  • fns.h - contains function declarations.
  • dat.h - contains data structures descriptions.
  • mem.h - is for memory layout macros and stuff.
  • io.h - contains io stuff, but mine is empty.
  • other files are not as important as the mentioned ones.

We are going to follow this layout ... kinda.

First of all I want to make it clear that Im not going to use Inferno's compiler and assembly because these guys are a little bit harder to use than gcc which im used to and which is mature enough to be trusted. This brings us to the second subtask aside of porting to ARM7: porting to gcc from Plan9 C. How hard could that be anyway? Why am i so sure that it will be kinda easy? Because Inferno already has non-native Linux port which is built with gcc. So we "just" have to port the rest. In order to build binaries for arm, you should have arm-none-eabi toolchain installed on your machine.

Lets start with creating our own subdirectory inside os:

mkdir lpc-e2468-olimex

Create the following file structure:

lpc-e2468-olimex
    mkfile
    main.c
    load.s
    lpc-2468.ld
    lpc-2468

We will have to populate every of these files for the first thing we will do, which is LED blinking of course.

Substage 2. Blinking the LED!

This step might seem dumb, but the idea is to use all the Inferno's infrastructure to compile our code with gcc and link with ld properly.

Lets add file LPC24xx.h to our little port, so we wont have to type all the LPC addresses by hands.

The board has STAT_LED1(green, P4.17) and STAT_LED2(red, P4.16) LEDS both of which are active low. Populate main.c with the following code to deactivate red LED:

#include <stdbool.h>

#include "LPC24xx.h"

#define STAT_LED1 (1<<17) //red
#define STAT_LED2 (1<<16) //green
#define STAT_LED1_PS9_bits (1<<2 | 1<<3)
#define STAT_LED2_PS9_bits (1 | 1<<1)
#define STAT_LED1_PM9_bits (1<<2 | 1<<3)
#define STAT_LED2_PM9_bits (1 | 1<<1)

int main(void) {
  PINSEL9 &= ~(STAT_LED1_PS9_bits | STAT_LED2_PS9_bits); //make sure pin configuration is in default state
  PINMODE9 &= ~(STAT_LED1_PM9_bits | STAT_LED2_PM9_bits);

  FIO4MASK &= ~(STAT_LED1 | STAT_LED2);
  FIO4DIR |= (STAT_LED1 | STAT_LED2);
  FIO4CLR |= STAT_LED1; // turn on the red
  FIO4SET |= STAT_LED2; //turn off the green

  while(true) {

  }
  return 0;
}

And here comes our own mkfile:

<../../mkconfig

CONF=lpc-e2468
CONFLIST=lpc-e2468

SYSTARG=Inferno
OBJTYPE=arm-gcc
INSTALLDIR=$ROOT/Inferno/$OBJTYPE/bin

<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE

<| $SHELLNAME ../port/mkdevlist $CONF

OBJ=\
    load.$O\
    main.$O\
    $IP\
    $DEVS\
    $ETHERS\
    $LINKS\
    $PORT\
    $MISC\
    $OTHERS\
    $CONF.root.$O\

LIBNAMES=${LIBS:%=lib%.a}
LIBDIRS=$LIBS
CP=arm-none-eabi-objcopy

CFLAGS= -mcpu=arm7tdmi-s -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp -I./ -DLINUX_ARM
KERNDATE=`{$NDATE}

HFILES=


default:V: i$CONF

i$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES
    $CC $CFLAGS -DKERNDATE=$KERNDATE $CONF.c
    $LD -mcpu=arm7tdmi-s  -nostartfiles -Wl,-Map=main.map,--cref,--gc-sections -Tlpc_e2468.ld $OBJ $CONF.$O $LIBFILES -lgcc -lm -o ilpc-e2468.elf
    $CP -O binary ilpc-e2468.elf ilpc-e2468.bin
    arm-none-eabi-size ./ilpc-e2468.elf

<../port/portmkfile

The interesting part here is that we include $ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE which is mkfile by itself, but there is no default mkfile called mkfile-Inferno-arm-gcc

Create this file in mkfiles and put the following inside:

TARGMODEL=  Inferno
TARGSHTYPE= rc
CPUS=       arm

O=      o
OS=     o

AR=     arm-none-eabi-ar
ARFLAGS= ruvs

AS=arm-none-eabi-gcc -c
ASFLAGS= -x assembler-with-cpp -mcpu=arm7tdmi-s -c

CC=arm-none-eabi-gcc -O0 -std=c1x -fno-builtin -fms-extensions -DUSED(...)=void -DSET(...) -c
CFLAGS=     -O\
        -Wuninitialized -Wno-unused -Wreturn-type -Wimplicit\
        -I$ROOT/Inferno/arm/include\
        -I$ROOT/include\
    -I$ROOT/os/lpc-e2468\
        -DLINUX_ARM\

ANSICPP= -p

LD=arm-none-eabi-gcc
LDFLAGS=-nostartfiles 

SYSLIBS=

YACC=       yacc
YFLAGS=     -d

If you want to know how this is obtained, then open mkfile-Inferno-arm and compare to this one, these files are very similar, but Inferno-arm uses Plan9 compiler for arm called 5c instead of gcc.

We have main.c, mkfile and host mkfile ready, but we still need startup code and linkerscript.

So here comes the startup code, put it into load.s:

/* ***************************************************************************************************************

    load.s                      STARTUP  ASSEMBLY  CODE 
                                -----------------------


    Module includes the interrupt vectors and start-up code.

  *************************************************************************************************************** */

/* Stack Sizes */
.set  UND_STACK_SIZE, 0x00000004        /* stack for "undefined instruction" interrupts is 4 bytes  */
.set  ABT_STACK_SIZE, 0x00000004        /* stack for "abort" interrupts is 4 bytes                  */
.set  FIQ_STACK_SIZE, 0x00000004        /* stack for "FIQ" interrupts  is 4 bytes                   */
.set  IRQ_STACK_SIZE, 0X00000004        /* stack for "IRQ" normal interrupts is 4 bytes             */
.set  SVC_STACK_SIZE, 0x00000004        /* stack for "SVC" supervisor mode is 4 bytes               */



/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs (program status registers) */
.set  MODE_USR, 0x10                    /* Normal User Mode                                         */
.set  MODE_FIQ, 0x11                    /* FIQ Processing Fast Interrupts Mode                      */
.set  MODE_IRQ, 0x12                    /* IRQ Processing Standard Interrupts Mode                  */
.set  MODE_SVC, 0x13                    /* Supervisor Processing Software Interrupts Mode           */
.set  MODE_ABT, 0x17                    /* Abort Processing memory Faults Mode                      */
.set  MODE_UND, 0x1B                    /* Undefined Processing Undefined Instructions Mode         */
.set  MODE_SYS, 0x1F                    /* System Running Priviledged Operating System Tasks  Mode  */

.set  I_BIT, 0x80                       /* when I bit is set, IRQ is disabled (program status registers) */
.set  F_BIT, 0x40                       /* when F bit is set, FIQ is disabled (program status registers) */


.text
.arm


.global Reset_Handler
.global _start

.func   _start

_start:


# Reset Handler

Reset_Handler:  

                /* Setup a stack for each mode - note that this only sets up a usable stack
                for User mode.   Also each mode is setup with interrupts initially disabled. */

                ldr   r0, =_stack_end
                msr   CPSR_c, #MODE_UND|I_BIT|F_BIT     /* Undefined Instruction Mode  */
                mov   sp, r0
                sub   r0, r0, #UND_STACK_SIZE
                msr   CPSR_c, #MODE_ABT|I_BIT|F_BIT     /* Abort Mode */
                mov   sp, r0
                sub   r0, r0, #ABT_STACK_SIZE
                msr   CPSR_c, #MODE_FIQ|I_BIT|F_BIT     /* FIQ Mode */
                mov   sp, r0    
                sub   r0, r0, #FIQ_STACK_SIZE
                msr   CPSR_c, #MODE_IRQ|I_BIT|F_BIT     /* IRQ Mode */
                mov   sp, r0
                sub   r0, r0, #IRQ_STACK_SIZE
                msr   CPSR_c, #MODE_SVC|I_BIT|F_BIT     /* Supervisor Mode */
                mov   sp, r0
                sub   r0, r0, #SVC_STACK_SIZE
                msr   CPSR_c, #MODE_SYS|I_BIT|F_BIT     /* User Mode */
                mov   sp, r0

        /*
         * Data initialization.
         * NOTE: It assumes that the DATA size is a multiple of 4.
         */
        ldr     r1, =_textdata
        ldr     r2, =_data
        ldr     r3, =_edata
dataloop:
        cmp     r2, r3
        ldrlo   r0, [r1], #4
        strlo   r0, [r2], #4
        blo     dataloop
        /*
         * BSS initialization.
         * NOTE: It assumes that the BSS size is a multiple of 4.
         */
        mov     r0, #0
        ldr     r1, =_bss_start
        ldr     r2, =_bss_end
bssloop:
        cmp     r1, r2
        strlo   r0, [r1], #4
        blo     bssloop

                /* Enter the C code  */
                b       main

.endfunc
.end

I took it from some other LED blinking project... I think. Anyway, the linkerscript lpc-e2468.ld:

ENTRY(_start)

/* LPC-2468 memory map */
MEMORY 
{
    ram             : ORIGIN = 0xa0008000, LENGTH = 16M-0x8000
}

_stack_end = 0xa0000000 + 16M;
_vStackTop = _stack_end;

SECTIONS 
{
    . = 0xa0008000;

  .start : { *(.start)} >ram

  .text :  ALIGN(16) SUBALIGN(16)
    { 
        _text = .;
        KEEP(*(vectors))
        *(.text)
        *(.text.*)
        *(.rodata)
        *(.rodata.*)
        *(.glue_7t)
        *(.glue_7)
        *(.gcc*)
        *(.ctors)
        *(.dtors)
    } > ram

    .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)}

    __exidx_start = .;
    .ARM.exidx : {*(.ARM.exidx* .gnu.linkonce.armexidx.*)} > ram
    __exidx_end = .;

    .eh_frame_hdr : {*(.eh_frame_hdr)}

    .eh_frame : ONLY_IF_RO {*(.eh_frame)}

    . = ALIGN(4);
    _etext = .;
    _textdata = _etext;

    .data :
    {
        _data = .;
        *(.data)
        . = ALIGN(4);
        *(.data.*)
        . = ALIGN(4);
        *(.ramtext)
        . = ALIGN(4);
        _edata = .;
    } > ram AT > ram

    .bss :
    {
        _bss_start = .;
        *(.bss)
        . = ALIGN(4);
        *(.bss.*)
        . = ALIGN(4);
        *(COMMON)
        . = ALIGN(4);
        _bss_end = .;
    } > ram    

    . = ALIGN(4);
    _bss_end = . ;
    _end = .;
}

The lpc-2468 file:

lib
    kern

init
    bootinit

Lets try to build now:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk

mk: don't know how to make '/home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/include/u.h'

Ok, we have created a new target, which is the same as normal Inferno-arm except for compiler. Lets check if we can copy inferno-os-lpc2468/Inferno/arm/include to inferno-os-lpc2468/Inferno/arm-gcc/include and compile with this.

From inferno-os-lpc2468/os/lpc-2468-olimex run

mk

This should fail with some troubles with strtoll. gcc states that:

strtoll.c:4:1: error: conflicting types for 'strtoll'
 strtoll(const char *nptr, char **endptr, int base)
 ^
In file included from /home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm/include/lib9.h:2:0,
                 from strtoll.c:1:
/home/snegovick/dev/inferno/inferno-os-lpc2468/include/kern.h:369:14: note: previous declaration of 'strtoll' was here
 extern vlong strtoll(char*, char**, int);
              ^

Linux man strtoll says tath the definition in kern.h is incorrect. Lets correct it (by adding const):

-extern vlong   strtoll(char*, char**, int);
+extern vlong   strtoll(const char*, char**, int);

Then try to build again:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk

arm-none-eabi-ar ruvs /home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/lib/libkern.a abort.o abs.o atol.o charstod.o cistrcmp.o cistrncmp.o cistrstr.o cleanname.o convD2M.o convM2D.o convM2S.o convS2M.o dofmt.o exp.o fcallfmt.o floor.o fmt.o fmtprint.o fmtquote.o fmtstr.o fmtvprint.o getfields.o log.o memccpy.o memchr.o memcmp.o pow.o pow10.o qsort.o rune.o runestrlen.o sin.o seprint.o smprint.o snprint.o sqrt.o strcat.o strcmp.o strcpy.o strdup.o strecpy.o strlen.o strncmp.o strncpy.o strrchr.o strstr.o strtod.o strtol.o strtoll.o strtoul.o strtoull.o tokenize.o toupper.o u16.o u32.o u64.o utfecpy.o utflen.o utfnlen.o utfrrune.o utfrune.o vseprint.o vsmprint.o vsnprint.o
arm-none-eabi-ar: /home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/lib/libkern.a: No such file or directory

Ok, obviously we have to create subdirectory lib in arm-gcc target directory:

inferno-os-lpc2468$ mkdir Inferno/arm-gcc/lib

Then try to build again from inferno-os-lpc2468/os/lpc-2468-olimex:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk

This gives alot of errors like:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk 2>&1 | grep error

../port/devroot.c:8:1: error: unknown type name 'Rootdata'
../port/devroot.c:9:1: error: unknown type name 'Dirtab'
../port/devroot.c:12:1: error: unknown type name 'Chan'
../port/devroot.c:17:2: error: unknown type name 'Rootdata'
../port/devroot.c:20:3: warning: implicit declaration of function 'error' [-Wimplicit-function-declaration]
   error(Ebadspec);
../port/devroot.c:23:8: error: request for member 'sizep' in something not a structure or union
../port/devroot.c:24:12: error: request for member 'sizep' in something not a structure or union
../port/devroot.c:25:5: error: request for member 'size' in something not a structure or union
../port/devroot.c:26:14: error: request for member 'length' in something not a structure or union
../port/devroot.c:33:9: error: unknown type name 'Chan'
../port/devroot.c:33:30: error: unknown type name 'Dirtab'
../port/devroot.c:71:1: error: unknown type name 'Walkqid'
../port/devroot.c:72:10: error: unknown type name 'Chan'
../port/devroot.c:72:19: error: unknown type name 'Chan'
../port/devroot.c:83:10: error: unknown type name 'Chan'
../port/devroot.c:91:1: error: unknown type name 'Chan'
../port/devroot.c:92:10: error: unknown type name 'Chan'
../port/devroot.c:104:11: error: unknown type name 'Chan'
../port/devroot.c:109:10: error: unknown type name 'Chan'
../port/devroot.c:128:11: error: unknown type name 'Chan'
../port/devroot.c:134:1: error: unknown type name 'Dev'
../port/devroot.c:138:2: error: 'devreset' undeclared here (not in a function)
../port/devroot.c:139:2: error: 'devinit' undeclared here (not in a function)
../port/devroot.c:140:2: error: 'devshutdown' undeclared here (not in a function)
../port/devroot.c:142:2: error: 'rootwalk' undeclared here (not in a function)
../port/devroot.c:143:2: error: 'rootstat' undeclared here (not in a function)
../port/devroot.c:144:2: error: 'rootopen' undeclared here (not in a function)
../port/devroot.c:145:2: error: 'devcreate' undeclared here (not in a function)
../port/devroot.c:146:2: error: 'rootclose' undeclared here (not in a function)
../port/devroot.c:147:2: error: 'rootread' undeclared here (not in a function)
../port/devroot.c:148:2: error: 'devbread' undeclared here (not in a function)
../port/devroot.c:149:2: error: 'rootwrite' undeclared here (not in a function)
../port/devroot.c:150:2: error: 'devbwrite' undeclared here (not in a function)
../port/devroot.c:151:2: error: 'devremove' undeclared here (not in a function)
../port/devroot.c:152:2: error: 'devwstat' undeclared here (not in a function)

Hm looks like we forgot to include something.

Grepping on the sources for struct Dev gives the following results:

inferno-os-lpc2468/os/lpc-e2468-olimex$ grep "struct *Dev" ../../ -R
../../emu/port/devlogfs.c:typedef struct Devlogfs Devlogfs;
../../emu/port/devlogfs.c:typedef struct DevlogfsSession DevlogfsSession;
../../emu/port/devlogfs.c:struct Devlogfs {
../../emu/port/dat.h:typedef struct Dev Dev;
../../emu/port/dat.h:struct Dev
../../man/10/dev:struct Dev
../../os/port/devlogfs.c:typedef struct Devlogfs Devlogfs;
../../os/port/devlogfs.c:typedef struct DevlogfsSession DevlogfsSession;
../../os/port/devlogfs.c:struct Devlogfs {
../../os/port/portdat.h:typedef struct Dev  Dev;
../../os/port/portdat.h:typedef struct DevConf  DevConf;
../../os/port/portdat.h:struct Dev
../../os/pc/dat.h:struct DevConf

The file of interest for us is ../../os/port/portdat.h:struct Dev

To save some time, Ive greped for other structures and here is the new main.c:

#include <stdbool.h>

#include "LPC24xx.h"

#include "u.h"
#include "../port/lib.h"
#include "../port/error.h"
#include "../port/portfns.h"
#include "../port/portdat.h"

#define STAT_LED1 (1<<17) //red
#define STAT_LED2 (1<<16) //green
#define STAT_LED1_PS9_bits (1<<2 | 1<<3)
#define STAT_LED2_PS9_bits (1 | 1<<1)
#define STAT_LED1_PM9_bits (1<<2 | 1<<3)
#define STAT_LED2_PM9_bits (1 | 1<<1)

int main(void) {
  PINSEL9 &= ~(STAT_LED1_PS9_bits | STAT_LED2_PS9_bits); //make sure pin configuration is in default state
  PINMODE9 &= ~(STAT_LED1_PM9_bits | STAT_LED2_PM9_bits);

  FIO4MASK &= ~(STAT_LED1 | STAT_LED2);
  FIO4DIR |= (STAT_LED1 | STAT_LED2);
  FIO4CLR |= STAT_LED1; // turn on the red
  FIO4SET |= STAT_LED2; //turn off the green

  while(true) {

  }
  return 0;
}

Lets try to build again:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk clean
    for i in lpc-e2468 $CLEANCONFLIST
    do
        rm -f $i.c i$i i$i.* $i.ver
    done
    rm -f *.[o] *.root.[sh] errstr.h *.out

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk

The output again contains alot of errors like these:

../port/portfns.h:4:1: error: unknown type name 'Timer'
../port/portfns.h:5:1: error: unknown type name 'Cname'
../port/portfns.h:6:15: error: unknown type name 'Proc'

But this time these are the structures, that we should define ourself:

inferno-os-lpc2468/os/lpc-e2468-olimex$ grep "struct *Timer" ../../ -R
../../os/ks32/io.h:typedef struct TimerReg TimerReg;
../../os/ks32/io.h:struct TimerReg {
../../os/manga/io.h:typedef struct TimerReg TimerReg;
../../os/manga/io.h:struct TimerReg {
../../os/port/portdat.h:typedef struct Timer    Timer;
../../os/port/portdat.h:typedef struct Timers   Timers;
../../os/port/portdat.h:struct Timer
../../os/port/portclock.c:struct Timers

So lets copy this into inferno-os-lpc2468/os/lpc-e2468-olimex/dat.h.

typedef struct Lock Lock;
typedef struct Ureg Ureg;
typedef struct Label Label;
typedef struct FPenv FPenv;
typedef struct Mach Mach;
typedef struct FPU FPU;
typedef ulong Instr;
typedef struct Conf Conf;

struct Lock
{
    ulong   key;
    ulong   sr;
    ulong   pc;
    int pri;
};

struct Label
{
  ulong sp;
  ulong pc;
};

enum
{
    FPINIT,
    FPACTIVE,
    FPINACTIVE
};

struct FPenv
{
    int x;
};

struct  FPU
{
    FPenv env;
};

struct Conf
{
    ulong   nmach;          /* processors */
    ulong   nproc;          /* processes */
    ulong   npage0;         /* total physical pages of memory */
    ulong   npage1;         /* total physical pages of memory */
    ulong   topofmem;       /* highest physical address + 1 */
    ulong   npage;          /* total physical pages of memory */
    ulong   base0;          /* base of bank 0 */
    ulong   base1;          /* base of bank 1 */
    ulong   ialloc;         /* max interrupt time allocation in bytes */
};

#include "../port/portdat.h"

struct Mach
{
  ulong splpc; /* pc of last caller to splhi */
    int     machno;     /* physical id of processor */
    ulong   ticks;      /* of the clock since boot time */
    Proc*   proc;       /* current process on this processor */
    Label   sched;      /* scheduler wakeup */
    ulong   cpuhz;

    /* stacks for exceptions */
    ulong   fiqstack[4];
    ulong   irqstack[4];
    ulong   abtstack[4];
    ulong   undstack[4];
    int stack[1];
};

extern Mach *m;
extern Proc *up;

typedef struct Vectorpage {
    void    (*vectors[8])(void);
    uint    vtable[8];
} Vectorpage;
extern Vectorpage *page0;

dat.h includes portdat.h already, so we can replace #include "../port/portdat.h" with #include "dat.h" in main.c. Also, put dat.h include under ../port/lib.h include and before ../port/portfns.h:

#include <stdbool.h>

#include "LPC24xx.h"

#include "u.h"
#include "../port/lib.h"
#include "dat.h"
#include "../port/portfns.h"
#include "../port/error.h"

Whew, now we can build:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk clean
    for i in lpc-e2468 $CLEANCONFLIST
    do
        rm -f $i.c i$i i$i.* $i.ver
    done
    rm -f *.[o] *.root.[sh] errstr.h *.out

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk

arm-none-eabi-gcc -c -x assembler-with-cpp -mcpu=arm7tdmi-s -c load.s
arm-none-eabi-gcc -O0 -std=c1x -fno-builtin -fplan9-extensions -fms-extensions -DUSED(...)=void -DSET(...) -c -mcpu=arm7tdmi-s -I/home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/include -I/home/snegovick/dev/inferno/inferno-os-lpc2468/include -I/home/snegovick/dev/inferno/inferno-os-lpc2468/libinterp -I./ -DLINUX_ARM main.c
In file included from dat.h:54:0,
                 from main.c:7:
../port/portdat.h:129:8: error: duplicate member 'head'
  Proc* head;
        ^
mk: arm-none-eabi-gcc -O0 -std=c1x ...  : exit status=exit(1)

What the ... Okay, lets check.

The problem is here:

struct Alarms
{
    QLock;
    Proc*   head;
};

This structure has anonymous first member. GCC doesnt like that even with -fplan9-extensions for some reason. This "feature" of plan9 c dialect is used everywhere, so we will have to fix quite a few places, and obviously that will make our inferno-os code different from the mainline =( Oh well.

Lets fix struct Alarms:

struct Alarms
{
    QLock q;
    Proc*   head;
};

Build fails again with:

../port/devroot.c:128:1: error: parameter name omitted

Thats because Inferno uses this kind of procedure implementations:

static long  
rootwrite(Chan*, void*, long, vlong)

GCC doesnt like that, lets fix it:

static long  
rootwrite(Chan* c, void* v, long l, vlong vl)

Failed again:

../port/devroot.c: In function 'rootclose':
../port/devroot.c:104:1: error: parameter name omitted

Fix:

@@ -101,7 +101,7 @@
  * sysremove() knows this is a nop
  */
 static void     
-rootclose(Chan*)
+rootclose(Chan* c)
 {
 }

Now we have thse errors:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk 2>&1 | grep error
../port/devroot.c:20:3: warning: implicit declaration of function 'error' [-Wimplicit-function-declaration]
   error(Ebadspec);
../port/devroot.c:138:2: error: 'devreset' undeclared here (not in a function)
../port/devroot.c:139:2: error: 'devinit' undeclared here (not in a function)
../port/devroot.c:140:2: error: 'devshutdown' undeclared here (not in a function)
../port/devroot.c:145:2: error: 'devcreate' undeclared here (not in a function)
../port/devroot.c:148:2: error: 'devbread' undeclared here (not in a function)
../port/devroot.c:150:2: error: 'devbwrite' undeclared here (not in a function)
../port/devroot.c:151:2: error: 'devremove' undeclared here (not in a function)
../port/devroot.c:152:2: error: 'devwstat' undeclared here (not in a function)

We dont care about warnings for now. If you try to grep devreset it turns out to be something device-specific. We dont want to deal with it now, so lets just edit lpc-e2468 file in our port and remove everything from dev section:

dev


code

lib
    kern

init
    bootinit

root
  /dev  /

Build it:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk
/bin/sh ../port/mkroot lpc-e2468
    /bin/sh ../port/mkdevc lpc-e2468 > lpc-e2468.c
awk: cmd. line:63: warning: escape sequence `\.' treated as plain `.'
(cd /home/snegovick/dev/inferno/inferno-os-lpc2468/libkern ; mk SHELLTYPE=sh SYSTARG=Inferno OBJTYPE=arm-gcc install)
warning: skipping missing include file: mkfile-arm-gcc: No such file or directory
arm-none-eabi-ar ruvs /home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/lib/libkern.a fmtstr.o fmtvprint.o getfields.o log.o memccpy.o memchr.o memcmp.o pow.o pow10.o qsort.o rune.o runestrlen.o sin.o seprint.o smprint.o snprint.o sqrt.o strcat.o strcmp.o strcpy.o strdup.o strecpy.o strlen.o strncmp.o strncpy.o strrchr.o strstr.o strtod.o strtol.o strtoll.o strtoul.o strtoull.o tokenize.o toupper.o u16.o u32.o u64.o utfecpy.o utflen.o utfnlen.o utfrrune.o utfrune.o vseprint.o vsmprint.o vsnprint.o
arm-none-eabi-gcc -c -x assembler-with-cpp -mcpu=arm7tdmi-s -c lpc-e2468.root.s
   arm-none-eabi-gcc -O0 -std=c1x -fno-builtin -fplan9-extensions -fms-extensions -DUSED(...)=void -DSET(...) -c -mcpu=arm7tdmi-s -I/home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/include -I/home/snegovick/dev/inferno/inferno-os-lpc2468/include -I/home/snegovick/dev/inferno/inferno-os-lpc2468/libinterp -I./ -DLINUX_ARM -DKERNDATE=1432466002 lpc-e2468.c
   arm-none-eabi-gcc -mcpu=arm7tdmi-s  -nostartfiles -Wl,-Map=main.map,--cref,--gc-sections -Tlpc_e2468.ld load.o main.o lpc-e2468.root.o lpc-e2468.o /home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/lib/libkern.a -lgcc -lm -o ilpc-e2468.elf
   arm-none-eabi-objcopy -O binary ilpc-e2468.elf ilpc-e2468.bin
   arm-none-eabi-size ./ilpc-e2468.elf
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 00000000a0008000
   text    data     bss     dec     hex filename
      0       0       0       0       0 ./ilpc-e2468.elf

Wow almost there. Why couldnt ld find the _start symbol? Because I forgot to put load.s contents in it :) So I just add load.s contents back to its place and rebuild:

inferno-os-lpc2468/os/lpc-e2468-olimex$ mk
arm-none-eabi-gcc -c -x assembler-with-cpp -mcpu=arm7tdmi-s -c load.s
/bin/sh ../port/mkroot lpc-e2468
(cd /home/snegovick/dev/inferno/inferno-os-lpc2468/libkern ; mk SHELLTYPE=sh SYSTARG=Inferno OBJTYPE=arm-gcc install)
warning: skipping missing include file: mkfile-arm-gcc: No such file or directory
arm-none-eabi-ar ruvs /home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/lib/libkern.a fmtstr.o fmtvprint.o getfields.o log.o memccpy.o memchr.o memcmp.o pow.o pow10.o qsort.o rune.o runestrlen.o sin.o seprint.o smprint.o snprint.o sqrt.o strcat.o strcmp.o strcpy.o strdup.o strecpy.o strlen.o strncmp.o strncpy.o strrchr.o strstr.o strtod.o strtol.o strtoll.o strtoul.o strtoull.o tokenize.o toupper.o u16.o u32.o u64.o utfecpy.o utflen.o utfnlen.o utfrrune.o utfrune.o vseprint.o vsmprint.o vsnprint.o
arm-none-eabi-gcc -c -x assembler-with-cpp -mcpu=arm7tdmi-s -c lpc-e2468.root.s
   arm-none-eabi-gcc -O0 -std=c1x -fno-builtin -fplan9-extensions -fms-extensions -DUSED(...)=void -DSET(...) -c -mcpu=arm7tdmi-s -I/home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/include -I/home/snegovick/dev/inferno/inferno-os-lpc2468/include -I/home/snegovick/dev/inferno/inferno-os-lpc2468/libinterp -I./ -DLINUX_ARM -DKERNDATE=1432466123 lpc-e2468.c
   arm-none-eabi-gcc -mcpu=arm7tdmi-s  -nostartfiles -Wl,-Map=main.map,--cref,--gc-sections -Tlpc_e2468.ld load.o main.o lpc-e2468.root.o lpc-e2468.o /home/snegovick/dev/inferno/inferno-os-lpc2468/Inferno/arm-gcc/lib/libkern.a -lgcc -lm -o ilpc-e2468.elf
   arm-none-eabi-objcopy -O binary ilpc-e2468.elf ilpc-e2468.bin
   arm-none-eabi-size ./ilpc-e2468.elf
   text    data     bss     dec     hex filename
    316       0       0     316     13c ./ilpc-e2468.elf

Cool, we have 316 Bytes of awesomness. Lets try to TFTP-boot our device.

inferno-os-lpc2468/os/lpc-e2468-olimex$ sudo rm /var/lib/tftpboot/iarmtest 
inferno-os-lpc2468/os/lpc-e2468-olimex$ sudo cp ./ilpc-e2468.bin /var/lib/tftpboot/iarmtest
inferno-os-lpc2468/os/lpc-e2468-olimex$ sudo /etc/init.d/tftpd-hpa restart
[ ok ] Restarting tftpd-hpa (via systemctl): tftpd-hpa.service.

I use ckermit to access serial port with this config:

inferno-arm7tdmi/os/lpc-e2468-olimex$ cat ~/S0
set port /dev/ttyUSB0
set speed 115200
set serial 8n1
set parity none
set car off
set flow-control none
set prefixing all
connect

So to connect to ttyUSB0 I just type:

kermit -y ~/S0

Here is device's console output:

**********************************************
*                 LPC-E2468                  *
*     www.olimex.com/dev/lpc-e2468.html      *
**********************************************
..


U-Boot 1.3.1 (Apr 15 2008 - 16:32:08)

CPU:   LPC2468 (ARM7tdmi-s from NXP)
       running at 57.6 MHz (12 MHz crystal)
DRAM:  16 MB
Flash: 500 kB
NAND:  128 MiB
In:    serial
Out:   serial
Err:   serial
Hit any key to stop autoboot:  0 

emac: check_phy - (22, 1619)
emac: link status = 100Mbps, full duplex
emac: MAC address =  0:de:ad:b0: 5: 2
TFTP from server 10.218.35.27; our IP address is 10.218.35.61
Filename 'iarmtest'.
Load address: 0xa0008000
Loading: #
done
Bytes transferred = 316 (13c hex)
## Starting application at 0xA0008000 ...

And here is device itself:

green led: