umanovskis / baremetal-arm Goto Github PK
View Code? Open in Web Editor NEWAn ebook about bare-metal programming for ARM
License: Other
An ebook about bare-metal programming for ARM
License: Other
Following the instructions in Chapter 03 I get the following error & QEmu exits/quits:
On: Debian GNU/Linux 11 x86_64
using latest U-Boot source:
U-Boot 2023.01-rc2-00072-g27c415ae8b-dirty (Nov 27 2022 - 09:52:08 -0700)
Experiencing the following issue:
66156 bytes read in 28 ms (2.3 MiB/s)
Image Name:
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 66092 Bytes = 64.5 KiB
Load Address: 60000000
Entry Point: 60000000
Verifying Checksum ... OK
Working FDT set to 0
Loading Kernel Image
FDT and ATAGS support not compiled in
resetting ...
According to the PrimeCell UART (PL011) TRM word length bits are [6:5], so I'm pretty sure the offset should be 5 instead of 6
baremetal-arm/src/06_uart/src/uart_pl011.h
Lines 52 to 55 in dcee0f8
Hi:
04_cenv example doesn't work on my qemu env, data abort bug happens, next is the u-boot logs:
## Booting kernel from Legacy Image at 60000000 ...
Image Name:
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 502 Bytes = 502 Bytes
Load Address: 60000000
Entry Point: 60000000
Verifying Checksum ... OK
Loading Kernel Image ... OK
Starting kernel ...
data abort
pc : [<60000080>] lr : [<7ff974f0>]
sp : 70003008 ip : 7fef5976 fp : 00000000
r10: 7fef6a6c r9 : 7fef5ef8 r8 : 00000001
r7 : 00000000 r6 : 60000000 r5 : 7ffd85cc r4 : 00000000
r3 : 7fef5fa8 r2 : 70000008 r1 : 70000000 r0 : 600001ee
Flags: Nzcv IRQs on FIQs on Mode SVC_32
Code: e59f0050 e59f1050 e59f2050 e1510002 (b4903004)
Resetting CPU ...
resetting ...
Next is the asm code disassembly by objdump.
60000070: e59f0050 ldr r0, [pc, #80] ; 600000c8 <Abort_Exception+0x1c>
60000074: e59f1050 ldr r1, [pc, #80] ; 600000cc <Abort_Exception+0x20>
60000078: e59f2050 ldr r2, [pc, #80] ; 600000d0 <Abort_Exception+0x24>
6000007c <data_loop>:
6000007c: e1510002 cmp r1, r2
60000080: b4903004 ldrlt r3, [r0], #4
60000084: b4813004 strlt r3, [r1], #4
60000088: bafffffb blt 6000007c <data_loop>
6000008c: e3a00000 mov r0, #0
60000090: e59f103c ldr r1, [pc, #60] ; 600000d4 <Abort_Exception+0x28>
60000094: e59f203c ldr r2, [pc, #60] ; 600000d8 <Abort_Exception+0x2c>
...
600000c8: 600001ee .word 0x600001ee
600000cc: 70000000 .word 0x70000000
600000d0: 70000008 .word 0x70000008
Seems data abort is trigger by unaligned memory access, _text_end
is not an aligned address.
$ arm-none-eabi-objdump -t cenv.elf | grep text_end
600001ee g .text 00000000 _text_end
Apply this patch to make sure text section aligned, this example can work fine again.
diff --git a/src/04_cenv/linkscript.ld b/src/04_cenv/linkscript.ld
index ab58e41..eb5f558 100644
--- a/src/04_cenv/linkscript.ld
+++ b/src/04_cenv/linkscript.ld
@@ -14,6 +14,7 @@ SECTIONS
startup.o (.vector_table)
*(.text*)
*(.rodata*)
+ . = ALIGN(4);
} > ROM
_text_end = .;
.data : AT(ADDR(.text) + SIZEOF(.text))
Now _text_end
is an aligned address.
$ arm-none-eabi-objdump -t cenv.elf | grep text_end
600001f0 g .text 00000000 _text_end
Hi,
First of all, thanks for creating this project!
Just wanted to point out the trouble when trying to run it with Windows Subsystem for Linux (WSL).
The first boot command
qemu-system-arm -M vexpress-a9 -m 32M -no-reboot -nographic -monitor telnet:127.0.0.1:1234,server,nowait
throws an error
ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5220:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2642:(snd_pcm_open_noupdate) Unknown PCM default
alsa: Could not initialize DAC
alsa: Failed to open `default':
alsa: Reason: No such file or directory
ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4732:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5220:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2642:(snd_pcm_open_noupdate) Unknown PCM default
alsa: Could not initialize DAC
alsa: Failed to open `default':
alsa: Reason: No such file or directory
audio: Failed to create voice `lm4549.out'
From a quick google search I figured that this has something to do with audio drivers (and the lack of for WSL), but it took a while to figure out a quick workaround, so I think it is worth posting it here. Setting this environment variable suppresses the error:
export QEMU_AUDIO_DRV=none
One more thing - that very first boot command does not crash under WSL, just hangs. Not sure why. The second try with first-hang.bin
works as intended.
This script tells the linker that the program's entry point is at the global symbol
_Entry
, which we export fromstartup.s
.
Should it be a _Reset
instead of _Entry
?
Hi,
In Chapter 2, page 15, under Memory mappings section, there appears to be a discrepancy in the memory address at first-hang.bin. Do we inspect memory at address 0x100000 or 0x10000?
Appreciate the work that's been put into this!
Thanks again for all you've done. I run into an issue in chapter 5 while building the project with cmake/make. It appears cmake will create files with a .obj extension instead of .o and can't figure out how this happens. Stack Overflow has not been helpful:
john@embedded:~/Desktop/arm-example$ cmake -S . -Bbuild
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/cc
-- The C compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/john/Desktop/arm-example/build
john@embedded:~/Desktop/arm-example$ cd build
john@embedded:~/Desktop/arm-example/build$ make
Scanning dependencies of target u-boot
[ 0%] Built target u-boot
Scanning dependencies of target bare-metal
[ 33%] Building C object CMakeFiles/bare-metal.dir/src/cstart.c.obj
[ 66%] Building ASM object CMakeFiles/bare-metal.dir/src/startup.s.obj
[100%] Linking C executable bare-metal.elf
/usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/bin/ld: cannot find startup.o
collect2: error: ld returned 1 exit status
CMakeFiles/bare-metal.dir/build.make:112: recipe for target 'bare-metal.elf' failed
make[2]: *** [bare-metal.elf] Error 1
CMakeFiles/Makefile2:131: recipe for target 'CMakeFiles/bare-metal.dir/all' failed
make[1]: *** [CMakeFiles/bare-metal.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
I have tried running this line of code and keep getting the error:
arm-none-eabi-ld:linkscript.ld:0: syntax error
I have this for my linkscript.ld file
ENTRY(_Reset)
MEMORY
{
ROM (rx) : ORIGIN = 0X60000000, LENGTH 1M
RAM (rwx): ORIGIN = 0X70000000, LENGTH 32M
}
SECTIONS
1 .text : {
2 startup.o (.vector_table)
3 *(.text)
4 *(.rodata)
5 } > ROM
6 _text_end = .;
7 .data : AT(ADDR(.text) + SIZEOF(.text))
8 {
9 _data_start = .;
10 *(.data)
11 . = ALIGN(8);
12 _data_end = .;
13 } > RAM
14 .bss : {
15 _bss_start = .;
16 *(.bss)
17 . = ALIGN(8);
18 _bss_end = .;
19 } > RAM
Please what could be the problem? I have searched online and can't find the issue
returns UART_INVALID_ARGUMENT_WORDSIZE in uart_configure.
Hi,
also in doc/03_bootloader.md, there's a note regarding the nbd module.
At least under Debian Stretch, I had some issues with section, and the SD card creation script, since nbd is not loaded by default. Also, simply executing modprobe nbd
didn't solve the problem. Specifically, I had to execute modprobe nbd max_part=16
, and after "connecting" the image with qemu-nbd -c /dev/nbd0 sdcard.img
, I had to execute partprobe /dev/nbd0
so the /dev/nbd0p1
device gets created. (Otherwise I could not execute mkfs.ext2 on it).
I'm not sure if it's worth mentioning this in the note, since this might be fairly Debian-specific, and other Linux distros might behave differently. (Related bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=824553)
I am following up the documents as per mention in https://github.com/umanovskis/baremetal-arm/blob/master/doc/04_cenv.md.
Could you please help where am I doing wrong?
:: Run QEMU by command ::
qemu-system-arm -M vexpress-a9 -m 512M -no-reboot -nographic -monitor telnet:127.0.0.1:1234,server,nowait -kernel ../bootloader/u-boot/u-boot -sd sdcard.img -serial mon:stdio
08_scheduler
doesn't work on my WSL2, no uart message anymore after Welcome to Chapter 8, Scheduling!
.
Version:
I had debug the source code and found ptimer_isr
is never enter, that will make the global variable systimie
always zero. Appending -d guest_errors
to qemu command line params, I got next error messages:
Welcome to Chapter 8, Scheduling!
gic_cpu_read: Bad offset 5
gic_cpu_write: Bad offset 5
gic_cpu_read: Bad offset 6
gic_cpu_write: Bad offset 6
gic_cpu_read: Bad offset 7
gic_cpu_write: Bad offset 7
gic_cpu_read: Bad offset 1
gic_cpu_write: Bad offset 1
gic_cpu_read: Bad offset 2
gic_cpu_write: Bad offset 2
gic_cpu_read: Bad offset 3
gic_cpu_write: Bad offset 3
Invalid read at addr 0x8, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid read at addr 0x208, size 1, region '(null)', reason: rejected
Invalid write at addr 0x8, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid write at addr 0x208, size 1, region '(null)', reason: rejected
Invalid read at addr 0x9, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid read at addr 0x209, size 1, region '(null)', reason: rejected
Invalid write at addr 0x9, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid write at addr 0x209, size 1, region '(null)', reason: rejected
Invalid read at addr 0xA, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid read at addr 0x20A, size 1, region '(null)', reason: rejected
Invalid write at addr 0xA, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid write at addr 0x20A, size 1, region '(null)', reason: rejected
Invalid read at addr 0xB, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid read at addr 0x20B, size 1, region '(null)', reason: rejected
Invalid write at addr 0xB, size 1, region 'arm_mptimer_timer', reason: invalid size (min:4 max:4)
Invalid write at addr 0x20B, size 1, region '(null)', reason: rejected
The private timer registers should be accessed with uint32, but qemu report it was accessed by uint8, let's check the disassemble code:
$ arm-none-eabi-objdump -d -S
...
600004a6 <ptimer_init>:
ptimer_error ptimer_init(uint16_t millisecs) {
600004a6: b538 push {r3, r4, r5, lr}
600004a8: 4604 mov r4, r0
600004aa: ee9f 5f10 mrc 15, 4, r5, cr15, cr0, {0}
regs = (private_timer_registers*)PTIMER_BASE;
600004ae: f505 62c0 add.w r2, r5, #1536 ; 0x600
600004b2: f241 0308 movw r3, #4104 ; 0x1008
600004b6: f2c7 0300 movt r3, #28672 ; 0x7000
600004ba: 601a str r2, [r3, #0]
if (!validate_config(millisecs)) {
600004bc: f7ff ffbc bl 60000438 <validate_config>
600004c0: b908 cbnz r0, 600004c6 <ptimer_init+0x20>
return PTIMER_INVALID_PERIOD;
600004c2: 2001 movs r0, #1
}
600004c4: bd38 pop {r3, r4, r5, pc}
uint32_t load_val = millisecs_to_timer_value(millisecs);
600004c6: 4620 mov r0, r4
600004c8: f7ff ffb8 bl 6000043c <millisecs_to_timer_value>
WRITE32(regs->LR, load_val); /* Load the initial timer value */
600004cc: f8c5 0600 str.w r0, [r5, #1536] ; 0x600
(void)irq_register_isr(PTIMER_INTERRUPT, ptimer_isr);
600004d0: f240 4181 movw r1, #1153 ; 0x481
600004d4: f2c6 0100 movt r1, #24576 ; 0x6000
600004d8: 201d movs r0, #29
600004da: f7ff ff96 bl 6000040a <irq_register_isr>
WRITE32(regs->CTRL, ctrl); /* Configure and start the timer */
600004de: f241 0308 movw r3, #4104 ; 0x1008
600004e2: f2c7 0300 movt r3, #28672 ; 0x7000
600004e6: 681b ldr r3, [r3, #0]
600004e8: 7a1a ldrb r2, [r3, #8]
600004ea: 2000 movs r0, #0
600004ec: 2207 movs r2, #7
600004ee: 721a strb r2, [r3, #8]
600004f0: 7a5a ldrb r2, [r3, #9]
600004f2: 7258 strb r0, [r3, #9]
600004f4: 7a9a ldrb r2, [r3, #10]
600004f6: 7298 strb r0, [r3, #10]
600004f8: 7ada ldrb r2, [r3, #11]
600004fa: 72d8 strb r0, [r3, #11]
return PTIMER_OK;
Yes, it is accessed by uint8. Remove the __attribute__((packed))
from the register declare structure, the gcc will make those register accessed by uint32. Next is a patch:
diff --git a/src/08_scheduler/src/gic.h b/src/08_scheduler/src/gic.h
index 4620c05..cd5ff80 100644
--- a/src/08_scheduler/src/gic.h
+++ b/src/08_scheduler/src/gic.h
@@ -4,7 +4,7 @@
#include <stdint.h>
#include "cpu.h"
-typedef volatile struct __attribute__((packed)) {
+typedef volatile struct {
uint32_t DCTLR; /* 0x0 Distributor Control register */
const uint32_t DTYPER; /* 0x4 Controller type register */
const uint32_t DIIDR; /* 0x8 Implementer identification register */
@@ -26,7 +26,7 @@ typedef volatile struct __attribute__((packed)) {
Don't care about them */
} gic_distributor_registers;
-typedef volatile struct __attribute__((packed)) {
+typedef volatile struct {
uint32_t CCTLR; /* 0x0 CPU Interface control register */
uint32_t CCPMR; /* 0x4 Interrupt priority mask register */
uint32_t CBPR; /* 0x8 Binary point register */
diff --git a/src/08_scheduler/src/linkscript.ld b/src/08_scheduler/src/linkscript.ld
index 4e6586a..b4b9100 100644
--- a/src/08_scheduler/src/linkscript.ld
+++ b/src/08_scheduler/src/linkscript.ld
@@ -14,6 +14,7 @@ SECTIONS
*(.vector_table)
*(.text*)
*(.rodata*)
+ . = ALIGN(4);
} > ROM
_text_end = .;
.data : AT(ADDR(.text) + SIZEOF(.text))
diff --git a/src/08_scheduler/src/ptimer.h b/src/08_scheduler/src/ptimer.h
index 7b3d1f3..05d1957 100644
--- a/src/08_scheduler/src/ptimer.h
+++ b/src/08_scheduler/src/ptimer.h
@@ -4,7 +4,7 @@
#include <stdint.h>
#include "cpu.h"
-typedef volatile struct __attribute__((packed)) {
+typedef volatile struct {
uint32_t LR; /* 0x0 Private timer load register */
uint32_t CR; /* 0x4 Private timer counter regster */
uint32_t CTRL; /* 0x8 Private timer control register */
Disassemble code after apply this patch:
WRITE32(regs->CTRL, ctrl); /* Configure and start the timer */
6000048e: f241 0308 movw r3, #4104 ; 0x1008
60000492: f2c7 0300 movt r3, #28672 ; 0x7000
60000496: 681b ldr r3, [r3, #0]
60000498: 2207 movs r2, #7
6000049a: 609a str r2, [r3, #8]
qemu work fine and no guest errors now:
Starting kernel ...
Welcome to Chapter 8, Scheduling!
Entering task 1... systime 2000
Exiting task 1...
Entering task 1... systime 4000
Exiting task 1...
Entering task 0... systime 5000
Exiting task 0...
Entering task 1... systime 6000
Exiting task 1...
Entering task 1... systime 8000
Exiting task 1...
Entering task 1... systime 10000
Exiting task 1...
Hi there,
First of all I want to thank you for your work and the quality of it. I tried to find exactly this course out there but all what I found was oriented to RasPI o requiring hardware.
Having said that, I want also to report a couple of mistakes I found during my read:
Page 19:
CONFIG_BOOTCOMMAND="run mmc_elf_bootcmd"
Should be:
CONFIG_BOOTCOMMAND=bootcmd_bare_arm
Page 25:
0x600210000
should be:
0x60021000
Edit: Adding some troubleshooting on ArchLinux
When trying to compile the C code in section 4:
$ arm-none-eabi-gcc -c -nostdlib -nostartfiles -lgcc -o cstart.o cstart.c
In file included from cstart.c:1:
/usr/lib/gcc/arm-none-eabi/9.1.0/include/stdint.h:9:16: fatal error: stdint.h: No such file or directory
9 | # include_next <stdint.h>
| ^~~~~~~~~~
compilation terminated.
Solution:
Install arm-none-eabi-newlib
package.
Kind regards,
Ernest
Hi,
in doc/03_bootloader.md, you say to modify the U-Boot config, specifically setting CONFIG_BOOTCOMMAND="run mmc_elf_bootcmd"
, while in the next paragraph, you add a bootcommand / environment variable bootcmd_bare_arm
.
Is this intended? Shouldn't it be CONFIG_BOOTCOMMAND="run bootcmd_bare_arm"
?
Because this way, when I start U-Boot in qemu, I get an error ## Error: "mmc_elf_bootcmd" not defined
, i.e. the autoboot doesn't work
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.