Search code examples
cassemblyarmqemu

Arm bootloader and kernel visualisation error


I want make tiny OS for ARM system. I have this bootloader:

.global _start

.text
_start:
    /* Set up the stack */
    ldr sp, =stack_top

    /* Call the kernel entry point */
    bl kernel_entry

    /* Infinite loop */
hang:
    b hang

.bss
.space 1024
stack_top:

And Here kernel:

void kernel_entry(void) {
    const char *msg = "Hello, ARM world!\n";
    char *uart = (char *)0x101f1000;  // UART base address for ARM

    while (*msg) {
        *uart = *msg++;
    }

    while (1);
}

I wrote make file for compiling:

all: bootloader.bin

bootloader.bin: bootloader.elf
    arm-none-eabi-objcopy -O binary bootloader.elf bootloader.bin

bootloader.elf: bootloader.o kernel.o
    arm-none-eabi-ld -Ttext 0x8000 -o bootloader.elf bootloader.o kernel.o

kernel.o: kernel.c
    arm-none-eabi-gcc -ffreestanding -nostdlib -c -o kernel.o kernel.c

bootloader.o: bootloader.s
    arm-none-eabi-as -o bootloader.o bootloader.s

run: bootloader.bin kernel.img
    ./run.sh

clean:
    rm -f *.o *.elf *.bin

I use command for runing qemu subsystem:

qemu-system-arm -M versatilepb -m 128M -kernel "C:/Users/Ukraine/Documents/FlowyOS/bootloader.bin" -initrd "C:/Users/Ukraine/Documents/FlowyOS/kernel.img" -d int

And i have 'error' cuz in wm i did not see hello world text:

enter image description here

Any ides what can be wrong and how to fix it?


Solution

  • strap.s

    .globl _start
    _start:
        mov sp,#0x20000
        bl notmain
    hang:
        b hang
    
    .globl PUT32
    PUT32:
        str r1,[r0]
        bx lr
    

    notmain.c

    void PUT32 ( unsigned int, unsigned int );
    int notmain ( void )
    {
        unsigned int ra;
        for(ra=0;;ra++)
        {
            ra&=7;
            PUT32(0x101f1000,0x30+ra);
        }
        return(0);
    }
    

    memmap

    /* memmap */
    MEMORY
    {
        ram  : ORIGIN = 0x00000000, LENGTH = 32K
    }
    
    SECTIONS
    {
       .text : { *(.text*) } > ram
       .bss  : { *(.text*) } > ram
    }
    

    build

    arm-none-eabi-as --warn --fatal-warnings -march=armv5t strap.s -o strap.o
    arm-none-eabi-gcc -c -Wall -O2 -nostdlib -nostartfiles -ffreestanding -march=armv5t notmain.c -o notmain.o
    arm-none-eabi-ld strap.o notmain.o -T memmap -o notmain.elf
    arm-none-eabi-objdump -D notmain.elf > notmain.list
    arm-none-eabi-objcopy notmain.elf -O binary notmain.bin
    

    Inspect

    00000000 <_start>:
       0:   e3a0d802    mov sp, #131072 ; 0x20000
       4:   eb000002    bl  14 <notmain>
    
    00000008 <hang>:
       8:   eafffffe    b   8 <hang>
    
    0000000c <PUT32>:
       c:   e5801000    str r1, [r0]
      10:   e12fff1e    bx  lr
    
    00000014 <notmain>:
      14:   e92d4070    push    {r4, r5, r6, lr}
      18:   e3a04000    mov r4, #0
      1c:   e59f5014    ldr r5, [pc, #20]   ; 38 <notmain+0x24>
      20:   e2044007    and r4, r4, #7
      24:   e2841030    add r1, r4, #48 ; 0x30
      28:   e1a00005    mov r0, r5
      2c:   ebfffff6    bl  c <PUT32>
      30:   e2844001    add r4, r4, #1
      34:   eafffff9    b   20 <notmain+0xc>
      38:   101f1000    andsne  r1, pc, r0
    

    To run as a bin need the entry point up front.

    One way to run is

    qemu-system-arm -M versatilepb -m 128M -nographic -kernel notmain.bin
    

    It will spew out

    01234567012345670123456701234567012345670...
    

    To stop/exit ctrl-a then x

    Or

    qemu-system-arm -M versatilepb -m 128M -kernel notmain.bin
    

    Then ctrl-alt-3 (Linux of course, if Windows I don't know how, but it's possible) to get to the serial console.

    That was position independent without trying. Let's see what is going on with the address space.

    .globl GETPC
    GETPC:
        mov r0,pc
        bx lr
    
    hexstring(GETPC());
    

    gives 0x10054 (different binary not shown). So 0x10000 based (not 0x8000 on my machine today).

    So can try this

    MEMORY
    {
        ram  : ORIGIN = 0x00010000, LENGTH = 32K
    }
    
    SECTIONS
    {
       .text : { *(.text*) } > ram
       .bss  : { *(.text*) } > ram
    }
    
    Disassembly of section .text:
    
    00010000 <_start>:
       10000:   e3a0d802    mov sp, #131072 ; 0x20000
       10004:   eb000002    bl  10014 <notmain>
    
    00010008 <hang>:
       10008:   eafffffe    b   10008 <hang>
    
    0001000c <PUT32>:
       1000c:   e5801000    str r1, [r0]
       10010:   e12fff1e    bx  lr
    
    00010014 <notmain>:
       10014:   e92d4070    push    {r4, r5, r6, lr}
       10018:   e3a04000    mov r4, #0
       1001c:   e59f5014    ldr r5, [pc, #20]   ; 10038 <notmain+0x24>
       10020:   e2044007    and r4, r4, #7
       10024:   e2841030    add r1, r4, #48 ; 0x30
       10028:   e1a00005    mov r0, r5
       1002c:   ebfffff6    bl  1000c <PUT32>
       10030:   e2844001    add r4, r4, #1
       10034:   eafffff9    b   10020 <notmain+0xc>
       10038:   101f1000    andsne  r1, pc, r0
    

    and run the elf file

    qemu-system-arm -M versatilepb -m 128M -nographic -kernel notmain.elf
    

    And that works fine.

    void notmain ( void )
    {
        const char *msg = "Hello, ARM world!\n";
        char *uart = (char *)0x101f1000;  // UART base address for ARM
    
        while (*msg) {
            *uart = *msg++;
        }
    }
    

    A number of reasons why this should not work for language reasons, but...

    00010014 <notmain>:
       10014:   e3a03048    mov r3, #72 ; 0x48
       10018:   e59f2014    ldr r2, [pc, #20]   ; 10034 <notmain+0x20>
       1001c:   e59f1014    ldr r1, [pc, #20]   ; 10038 <notmain+0x24>
       10020:   e5c13000    strb    r3, [r1]
       10024:   e5f23001    ldrb    r3, [r2, #1]!
       10028:   e3530000    cmp r3, #0
       1002c:   1afffffb    bne 10020 <notmain+0xc>
       10030:   e12fff1e    bx  lr
       10034:   0001003c    andeq   r0, r1, ip, lsr r0
       10038:   101f1000    andsne  r1, pc, r0
    

    r1 points to the uart, r2 points at the string.

    It created an .rodata section even though I did not specify one.

    Disassembly of section .rodata.str1.4:
    
    0001003c <.rodata.str1.4>:
       1003c:   6c6c6548    cfstr64vs   mvdx6, [ip], #-288  ; 0xfffffee0
       10040:   41202c6f            ; <UNDEFINED> instruction: 0x41202c6f
       10044:   77204d52            ; <UNDEFINED> instruction: 0x77204d52
       10048:   646c726f    strbtvs r7, [ip], #-623 ; 0xfffffd91
       1004c:   Address 0x000000000001004c is out of bounds.
    
    qemu-system-arm -M versatilepb -m 128M -nographic -kernel notmain.elf
    
    Hello, ARM world!
    QEMU: Terminated
    

    You want a volatile for the uart pointer, or other solutions. Want to have control over the linker script.

    The -Ttext 0x8000 is going to take a/the stock/default linker script and basically hack it up with changing where .text lives. You will want to just control the whole link and not do this, you can see how trivial linker scripts can be despite almost everyone grossly over complicating theirs.

    If I take my binary that prints numbers out and do this

    ENTRY(_start)
    MEMORY
    {
        ram  : ORIGIN = 0x0002000, LENGTH = 32K
    }
    
    SECTIONS
    {
       .text : { *(.text*) } > ram
       .bss  : { *(.text*) } > ram
    }
    

    Telling the linker to put the entry point into the binary (other than a default of the first thing in the memory image). And run with the elf.

    00002054 
    

    And your 0x8000 and other kernel elf images would do this as well, just to force the entry point...

    b .
    b .
    b .
    b .
    b .
    
    .globl _start
    _start:
    reset:
        mov sp,#0x10000
        bl notmain
    hang:
        b hang
    
    
    Disassembly of section .text:
    
    00002000 <_start-0x14>:
        2000:   eafffffe    b   2000 <_start-0x14>
        2004:   eafffffe    b   2004 <_start-0x10>
        2008:   eafffffe    b   2008 <_start-0xc>
        200c:   eafffffe    b   200c <_start-0x8>
        2010:   eafffffe    b   2010 <_start-0x4>
    
    00002014 <_start>:
        2014:   e3a0d801    mov sp, #65536  ; 0x10000
        2018:   eb000026    bl  20b8 <notmain>
    
    0000201c <hang>:
        201c:   eafffffe    b   201c <hang>
    
    00002020 <GETPC>:
        2020:   e1a0000f    mov r0, pc
        2024:   e12fff1e    bx  lr
    
    00002028 
    

    So it is really using the entry point not simply executing from the start.

    It may be that you simply are not looking at the console output screen. Without seeing a disassembly/dump of your binary, it is difficult to tell why yours is not working. Want to boot something bare-metal, disassembly and dumping the binary is examined before even trying to run.