Last time I wrote about writing and debugging bare metal ARM software using the CodeSourcery toolchain. Now I want to exploit QEMU’s emulation of a complete system and create the simplest “Hello world!” example.
The QEMU emulator supports the VersatilePB platform, that contains an ARM926EJ-S core and, among other peripherals, four UART serial ports; the first serial port in particular (UART0) works as a terminal when using the -nographic
or “-serial stdio
” qemu option. The memory map of the VersatilePB board is implemented in QEMU in this board-specific C source; from that I note the address where the UART0 is mapped: 0x101f1000
. The code that emulates the serial port inside QEMU (here in the source repository) implements a subset of the functionalities of the PL011 Prime Cell UART from ARM; there is a useful technical manual from the ARM info center that describes how to interact with the hardware. In details, there is a register (UARTDR) that is used to transmit (when writing in the register) and receive (when reading) bytes; this register is placed at offset 0x0
, so I need to read and write at the beginning of the memory allocated for the UART0.
To implement the simple “Hello world!” printing, I wrote this test.c
file:
volatile unsigned int * const UART0DR = (unsigned int *)0x101f1000; void print_uart0(const char *s) { while(*s != '\0') { /* Loop until end of string */ *UART0DR = (unsigned int)(*s); /* Transmit char */ s++; /* Next char */ } } void c_entry() { print_uart0("Hello world!\n"); }
The code is pretty straightforward; a couple of details:
- The
volatile
keyword is necessary to instruct the compiler that the memory pointed by UART0DR can change or has effects independently of the program. - The
unsigned int
type enforces 32-bits read and write access. - The QEMU model of the PL011 serial port ignores the transmit FIFO capabilities; in a real system on chip the “Transmit FIFO Full” flag must be checked in the UARTFR register before writing on the UARTDR register.
The QEMU emulator is written especially to emulate Linux guest systems; for this reason its startup procedure is implemented specifically: the -kernel
option loads a binary file (usually a Linux kernel) inside the system memory starting at address 0x00010000
. The emulator starts the execution at address 0x00000000
, where few instructions (already in place) are used to jump at the beginning of the kernel image. The interrupt table of ARM cores, usually placed at address 0x00000000
, is not present, and the peripheral interrupts are disabled at startup, as needed to boot a Linux kernel. Knowing this, to implement a working emulation I need to considerate a few things:
- The software must be compiled and linked to be placed at
0x00010000
- I need to create a binary image of our program
- I can ignore interrupt handling for now
This is the startup.s
assembler file I wrote, simplified from the one I wrote in the previous blog post:
.global _Reset _Reset: LDR sp, =stack_top BL c_entry B .
And this is the linker script test.ld
, modified from last time to place the program at the right address (thanks to Gnurou for the suggestion to specify only the text section of startup.o
):
ENTRY(_Reset) SECTIONS { . = 0x10000; .startup . : { startup.o(.text) } .text : { *(.text) } .data : { *(.data) } .bss : { *(.bss COMMON) } . = ALIGN(8); . = . + 0x1000; /* 4kB of stack memory */ stack_top = .; }
To create the binary file, the CodeSourcery toolchain (arm-none-eabi type) must be installed and the PATH
environmental variable must be set accordingly. These are the commands to run:
$ arm-none-eabi-as -mcpu=arm926ej-s -g startup.s -o startup.o $ arm-none-eabi-gcc -c -mcpu=arm926ej-s -g test.c -o test.o $ arm-none-eabi-ld -T test.ld test.o startup.o -o test.elf $ arm-none-eabi-objcopy -O binary test.elf test.bin
These commands create a test.elf
program and a test.bin
binary image that I can use with the QEMU emulator for ARM systems. In Debian, QEMU can be installed running “apt-get install qemu
” as root, and the installation includes the qemu-system-arm
program that I need in my example. In Ubuntu machines, this program is not present in the qemu
package but is placed instead in the qemu-kvm-extras
package; for this reason the “sudo apt-get install qemu-kvm-extras
” command must be used to install it.
To run my program in the emulator, the command is:
$ qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin
The -M
option specifies the emulated system. The program prints “Hello world!” in the terminal and runs indefinitely; to exit QEMU, press “Ctrl + a” and then “x”.
It is possible also to debug the program using the CodeSourcery version of gdb, because QEMU implements a gdb connector using a TCP connection. To do so, I run the emulator with the correct options as follows:
$ qemu-system-arm -M versatilepb -m 128M -nographic -s -S -kernel test.bin
This command freezes the system before executing any guest code, and waits for a connection on the TCP port 1234. From another terminal, I run arm-none-eabi-gdb
and enter the commands:
target remote localhost:1234 file test.elf
This connects to the QEMU system and loads the debugging symbols of the test program, whose binary image is already loaded in the system memory. From there, it is possible to run the program with the continue
command, single-step the program and debug it in general. The exit
command in gdb closes both the debugger and the emulator.
To summarize the necessary steps to create a “Hello world” program:
- Install CodeSourcery toolchain
- Install QEMU (in particular
qemu-system-arm
) - Write the
test.c
,startup.s
andtest.ld
source files - Build the test ELF and binary image
- Run QEMU ARM emulator using the created binary image as a kernel
- Run the gdb debugger and attach to QEMU
Greg Olin
2010/05/13
Great set of articles on ARM and QEMU. I’ve been working backwards from the oldest article on the simplest program for bare metal following your instructions. I am running 32-bit Ubuntu 10.04 and I installed qemu-kvm in order to try the Hello world on bare metal and I find that QEMU is seg faulting, even when I try run with -S which should halt the processor before running my binary. Any hints at what I should do to isolate the problem? Is there another ARM platform that I should try instead of VersatilePB?
Greg Olin
2010/05/13
I built QEMU from source and that one worked both for this example and your U-boot example. There is something wrong with the Ubuntu package. I tried to run the Ubuntu qemu-system-arm under gdb but all the symbols are stripped out, so I built it from source thinking that that way I’d have symbols and I could see where the segmentation fault was, but everything worked instead 🙂
Balau
2010/05/17
I haven’t tried on Lucid yet. The most probable cause of segfault is writing to the UART address. I tried to get the differences using “apt-get source qemu-kvm-extras”, but the diff file is quite big and I can’t understand yet what could cause it.
Balau
2010/05/23
Luckily I found a workaround. Just add the memory option:
qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin
The option should be already the default for the versatile. I already filed a bug and I’m going to add the workaround to my blog posts.
https://bugs.launchpad.net/ubuntu/+source/qemu-kvm/+bug/584480
I also noticed that the -S command does not freeze QEMU on start.
Robert Smith
2010/10/14
I have qemu related question. If you write something to UART0, then this output will appear on the qemu serial console, which can be opened by . I also noticed that combination opens parallel0 console.
I wonder whether it is possible to configure qemu in such way that it will allow to see output of UART1 and UART2 with help of and for example?
Thanks
Balau
2010/10/14
If you specify “serial” option multiple times, it will affect the other serial ports.
For example you can run:
$ qemu-system-arm -m 128M -M versatilepb -m 128M -kernel test.bin -serial stdio -serial telnet:localhost:1235,server -serial telnet:localhost:1236,server
It will wait for the two telnet connection that you can initiate from other terminals, and then you can use three UARTs.
You can also use “socat” to create pseudo-terminal to attach instead of using QEMU telnet server.
DangNguyen
2010/11/11
Do you know how to QEMU control interrupt event. And how to test it on QEMU (I mean how write a program to check UART serial ports interrupt event on QEMU).
I try to write a program but it doesn’t work
The startup file
.section INTERRUPT_VECTOR, “x”
.global _Reset
_Reset:
B Reset_Handler
B .
B .
B .
B .
B .
B IRQ_Handler
B .
Reset_Handler:
LDR sp, =stack_top
BL c_entry
B .
B IRQ_Handler jump to interrupt service routine
And this is main program
//this program use to check interrupt on uart serial port on realview platform
//uart serial port is mapped at 0x10009000 on realview platform
volatile unsigned char * const uart_add = (unsigned char *)0x10009000;
//interrupt service routine
// do sth when interrupt happen
void __attribute__((interrupt(“IRQ”))) IRQ_Handler()
{
asm (
“mov r1,#200 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#201 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#202 \n\t”
“add r1,r1,#10 \n\t”
:
:
: “r1”);
}
// this function enable interrupt
void event_EnableIRQ (void)
{
asm volatile (
“MRS r1, CPSR \n\t”
“BIC r1, r1, #0x80 \n\t”
“MSR CPSR_c, r1 \n\t”
);
}
// c_entry is called on startup
void c_entry() {
int i=0;
event_EnableIRQ();
asm (
“mov r1,#100 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#101 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#102 \n\t”
“add r1,r1,#10 \n\t”
:
:
: “r1”);
*uart_add=65;
asm (
“mov r1,#120 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#121 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#122 \n\t”
“add r1,r1,#10 \n\t”
:
:
: “r1”);
i=*uart_add;
asm (
“mov r1,#150 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#151 \n\t”
“add r1,r1,#10 \n\t”
“mov r1,#152 \n\t”
“add r1,r1,#10 \n\t”
:
:
: “r1”);
}
I use asm code in c to check code in log file easier
(-M realview-pbx-a9 -nographic -kernel /root/test_program/test_interrupt_2/test.bin -d in_asm)
The problem is this program doesn’t jump to interrupt service routine when read and write at 0x10009000 address
Do you have any advice for me?
Thank you very much!
Balau
2010/11/13
The problem I see right away is that you are emulating a Cortex-A processor, that has a different way of dealing with exceptions with respect to ARM926. While the ARM926 jumps to the exception table and executes the instruction that is there (in your example, a branch to the IRQ handler), the Cortex-A expects the address of the exception written directly in the table. Other than that, the list of exceptions is different.
Unfortunately the ARMv7 reference manual is available only to ARM customers but there’s enough information in the RealView developer tools documents: take a look at RealView Compilation Tools Developer Guide, in the chapter “Handling Processor Exceptions”.
Other than that, if you want to check log files easy and need assembly code, you can run:
$ arm-none-eabi-objdump -dS test.elf > test.code
assuming that your compiled program is called test.elf like my example, this command will create a file containing the assembly code and the relative source code complete with execution address.
I think there should be code in your program that copies the interrupt vectors into address 0x00000000, because QEMU loads the file that you specify with “kernel” at a higher address.
Usually you have to enable interrupts in many points: inside the ARM core (that’s what you did), on the peripheral registers (usually some “enable interrupt” flags) and inside the interrupt controller (if there is one). This procedure is dependent on the platform, maybe the realview-pbx-a9 platform documentation explains it in details.
Hope this helps.
DangNguyen
2010/11/14
Thank for respond. It’s really helpfull for me.
>> take a look at RealView Compilation Tools Developer Guide
I’m reading it. I hope It will describe arm interrupt in detail
>> you can run:
>> $ arm-none-eabi-objdump -dS test.elf > test.code
Simple command but very effective. I will use it.
>>Usually you have to enable interrupts in many points: inside the ARM core
>> (that’s what you did), on the peripheral registers (usually some “enable interrupt” flags)
>> and inside the interrupt controller (if there is one)
As you said, my program have alot of problems, I didn’t initial for device and interrupt controller.
Understand how QEMU trap interrupt event is my target. So I try to write a program that use interrupt service routine than I’ll debug to understand QEMU. But at this time my program have a lot of issue. If you know how QEMU trap interrupt event can you give me some advice.
Thank you very much
Gnurou
2010/11/15
Thanks for this great post, concise but explains a lot. I enjoyed going through it.
It seems to me that the linker script as it is would include the contents of all sections (including debug information) of startup.o into the .startup section of test.elf – and actually, this is confirmed by objdump:
After the code of startup.o you get hundreds of bytes (not) worth of binary data, which corresponds to the dump of other sections of the object file. Changing line 5 of the linker script to
Only includes the code of startup.o and results in a binary image that is 148 bytes versus 364 for the original one.
Balau
2010/11/15
Thanks for the suggestion, I added it to the post.
e_pech
2010/12/31
Hello, first of all thank you for this tutorial. I want to get more into Linux and ARM.
I tried to follow your tutorial, I downloaded the CodeSourcery cross compiler, I made all 3 files you mention in this article (startup.s, test.c, test.ld) exactly as you posted them here.
I’m able to execute the first 2 commands (as and gcc) but when I get to the point of linking it throws some errors:
pech@Pech-Ubuntu:~/Programacion/Embedded/HelloWorldArm$ arm-as -mcpu=arm926ej-s -g startup.s -o startup.o
pech@Pech-Ubuntu:~/Programacion/Embedded/HelloWorldArm$ arm-gcc -c -mcpu=arm926ej-s -g test.c -o test.o
pech@Pech-Ubuntu:~/Programacion/Embedded/HelloWorldArm$ arm-ld -T test.ld test.o startup.o -o test.elf
test.o:(.ARM.exidx+0x0): undefined reference to `__aeabi_unwind_cpp_pr0'
test.o:(.ARM.exidx+0x8): undefined reference to `__aeabi_unwind_cpp_pr1'
pech@Pech-Ubuntu:~/Programacion/Embedded/HelloWorldArm$
Any suggestions on what I’m doing wrong?
Thank you for your help, hope to hear from you 🙂
e_pech
2010/12/31
Hello, what I added dummy functions named:
void __aeabi_unwind_cpp_pr0(void)
void __aeabi_unwind_cpp_pr1(void)
just as the U-Boot guys did (googled it 🙂 )
Is that the best solution?
Balau
2010/12/31
I don’t think you are using the CodeSourcery compiler: the tools have a “
arm-none-eabi-
” prefix, such as “arm-none-eabi-gcc
” but your commands say “arm-gcc
“.I suppose the solution you found will work, but I have no idea what those two function should do :).
e_pech
2010/12/31
Oh, I’m sorry, I made soft links to the names because they were too long in my opinion. 🙂
Sorry for not pointing that out.
pech@Pech-Ubuntu:/usr/local/Sourcery_G++_Lite/bin$ ls -l
total 16924
lrwxrwxrwx 1 root root 25 2010-12-31 00:39 arm-as -> arm-none-linux-gnueabi-as
lrwxrwxrwx 1 root root 26 2010-12-30 23:01 arm-gcc -> arm-none-linux-gnueabi-gcc
lrwxrwxrwx 1 root root 26 2010-12-31 01:22 arm-gdb -> arm-none-linux-gnueabi-gdb
lrwxrwxrwx 1 root root 25 2010-12-30 23:03 arm-ld -> arm-none-linux-gnueabi-ld
-rwxr-xr-x 1 root root 569820 2010-11-07 08:53 arm-none-linux-gnueabi-addr2line
-rwxrwxr-x 2 root root 593236 2010-11-07 08:53 arm-none-linux-gnueabi-ar
-rwxrwxr-x 2 root root 1046336 2010-11-07 08:53 arm-none-linux-gnueabi-as
-rwxr-xr-x 2 root root 225860 2010-11-07 08:53 arm-none-linux-gnueabi-c++
-rwxr-xr-x 1 root root 572028 2010-11-07 08:53 arm-none-linux-gnueabi-c++filt
-rwxr-xr-x 1 root root 224196 2010-11-07 08:53 arm-none-linux-gnueabi-cpp
-rwxr-xr-x 1 root root 18612 2010-11-07 08:53 arm-none-linux-gnueabi-elfedit
-rwxr-xr-x 2 root root 225860 2010-11-07 08:53 arm-none-linux-gnueabi-g++
-rwxr-xr-x 2 root root 222948 2010-11-07 08:53 arm-none-linux-gnueabi-gcc
-rwxr-xr-x 2 root root 222948 2010-11-07 08:53 arm-none-linux-gnueabi-gcc-4.5.1
-rwxr-xr-x 1 root root 26780 2010-11-07 08:53 arm-none-linux-gnueabi-gcov
-rwxr-xr-x 1 root root 3186492 2010-11-07 08:53 arm-none-linux-gnueabi-gdb
-rwxr-xr-x 1 root root 3186492 2010-11-07 08:53 arm-none-linux-gnueabi-gdbtui
-rwxr-xr-x 1 root root 630944 2010-11-07 08:53 arm-none-linux-gnueabi-gprof
-rwxrwxr-x 2 root root 987204 2010-11-07 08:53 arm-none-linux-gnueabi-ld
-rwxrwxr-x 2 root root 579164 2010-11-07 08:53 arm-none-linux-gnueabi-nm
-rwxrwxr-x 2 root root 726936 2010-11-07 08:53 arm-none-linux-gnueabi-objcopy
-rwxrwxr-x 2 root root 866012 2010-11-07 08:53 arm-none-linux-gnueabi-objdump
-rwxrwxr-x 2 root root 593268 2010-11-07 08:53 arm-none-linux-gnueabi-ranlib
-rwxr-xr-x 1 root root 340780 2010-11-07 08:53 arm-none-linux-gnueabi-readelf
-rwxr-xr-x 1 root root 572288 2010-11-07 08:53 arm-none-linux-gnueabi-size
-rwxr-xr-x 1 root root 363252 2010-11-07 08:53 arm-none-linux-gnueabi-sprite
-rwxr-xr-x 1 root root 572220 2010-11-07 08:53 arm-none-linux-gnueabi-strings
-rwxrwxr-x 2 root root 726936 2010-11-07 08:53 arm-none-linux-gnueabi-strip
lrwxrwxrwx 1 root root 30 2010-12-31 00:40 arm-objcopy -> arm-none-linux-gnueabi-objcopy
pech@Pech-Ubuntu:/usr/local/Sourcery_G++_Lite/bin$
e_pech
2010/12/31
My Issue was that I downloaded a different compiler (GNU/Linux) instead of the EABI. Sorry but thanks 🙂
Ravi Teja G
2011/01/17
hi,
Thanks for this wonderful post. I have been following your blog for a long time. I am actually a fan of yours.
I tried to use qemu+gdb like you did in this post but qemu doesn’t freeze and it just keeps executing even when i try to stop it from gdb. i want to set breakpoints from gdb and do all kinds of stuff like single stepping, memory modification, etc.
Thanks
Balau
2011/01/17
In my experiments I found that the qemu-system-arm present in the Ubuntu package does not block itself for debugging, but if you compile QEMU from source it works correctly.
So, I suggest you to download QEMU original source and compile it, and then the “-S -s” options should work as expected.
ola_d
2011/05/13
Thanks for this post, it was really helpful to get me started with bare-metal qemu!
I tried your bare-metal Hello world example with GNUARM
http://gnuarm.com/bu-2.16.1_gcc-4.0.1-c-c++_nl-1.13.0_gi-6.1_x86-64.tar.bz2
on a Centos release 5.6 on Intel Xeon 64-bit host
It worked out-of-the-box!
Thanks
Siddhesh
2011/08/29
How to specify .a lib files in ld command. I have tried follow:
arm-none-eabi-ld -T test.ld -L/library_files_path main.o startup.o -O test.elf -llibname.
It is not building .elf file. Giving errors like undefined reference to some functions.
Balau
2011/08/29
Your method should work if the library file has a format “lib*.a” and you must remove the “lib” prefix when adding the library with “-l”
For example to add “libtest1.a” you specify the option “-ltest1”
Use also the “–verbose” option to check what the linker is trying to do.
You can otherwise try to add directly the “libname.a” file with the .o:
arm-none-eabi-ld -T test.ld main.o startup.o libname.a -O test.elf
Diego
2011/12/15
Hi Balau,
I have an ARM1176-jzf-s board. Works nicely with Linux etc …. but I am trying to get into baremetal programming. I am trying to search through whatever documents came with it (not much), I would like a pointer on what do I need to start metal programming on real board:
start of RAM or NAND flash and what else? I just want to be able to write a correct script and initscript files for this and then I’ll experiment from there.
Balau
2011/12/15
The main document should be the chipmaker’s user manual. For example the BeagleBone has an AM3359, and Texas Instruments provides the tech manual for download from their website. That is the basis to start bare-metal programming on that object, especially the memory map and the behavior at reset.
As another example, the Raspberry Pi board has a BCM2835 chip, and I seem to understand by reading in this forum that to access the documentation you need to ask Broadcom, they make you sign an NDA (non-disclosure agreement) and then they deliver the docs. Maybe you are struggling for a similar reason, without the docs it’s very hard to do what you want to do, and it involves some reverse engineering.
Ivan Chen
2011/12/22
Hi Balau,
I’m a beginner of QEMU, I want to port arduino uno become a QEMU guest target.
In you article, you mention that “-kernel option loads a binary file (usually a Linux kernel) inside the system memory starting at address 0x00010000.”
Where do you find out the “start address” 0x00010000 ? Is This address vary based on different target board? Thank a lot!!
b.t.w. It’s a great article, I learn a lot!!
Balau
2011/12/22
I don’t think it is documented anywhere, first I found the address by launching QEMU with the “-s -S” options and then attaching with GDB for ARM, and debugging step-by-step it shows that the kernel starts at that address. Then I found it in the source code, looking in “hw/arm_boot.c”, where the constant KERNEL_LOAD_ADDR is set to 0x00010000 and used as an offset from a certain value called “loader_start” which can be overridden by the board settings. Searching for KERNEL_LOAD_ADDR in the source code, it shows that there are many different ones, so the answer to your question is that it depends on the architecture and the board.
DangNguyen
2011/12/23
Hi Ivan Chen!
You can see how kernel is booted in QEMU source code for specific target.
Example:
For realview board (similar for other board)
realview_init (in hw/realview.c) -> arm_load_kernel (in arm_boot.c)
In arm_load_kernel you find the information you want.
Ivan Chen
2012/01/04
Hi Balau and DangNguyen
Thank you all advices! I’m tracing stellaris.c (Stellaris LM3S811EVB with Cortex-M3)
This board is the most approach of Arduino Uno I think, but it doesn’t execute arm_load_kernel() and it cannot run ELF file in the board. Why? Maybe this question is corelate with arm_boot.c or just my misunderstanding, maybe I need follow arm_load_kernel !
Recently, I have another question,I use Arduino IDE to get the elf file and .hex file.
The hex file startup address seem to be 0x10000000, so for arduino, is the KERNEL_LOAD_ADDR equal to 0x10000000? and does anyone know arduino hex file format? Thanks!
I think I need to read Balau’s all articles first! It’s really useful.
Would you mind I refer your articles in my blog? thanks again.
Balau
2012/01/04
Watch out, the stellaris you are emulating and the Arduino are very different architectures. For example Arduino is 8-bit while Cortex-M3 is 32-bit. The programs you compile for one ate absolutely not compatible with the other.
Moreover, Cortex-M3 can’t run Linux, so maybe the way to treat kernel loading is different.
Ivan Chen
2012/01/04
Yeah,I know what you mean. I just think it’s much similar to arduino compare to all target that QEMU can emulate (Although it’s still very different),so I just follow it’s work to build arduino prototype as target in QEMU.
My preliminary goal is to eat ELF file, extract text/data section. In my observation, the load_elf() is also work on arduino ELF file (Base on QEMU insert to rom struct data rom.name/rom.data/rom.size is match ELF content).
However, current status is that QEMU will show me the error message “qemu: fatal: Trying to execute code outside RAM or ROM at 0x01296fd4” which only happen in get_page_addr_code() (exec-all.h) ,so now I am checking where is the problem!
Do you have any advices for porting arduino to QEMU?
Thank your comment again!
Balau
2012/01/04
That error seems to me that should happen during guest execution, so you probably are already over the load_elf part.
I’m not sure that ELF files for the AVR architecture can be recognized by QEMU, but I’m not so familiar with QEMU internals nor with ELF formats.
I think that your preliminary goal is an optional feature of the emulator, that you can do without by using binary files directly.
Binary files can be created with the “objcopy” command of the toolchain you are using (like avr-objcopy or arm-none-eabi-objcopy).
A useful trick: If you run QEMU adding the following options: “
-d in_asm,cpu -D qemu.log -singlestep
“, it will dump a log file (qemu.log) containing state of the CPU and the instructions during guest execution.Anyway, I think QEMU developers are the ones that you should seek advices from, I don’t think I am the right person.
Ivan Chen
2012/01/04
Thank you! I will try your way to find out solution. 🙂
Have a nice day
fisat
2012/01/10
hi balau,
can we edit qemu’s vl.c to establish a client-server communication through qemu interface?
please reply.
chandan
2012/02/10
getting error while linking .c and .s files
./CodeSourcery/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi-ld -T test1.ld test.c startup.o -o test1.elf
./CodeSourcery/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi-ld:test.c: file format not recognized; treating as linker script
./CodeSourcery/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi-ld:test.c:1: syntax error
Balau
2012/02/10
You are passing test.c instead of test.o.
The linker can’t link .c and .s source files, but only .o object files and object libraries.
chandan
2012/02/10
Thanks for your quick reply
But I got a new error (using lucid lynx)
I copied and pasted same code as yours.
root@ubuntu:~# ./CodeSourcery/Sourcery_G++_Lite/bin/arm-none-linux-gnueabi-ld -T test1.ld test1.o startup1.o -o test1.elf
startup1.o: In function `_Reset’:
/root/startup1.s:3: multiple definition of `_Reset’
startup.o:/root/./startup.s:4: first defined here
test1.o:(.ARM.exidx+0x0): undefined reference to `__aeabi_unwind_cpp_pr0′
test1.o:(.ARM.exidx+0x8): undefined reference to `__aeabi_unwind_cpp_pr1′
Balau
2012/02/10
The first error (“multiple definition of _Reset”) should be because the linker script says to use “startup.o” but in the command line you are passing “startup1.o” so it links both and gives the error.
About the “undefined reference…” errors, you can define empty functions such as:
void __aeabi_unwind_cpp_pr0 (void) {}
void __aeabi_unwind_cpp_pr1 (void) {}
These functions should not be important for you, I think they are for exceptions in C++. Also, these functions should not be needed when using the bare metal toolchain (arm-none-gnueabi-ld).
chandan
2012/02/11
Thanks, I did as you said and I got the expected result.
Patrick
2012/02/17
Thanks Balau.
I’m starting a kernel project for an arm board and this is exactly what I was looking for.
krokoziabla
2012/02/24
Hello Balau.
I have a question about interrupts. You said interrupts were disabled at start-up. How can they be turned on?
I’m endevouring to run ThreadX on VersatilePB which you described. I’ve managed to start it and make threads print log info to UART0. The next step I wanted to take is to make the guest system get some info from the external world. I learned that UART0 sat at 12 pin of the Primary Interrupt Controller. I wrote the needed code but it didn’t work. I set a break point at the interrupt handler. To my surprise the guest OS never had stopped at it. I checked PICIntEnable register value and it equaled 0. Even if I set the twelfth bit to 1, PIC anyway reset it to 0.
ThreadX kernel also failed to enable interrupts.
So do you know how to enable the interrupts?
Balau
2012/02/25
You should enable the interrupt in three places: in UARTIMSC register and in the two ARM Vector Interrupt Controller registers VICINTENABLE and VICVECTCNTL??. You must also set the address of the handler in VICVECTADDR?? (http://infocenter.arm.com/help/topic/com.arm.doc.ddi0181e/DDI0181.pdf). You must substitute the ?? with the correct IRQ number, in your case it should be 12.
krokoziablala
2012/02/26
Sorry if I say something stupid. I’m quite new in ARM architecture.
So you want to say that IRQ for UART0 is a vectored IRQ? I thought that it was a standard IRQ. As far as I understand when PIC sends a standard interrupt request signal to the processor, the processor jumps to the interrupt vector table (at positiion 6, if I recall corectly) and the interrupt handler has to analyse PICIRQStatus register whether it has set bits. For UART0 the handler should check bit 12 and if it is set the handler should call ISR for UART0.
That is at the moment I fail to see the role of VICVECTCNTL12 and VICVECTADDR12 registers in this sequence. Futhermore PICIRQStatus has 32 positions for devices and if I understand correctly there are only 16 vectored interrupts supported.
Where did I make a mistake?
Balau
2012/02/26
I’m sorry but I never actually activated the interrupts on QEMU. I’m answering based on what I read on the guides.
Every IRQ is managed with the Vector Interrupt Controller, but you can use “simple” handling similar to what you describe or “vectored” handling which is more complex. Now that I read better it should be possible to ignore the vectored settings, but I think at least the VICINTENABLE register should be set to the correct value to enable UART interrupt. When I have time I will try what works in QEMU.
See also the VersatilePB user guide.
Steven
2012/03/08
You should claim your copyright … based on the publication date … http://www.linuxforu.com/2011/07/qemu-for-embedded-systems-development-part-2/
Nice set of tutorials you have posted. Congrats!
Steven
Balau
2012/03/08
Thanks for reporting it!
I just sent them a mail requesting their compliance to CC-BY-SA license, which means at least some sort of attribution/link to my posts.
[EDIT] They did comply promptly, thanks again!
ala
2012/03/09
hi balau,
while running an os in qemu we got the log file and we have send the log file from one machine to other by editing qemu source code.how can we do this.???also we need the name of structure used for creating log file in qemu.please help
ala
2012/03/11
hi balau,
while running an os in qemu we got the log file and we have send the log file from one machine to other by editing qemu source code.how can we do this.???also we need the name of structure used for creating log file in qemu.please help
Balau
2012/03/11
I don’t understand well what you are trying to do, but QEMU uses a global variable called “FILE * logfile” and helper macros such as “qemu_log_vprintf” that are present in qemu-log.h in order to print information in the log. I hope that’s what you are searching for.
ala
2012/03/12
hi balau,
how can we print the contends of log file in the terminal while running an os.
what will be the parameters of qemu_log_vprintf ?
Balau
2012/03/12
If you run qemu with “-D /dev/stdout” it will print the log in the terminal.
The parameters of qemu_log_vprintf are the same as the standard vprintf.
The parameters of qemu_log are the same as the standard fprintf.
The parameters of qemu_log_mask is first a bit mask (they are found in cpu-all.h as CPU_LOG_* macros) then the same as the standard fprintf.
fisat
2012/03/13
hai balau,
we need to print the log file in the terminal when we run qemu. how we do this? do we need to edit vl.c?
Balau
2012/03/13
I already answered that to your colleague, but I have the feeling that I did not understand the question.
I read the question as “How do we print QEMU log file (the one displaying guest execution information) on the host terminal that launched QEMU?”.
And I answered that you just need the “-D /dev/stdout” option, so you don’t need to edit “vl.c” or anything.
I believe after all this time you two should revise the meaning of your project.
You told me some time ago that you needed client-server communication between two QEMU guests.
– What is the channel of communication? The obvious options are networking and serial ports. Networking allows two guest to communicate using (for example) the “tap” networking on the same virtual LAN. There’s plenty of examples on the Internet. With serial communication for example you could use “-serial tcp::4444,server” launching the first guest and “-serial tcp:127.0.0.1:4444” launching the second guest.
– Why are you not considering the obvious options?
– Why do you need to modify QEMU source?
– What architecture do you have to use? At the beginning you showed me that you were trying to use qemu-i386, then you try to use ARM cross compilers…
– Do you need bare metal client-server or do you need them to run on top of an OS?
– Why do you need QEMU at all?
You should not answer these questions to me, you should just be aware of the answers yourselves, and if you don’t know the answers then you are surely wasting your time on technical tasks that are not useful towards your final goal.
ala
2012/03/13
hi,
when we run qemu by just typing “qemu” in the terminal, normally we got a window and it jst display “no bootable device”. but our project is we need to perform client server communication ie. send a string from client to server when we run qemu in two terminal by simply type “qemu” in that terminals. for that we add client pgm into one qemu(add to vl.c) and server program to another qemu(add to vl.c) and we got the output. now we need to send the machine instructions from client qemu to server qemu that is generated when we run any os in qemu. we store that instructions into a log file by ” log out_asm” we got a qemu.log file that having those machine instructions.. but our project head told, we need to find the structure in qemu source that generate the log file and by using that structure we need to send those instruction from client qemu to server qemu. thats why i ask do we need to modify qemu source? which portion of the qemu source code perform the log file generation? i hope now you understand our question… 😦
Balau
2012/03/13
I think you have two options:
– modify the qemu-log.h macros so that they call your client functions, and all log output is sent to your server instead of being written into a file.
– if you just need the out_asm output, it seems to me that there is only one place where this output is logged, and you can find it by searching for CPU_LOG_TB_OUT_ASM inside translate-all.c file. You can add your client code only there.
I still don’t understand the purpose of the “server”, though.
Balau
2012/03/24
@krokoziablala: I think I got it. You have to also enable interrupts in the CPSR (current program status register) in the ARM core. To enable IRQs you have to clear bit 7 of the special register.
See this: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3540.html
So, the steps are three:
– enable IRQs in the CPSR
– enable interrupts in the UART registers (IMSC)
– enable IRQ 12 in INTENABLE register of the interrupt controller.
nmr
2012/04/10
hi balau,
while linking object files test.o and startup.o using the command:-
” $ arm-none-eabi-ld -T test.ld test.o startup.o -o test.elf ” it shows an ERROR
Error:- “cannot find startup.o”.
plz help me……
thanks,
nmr
Balau
2012/04/10
– can you find startup.o in the same directory where you run the command?
– did the compiler print some errors when you compiled startup.o?
nmr
2012/04/11
hai,
thanks for your reply…
When I have included the compiler path to $PATH the error has been fixed…….
Now, while running “qemu-system-arm” there is no output displaying and no errors but qemu is up showing : “VNC server is running 127.0.0.1:5900”
command:
./qemu-system-arm -M versatilepb -nographic -kernel hello.bin
thanks.
nmr
Balau
2012/04/11
I have this error when QEMU has not been compiled with graphic support. Have you compiled QEMU or did you install it from your distribution package manager?
If you have compiled QEMU, be aware that it needs SDL libraries installed. When you ./configure you can check if QEMU can find them by looking at the results of the automatic checks.
Otherwise, maybe there’s a problem with your graphic setup, if you run “xterm” and a window opens then you know there’s no problem.
tlx
2012/04/15
hi, Balau
I am wrire some code to out something on screen in graphic mode ARM qemu
http://kliga.ru/files/linux-2.6.32.3_patched_by_kliga.ru.zip
Tell me if you interested
Use to run:
qemu-system-arm -M versatilepb -m 128M -kernel zImage -vga cirrus
p.s. sorry my english
nmr
2012/04/17
hi,
thanks for your reply…..
these files are working safely while compiling for arm926ej-s.
i have to compile it for cortex-a9 processor
can I use these same startup.s code and linker.ld for cortex-a9 processor?what are the modifications to be done ?
looking forward for your reply…
thanks
nmr
Balau
2012/04/17
I never tried it, but it seems it could work.
Be aware that in my example I didn’t put a vector table for exceptions, see this link for more information on what to put at the beginning in real programs:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471f/CHDEFDJG.html
Steven
2012/04/17
Hi,
I don’t think you can use the same startup code as in the example, with a Cortex type ARM cpu. The startup behaviour of a Cortex, as well as the different CPU modes, are completely different for a Cortex.
If you want to start working with a Cortex type ARM cpu (and come from a non Cortex background), I highly recommend reading “The Definitive Guide to the ARM Cortex-M3” by Joseph Yiu. It contains a concise description of the differences and how to create startup code. I believe it contains GNU tool examples as well.
Kind regards,
Steven
nmr
2012/04/17
hi balau,
-i have created “test.bin” for cortex a9 processor.
-but while running with “qemu-system-arm” qemu is up but not showing any output.
-qemu have also support for cortex a9 processor and its board
-plz help me
thanks
nmr
Balau
2012/04/17
@nmr
I suggest one of two things:
– running it with “-d in_asm,cpu -D ./qemu.log -singlestep” options, to make it generate a (big) log that can help you trace what happens
– running it with “-s -S” options and attaching with the GDB of the toolchain you are using, using “target remote localhost:1234″ and debugging with that
Be aware that if you use a different machine emulation (-M option) the memory map also changes, so the UART may not be at the same address and you don’t see any output.
Balau
2012/04/17
@Steven
I am under the impression that Cortex-M are different from Cortex-A in matters of exception tables:
(source: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471f/CHDEFDJG.html)
That said, I never worked on Cortex-A processors.
Steven
2012/04/17
Indeed, I stand corrected. A useful article is the following http://stackoverflow.com/questions/6139952/what-is-the-booting-process-for-arm where a clear difference is made between ‘Classic and cortex A/R’ and ‘Cortex-M’.
Regards,
Steven
Balau
2012/04/17
Thanks for the useful links!
nmr
2012/04/19
hi balau,
-the linker.ld and startup.s code for cortex-a8 worked for cortex-a9 as well………..
-one of the machine emulation is not listing while giving the command
qemu-system-arm -M ?
-but the same is listed when i use
./qemu-system-arm -M ?
and there is no difference in other machine emulations listed…..
any idea?
regards,
nithin
Balau
2012/04/19
In my opinion you are calling two different qemu-system-arm programs.
The first is the installed version, the second is the compiled version, which may emulate different machines.
If you run “which qemu-system-arm” it should give you the path of the installed version.
nmr
2012/04/23
hi Balau,
I have a machine emulation in which the UART is modeled by “cadence_uart”.
So what are the modifications to be done in init.c file given.
other than uart address….
plz do reply
regards,
nmr
Balau
2012/04/23
I’m sorry but I don’t know Cadence UART, and from what I see the User’s Guide can’t be downloaded without buying the IP.
The cadence_uart peripheral is not present in mainline QEMU, so you could be using a fork.
This could be the code in your version of QEMU: http://lists.gnu.org/archive/html/qemu-devel/2012-01/msg02836.html
From the code I seem to understand that the register at offset 0x30 is used to read and write data, so you should add 0x30 to the base address of the UART in order to have the same effect of the UART0DR register in my init.c example.
nmr
2012/04/24
Ok Balau,
Thanks for your reply.
Regards
nmr
nmr
2012/05/18
Hi Balau,
how do I read data from UART port in Bare metal application.
Is there any built in function.
Regards,
Nithin
Balau
2012/05/18
There’s no built-in function. The way to read data from UART depends on the type of UART.
For ARM PL011 serial ports, you can do as I did in this post.
For other serial ports, you need to have the manual.
For Cadence UART I already said all I can say.
Rich
2012/08/23
Hi Balau,
Nice post, still useful years later. I am trying to do something similar to what you’ve done here, but with Cortex A9. I realize the VM included for Cortex A9 (realview-pbx-a9) likely has a different memory map than versatilepb. My question is HOW do you find out this kind of information (memory map and system architecture) about the machine? QEMU official website is very out of date, the links to where I think this info would be are broken.
Thanks,
Rich
Balau
2012/08/23
I search for machine information in the machine site, in this case on ARM infocenter: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0440b/index.html
QEMU usually does not invent anything about the architecture, it simply tries to follow the specifications and the behavior of the machine it is trying to emulate, for this reason you won’t usually find these kind of information on QEMU website.
If I have doubts about something even after reading the docs, I look at QEMU source code, in this case the file “hw/realview.c” for the board or “hw/pl011.c” for the UART.
Sudheendra
2012/09/24
what procedure do i have to follow on windows machine for doing the above
Balau
2012/09/24
Answered here on your other comment.
Nur Hussein
2012/11/07
Hi it’s me again. Thanks for the informative articles. I too am trying to boot a Cortex VM (vexpress-a9), and I tried to just recompile the code for that platform, and obviously it didn’t work. I am guessing it’s because of a different UART address? What else do I need to look up. The interrupt vectors seem to be the same.
Balau
2012/11/07
Yes the problem is most probably that the start address of the registers is different.
You could check if the UART is still a PrimeCell PL011, but it should be because the board is made by ARM.
The interrupts should not be involved if you are porting the example in this post, because I am simply writing the data register.
Nur Hussein
2012/11/15
Hi, it’s me again! I successfully got your code to work on a vexpress-a9 qemu system by changing the UART address to 0x10009000. So I thought the next step I’d try was to get some baremetal code running on an actual board. I have a Pandaboard ES, and I put U-boot as the bootloader on it, and tried to boot this example (by changing the UART address to what I think is the Pandaboard ES’s : 0x4806a000 (probably wrong, I don’t know). However it’s not working and I wonder if real hardware is more complicated to work with? Can you give me some hints on how to proceed?
Balau
2012/11/16
Pandaboard ES core is an OMAP4460, so the first thing to do is download its Technical Reference Manual, even if it’s very large PDF. In particular take a look at section “23.3.5.1 UART Programming Model”.
I took a look at it and it’s clear that the UART is very different from the one I mention in my post, it seems more complex but the concepts should be similar. Watch out for baud rate and hardware flow control on both sides of the serial cable.
If you can’t write a program with the manual, anything you google on OMAP4460 should work with the Pandaboard ES.
Also, make sure that your program is really executed by U-Boot.
I don’t have experience on OMAP or Pandaboard, so I can’t help you more than this.
balaji
2013/01/18
Thanks for quick response. I have to test the different exceptions on my soc. I am able to handle the exceptions but not going back to previous state. I analysed exception’s handlers code present in start.S file of u-boot-1.1.6. For restoring the previous state the code is written
such that resetting the cpu only. Please do needful. How to come out from this
Balau
2013/01/18
I don’t know, it depends on the architecture of the SoC.
Most of the information is present on infocenter.arm.com, in the documents of the specific core, but some information must be taken from the SoC architecture. For example a bus error might need that the core resets a peripheral.
Craig
2013/05/05
I had some issues with linking to c code. First, I’d get a two errors like this when linking with arm*ld:
undefined reference to `__aeabi_unwind_cpp_pr0′
I switched to linking with arm*gcc, and then got these:
elf-init.c:(.text+0x5c): undefined reference to `__init_array_start’
elf-init.c:(.text+0x60): undefined reference to `__init_array_end’
Using the output from arm-*-ld –verbose, I modified the linker script to look like:
======================================
OUTPUT_ARCH(arm)
ENTRY(main)
SEARCH_DIR(“=/usr/local/lib”); SEARCH_DIR(“=/lib”); SEARCH_DIR(“=/usr/lib”);
SECTIONS
{
. = 0x10000;
.startup . : { startup.o(.text) }
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss COMMON) }
. = ALIGN(8);
. = . + 0x1000;
stack_top = .;
.rel.plt :
{
*(.rel.plt)
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
}
}
=====================================
Fixing the unresolved references, and everything seems to be working happily now 🙂
Balau
2013/05/06
Thanks for the information!
You could stick with arm*ld linking if you create an empty __aeabi_unwind_cpp_pr0 definition.
That function is only used in exception handling, and if you don’t plan to manage them (c++ or div/0) then there’s no need to implement it correctly.
See:
http://comments.gmane.org/gmane.linux.linaro.toolchain/1052
http://lists.linaro.org/pipermail/linaro-toolchain/2011-April/001098.html
minghuasweblog
2013/05/27
Reblogged this on minghuasweblog and commented:
Really great and very detailed series of artistic work on ARM and emulation.
John Bradley
2013/11/11
Has anyone got this to work on Windows? The trace seems to say it is working but I get no output
Karibe
2014/02/07
I tried in Ubuntu for cortex-m3, qemu only worked consistently when -m 512M was used, quite some memory there…
Balau
2014/02/09
Cortex-M cores have a completely different startup respect to the one I showed here.
See ARM infocenter and Flashing the STM32-P152 board.
The fact that it works “consistently” with that option is probably just a coincidence, you could try to debug it using QEMU “
-s -S
” options and attaching to it witharm-none-eabi-gdb
or whatever you are using.Stefano
2014/03/07
Thank you very much for your posts.
I have downloaded your examples from GitHub and excuting the “qemu-bare-hello” make I have found the following error:
arm-none-eabi-gcc -mcpu=arm926ej-s -g -T test.ld test.c startup.o -o test
/home/qstefano/toolchain/gcc-arm-none-eabi-4_7-2013q3/bin/../lib/gcc/arm-none-eabi/4.7.4/../../../../arm-none-eabi/lib/crt0.o: In function
start':
main’(.text+0xec): undefined reference to
to execute seccessfully the example I have changed the linker flags adding -nostartfiles -nostdlib
LDFLAGS = -T $*.ld -nostartfiles -nostdlib
Balau
2014/03/09
Thanks for the suggestion, it probably means that there’s a difference in the behavior of Codesourcery toolchain that I used and GCC Embedded ARM toolchain that you are using. I think that “
-nostdlib
” should be enough because in the manual it says it removes also system startup files.Michael Rupp
2015/05/24
If running Qemu on Windows 7(possibly the same with other versions) you can’t run the example like this.
qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin
If you do you will get an error stating that you
“qemu could not open monitor device ‘stdio’ ”
I solved this by removing the -nographic arg then once the qemu console opens, you will get a blank screen. At this point use the Ctrl-Alt-3. You should then see the output “Hello world!”
I hope this helps others that run across this. Great tutorial, by the way!
Juan Antionio Zuloaga Mellino
2015/08/29
CMSIS CORTEX-M3 Example project for QEMU based on this fantastic guide:
https://github.com/xvan/CMSIS_LM3S
Seth
2016/10/13
Thank you very much for this article, it was a great starting point for what I’m trying to do with a cortex m3 and a netduino2. I had to step through with gdb to see how this chip loaded addresses and to where, but almost everything else was directly applicable and saved me a ton of time, thanks!
But I have run into a weird quirk that has stumped me and stumped some QEMU devs, perhaps you can provide some insight. I can write to UART 1 and 4, but writes to UARTs 2 and 3 disappear. The writes don’t even make it to the QEMU code that handles writes to all UARTs. I put everything here if you would like to run it https://github.com/skintigh/baremetal_netduino2
Balau
2016/10/18
Hi, I can replicate what you see in my qemu-system-arm 2.6.0. The code seems quite fine, except that the USART pointers should be uint32_t to perform 32-bit access instead of 8-bit access. I would suggest building QEMU with debug symbols, run it inside GDB (attached to the host QEMU, not the guest USART program) and break at stm32f2xx_usart_write to see if it reaches there at least, then investigate according to what you see.
Seth
2016/10/19
I’ve already confirmed it does not make it to stm32f2xx_usart_write with the non-working uarts by putting in print statements. I will do what you said and run qemu in gdb to find out what is calling (or is supposed to be calling) that function. Thanks
Francis
2016/10/25
Hello Balau,
Congratulations for this blog! It is really helpful and I hope you continue to develop for many years.
I have a question for you, to emulate a Cortex M4, what do you think which is the best way to start?
Regards,
Francis
Balau
2016/10/30
I would suggest starting with an existing machine that emulates a Cortex-M. I don’t think QEMU already has a machine with Cortex-M4 so you can start with one that has Cortex-M3 such as lm3s6965evb, lm3s811evb or netduino2, and add option
-cpu cortex-m4
to QEMU command line.Seth
2016/10/31
Francis you can also check out my hello world program for a cortex-m3 linked above. The m4 is almost the same chip, I’m working on one of those, too.
Balau thanks again for your help, I found several minor bugs in QEMU that were causing issues with some UARTs and I’m submitting a patch.
Ben
2016/11/16
Hi this is simply amazing — I’m trying my best to port it to qemu’s virt machine as many docs/devs are trying to push for it to be used over what the sources call a 15 year old board. However there is far less doc around on virt.
These are the current changes I have compared to verstailepb:
– using “-march=armv8-a” in the toolchain for lack of a clear SoC like versatilepb
– from https://github.com/qemu/qemu/blob/master/hw/arm/virt.c#L165 adapting UART0DR address to be 0x09000000
– test.ld set to intercept and start working at 0x00000000
Although seeing no issues with the compilation/running qemu, I am not seeing the output on the serial port. Would anyone have any tips please?
David
2016/12/09
Hello Balau,
Thank you! This helped getting me going on emulating an STM32F4xx Cortex-M4 device.
Do you have any advice on how to emulate an external SDRAM accessed via the FMC with qemu?
I’m using the gnu arm eclipse version of qemu and am willing to modify the source if required, but some pointers on where to start would be really helpful!
Best regards,
David
Balau
2016/12/11
Probably it involves adding a peripheral somewhere like
/hw/mem/fmc.c
, and then plug it inside/hw/arm/stm32...c
, but anyway you could try to ask qemu-devel mailing list, they should be able to help you more.Jiahuan Zhang
2017/03/02
Hi Balau,
Have you ever tried to transfer a data structure via the serial port? For example, a string of data and a int for the length of the data?
I am working on the guest/host communication using serial port redirection. Any clue is greatly appreciated!
Best,
Jiahuan
Jiahuan Zhang
2017/03/03
Hi Balau,
I try to apply this bare-metal implementation to vexpress-a9. I used the same test.c, startup.s and test.ld file, but changed the UART0 address in test.c to 0x10009000, which is the UART0 address for vexpress-a9.
Since vexpress-a9 and versatilepb use the same UART, pl011, the program working with versatilepb should also work with vexpress-a9.
However, the testing result is negative. See the output below.
Am I missing anything? what can I do with it?
best,
Jiahuan
qzj@WS052111 MINGW64 ~/qemu_master/3-3-2017
$ ../qemu/build/arm-softmmu/qemu-system-arm.exe -M vexpress-a15 -kernel test_vex.bin -nographic -serial stdio -serial stdio -serial stdio -serial stdio -serial stdio
trying to register pipe
pipe registerd
QEMU 2.8.50 monitor – type ‘help’ for more information
(qemu) dsound: Could not initialize DirectSoundCapture
dsound: Reason: No sound driver is available for use, or the given GUID is not a valid DirectSound device ID
C:\msys64\home\qzj\qemu_master\qemu\build\arm-softmmu\qemu-system-arm.exe: Trying to execute code outside RAM or ROM at 0x04000000
This usually means one of the following happened:
(1) You told QEMU to execute a kernel for the wrong machine type, and it crashed on startup (eg trying to run a raspberry pi kernel on a versatilepb QEMU machine)
(2) You didn’t give QEMU a kernel or BIOS filename at all, and QEMU executed a ROM full of no-op instructions until it fell off the end
(3) Your guest kernel has a bug and crashed by jumping off into nowhere
This is almost always one of the first two, so check your command line and that you are using the right type of kernel for this machine.
If you think option (3) is likely then you can try debugging your guest with the -d debug options; in particular -d guest_errors will cause the log to include a dump of the guest register state at this point.
Execution cannot continue; stopping here.
Balau
2017/03/05
About the QEMU error you have, it’s probably because the address of the RAM could be different in the two machines, so it’s not just the UART address but also the linker script that should be checked.
About the transfer a data structure via the serial port, this is a very broad subject that could be found in most communications channels, even TCP/UDP protocols. It’s also not the topic of this blog post.
jiahuanzhang
2017/03/06
Hi Balau,
Many thanks for your reply!
Following your advice, I checked the qemu codes for versatilepb and vexpress, and found versatilepb starts RAM at 0x0 and vexpress-a9 starts RAM at 0x60000000. So I modified your test.ld as follows: (adding ” .=0x6000000;” where data should start)
ENTRY(_Reset)
SECTIONS
{
. = 0x10000;
.startup . : { startup.o(.text) }
.text : { *(.text) }
. = 0x60000000;
.data : { *(.data) }
.bss : { *(.bss COMMON) }
. = ALIGN(8);
. = . + 0x1000; /* 4kB of stack memory */
stack_top = .;
}
Assigning UART0 to 0x10009000 and compiling the program with -mcpu=cortex-a9, however, the generated test.bin is not able to run with vexpress-a9, no ” Hello world!” gets printed.
Am I doing anything obviously wrong?
Jiahuan Zhang
2017/03/09
Hi Balau,
It’s me again. I just give up the bare-metal implementation for vexpress.
Here I have a question about this implemenation. Why you enforces 32-bits read and write access for the UART? Because in this case, each time UART transfers one char. What I understand about the casting for a pointer ,”UART0DR = (unsigned int)(s); “, is to claim the data of size of an int from by the pointer and let the UART send an int-size data each time. And an int is four char, so why does not the UART transfer four char at a time?
Is this because qemu emulate the UART data register of uint32_t?
oxuf
2017/11/15
Hi Jiahuan Zhang,
The linker script should use 0x60010000 (not 0x60000000) instead of 0x1000 for vexpress. So, the linker script is the same as the original, but with that change only. I tried it and it works for me.
Alex
2018/12/08
Hi! I ran Hello World on QEMU Cortex-M3 (stellaris)
File test.c:
static volatile unsigned int * const UART_DR = (unsigned int *)0x4000c000;
static void uart_print(const char s) {
while (s != ‘\0’) {
*UART_DR = *s;
s++;
}
}
void c_entry(void) {
uart_print(“Hello, World!\n”);
while (1)
;
}
File startup.S:
.type start, %function
.word stack_top
.word start
.global start
start:
ldr r1, =c_entry
bx r1
File test.ld:
SECTIONS
{
. = 0x0;
.text : {
startup.o(.text)
test.o(.text)
}
. = 0x20000000;
.data : { *(.data) }
.bss : { *(.bss) }
. = ALIGN(8);
. = . + 0x1000;
stack_top = .;
}
Then compile with:
#!/bin/sh
arm-none-eabi-as -c -mthumb -mlittle-endian -march=armv7-m -mcpu=cortex-m3 startup.S -o startup.o
arm-none-eabi-gcc -c -mthumb -ffreestanding -mlittle-endian -march=armv7-m -mcpu=cortex-m3 test.c -o test.o
arm-none-eabi-ld -T test.ld test.o startup.o -o test.elf
Run:
$ qemu-system-arm -M lm3s811evb -kernel test.elf -nographic
Hello, World!
I also ported and ran POSIX-compatible RTOS Embox on this platform – https://github.com/embox/embox