Being new to binutils and related tools, a quick bare-metal debugging-related question: how to modify the ‘link.ld’ script in bzt’s tutorials in order to contiguously include all debugging info (i.e. dwarf .debug_* sections) and how would e.g. a running program later discover its own debug info memory starting address and its size?bzt wrote: ↑Wed Oct 31, 2018 11:16 pm3. most complicated, but most promosing for fully featured IDE itegration is to compile a gdb remote stub into you kernel. In theory gdbserver has a patch for AArch64, but honestly I haven't tried that. There's also a (not very helpful) description on ARM info center on how to use JTAG in virtual ethernet/tty mode with gdb.
Add "-g" to CFLAGS in Makefile. Then load "kernel8.elf" into gdb while running "kernel8.img" as usual. I think that's all (the elf executable already contains the starting address and size for each segment, and "-g" adds dwarf sections and symbol translations to it). You can add a new segment in link.ld for debug info, but shouldn't be needed. Normally they are just appended to the text segment. If memory serves gdb may need some gnu specific sections though (like gnu hash I think), so you should remove (.gnu*) from the DISCARD rule just to be on the safe side.pxlnpx wrote: ↑Sat Feb 02, 2019 7:07 pmBeing new to binutils and related tools, a quick bare-metal debugging-related question: how to modify the ‘link.ld’ script in bzt’s tutorials in order to contiguously include all debugging info (i.e. dwarf .debug_* sections) and how would e.g. a running program later discover its own debug info memory starting address and its size?
Thanks for this hint; I just added
Code: Select all
.debug_info 0 : {
__debug_info_start = .;
*(.debug_info)
__debug_info_end = .;
}
__debug_info_size = SIZEOF(.debug_info);
Code: Select all
objcopy -O binary kernel8.elf kernel8.img
This shouldn't be a problem. You run the .img, true, but you load the .elf (with all the sections) into gdb. Therefore gdb will be able to read the debug information and the symbols even though the running .img doesn't have them.
Thanks for this hint, it looks promising.
Code: Select all
.text : {
*(.debug_info)
}
Code: Select all
.section ".text.boot"
.global _start
_start:
// read cpu id, stop slave cores
mrs x1, mpidr_el1
and x1, x1, #3
// cpu id > 0, stop
cbnz x1, 1f
// set stack before our code
adr x1, _start
msr sp_el1, x1
// enable AArch64 in EL1
mov x2, #(1 << 31) // AArch64
orr x2, x2, #(1 << 1) // SWIO hardwired on Pi3
msr hcr_el2, x2
mrs x2, hcr_el2
// Setup SCTLR access
mov x2, #0x0800
#if (defined __BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
movk x2, #0x33d0, lsl #16
#else
movk x2, #0x30d0, lsl #16
#endif
msr sctlr_el1, x2
// change execution level to EL1
mov x2, #0x3c4 // PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h
msr spsr_el2, x2
adr x2, 5f
msr elr_el2, x2
eret
5: mov sp, x1
// clear bss
ldr x1, =__bss_start
ldr w2, =__bss_size
3: cbz w2, 4f
str xzr, [x1], #8
sub w2, w2, #1
cbnz w2, 3b
// jump to C code, should not return
4: bl main
// for failsafe, halt this core too
1: wfe
b 1b
Code: Select all
#include "gpio.h"
/* Auxilary mini UART registers */
#define AUX_ENABLE ((volatile unsigned int*)(MMIO_BASE+0x00215004))
#define AUX_MU_IO ((volatile unsigned int*)(MMIO_BASE+0x00215040))
#define AUX_MU_IER ((volatile unsigned int*)(MMIO_BASE+0x00215044))
#define AUX_MU_IIR ((volatile unsigned int*)(MMIO_BASE+0x00215048))
#define AUX_MU_LCR ((volatile unsigned int*)(MMIO_BASE+0x0021504C))
#define AUX_MU_MCR ((volatile unsigned int*)(MMIO_BASE+0x00215050))
#define AUX_MU_LSR ((volatile unsigned int*)(MMIO_BASE+0x00215054))
#define AUX_MU_MSR ((volatile unsigned int*)(MMIO_BASE+0x00215058))
#define AUX_MU_SCRATCH ((volatile unsigned int*)(MMIO_BASE+0x0021505C))
#define AUX_MU_CNTL ((volatile unsigned int*)(MMIO_BASE+0x00215060))
#define AUX_MU_STAT ((volatile unsigned int*)(MMIO_BASE+0x00215064))
#define AUX_MU_BAUD ((volatile unsigned int*)(MMIO_BASE+0x00215068))
unsigned int get32le (volatile unsigned int *p)
{
#if (defined __BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
return __builtin_bswap32 (*p);
#else
return (*p);
#endif
}
void put32le (volatile unsigned int *p, unsigned int i)
{
#if (defined __BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
(*p) = __builtin_bswap32 (i);
#else
(*p) = i;
#endif
}
/**
* Set baud rate and characteristics (115200 8N1) and map to GPIO
*/
void uart_init (void)
{
register unsigned int r;
/* initialize UART */
put32le (AUX_ENABLE, get32le (AUX_ENABLE) | 1); // enable UART1, AUX mini uart
put32le (AUX_MU_CNTL, 0);
put32le (AUX_MU_LCR, 3); // 8 bits
put32le (AUX_MU_MCR, 0);
put32le (AUX_MU_IER, 0);
put32le (AUX_MU_IIR, 0xC6); // disable interrupts
put32le (AUX_MU_BAUD, 270); // 115200 baud
/* map UART1 to GPIO pins */
r = get32le (GPFSEL1);
r &= ~((7<<12) | (7<<15)); // gpio14, gpio15
r |= ((2<<12) | (2<<15)); // alt5
put32le (GPFSEL1, r);
put32le (GPPUD, 0); // enable pins 14 and 15
r = 150; while (r--) { asm volatile ("nop"); }
put32le (GPPUDCLK0, (1<<14) | (1<<15));
r = 150; while (r--) { asm volatile ("nop"); }
put32le (GPPUDCLK0, 0); // flush GPIO setup
put32le (AUX_MU_CNTL, 3); // enable Tx, Rx
}
/**
* Send a character
*/
void uart_send (unsigned int c)
{
/* wait until we can send */
do { asm volatile ("nop"); } while (!(get32le (AUX_MU_LSR) & 0x20));
/* write the character to the buffer */
put32le (AUX_MU_IO, c);
}
/**
* Receive a character
*/
char uart_getc (void)
{
char r;
/* wait until something is in the buffer */
do { asm volatile ("nop"); } while (!(get32le (AUX_MU_LSR) & 0x01));
/* read it and return */
r = (char)(get32le (AUX_MU_IO));
/* convert carrige return to newline */
return r == '\r' ? '\n' : r;
}
/**
* Display a string
*/
void uart_puts (char *s)
{
while (*s) {
/* convert newline to carrige return + newline */
if (*s == '\n')
uart_send ('\r');
uart_send (*s++);
}
}
Code: Select all
gcc -ffreestanding -nostdinc -nostdlib -nostartfiles -mbig-endian -o kernel8.elf -T link.ld start.S main.c uart.c
objcopy -O binary kernel8.elf kernel8.img
I'm sorry, I can't give you cut out instructions here. The best advice I can give you is to try different configurations until the addresses in dwarf info became correct. Having debug info in the elf only and not in the img shouldn't be a problem, so including debug section into text segment is just optional.pxlnpx wrote:the dwarf info unfortunately gets "corrupted"
Yes, and you have already done thatpxlnpx wrote:In the process of studying the arm processor by following bzt's tutorials an interesting question emerged: can raspberrypi be deployed in a big-endian mode?
Probably. My goal was to create easily distinguishable blocks for education, and not optimalization. Although _start is quite small, and only executed once during boot, so optimization doesn't worth it imho.pxlnpx wrote:There are still few open questions:
- could start.S be reduced in the sense of fewer assembler instructions?
Probably. The first one sets the stack for exception handlers (while running in EL2), the second one sets the current sp (running in EL1). There's a good chance you never start your kernel at EL1, therefore the eret is always executed, and sp is always loaded from sp_el1.pxlnpx wrote:[*] what is the purpose of "msr sp_el1, x1", or why do we need "mov sp, x1"?
Yes. Simplicity was my goal. For a full-blown implementation, take a look at my bootloader's boot.S. It sets up all cores (EL1, virtual mappings, etc.), loads an ELF from an initrd, maps it in higher half (-2M) and starts executing it on all cores. Except for the stack, all cores are intialized equally.pxlnpx wrote:[*] is it correct that cores 1, 2, 3 stay in EL2?
Very much. It's generating 100% CPU usage.pxlnpx wrote:[*] how power-consuming is "1: b 1b"?
No, you should implement an exclusive access mechanism for it. Simpliest is a spinlock, so that only one CPU can write the mailbox MMIO address at any given time. By the way, the same stands for all MMIO addresses. It's not healthy either if more CPUs are trying to write the same UART registers concurrently for example.pxlnpx wrote:[*] the mailbox-interface is certainly not thread-safe? (Just for the case we later invent main0(), ..., main3() for cores 0, ..., 3 to enter, and just want to access mailbox from any of them.)
Don't you worrypxlnpx wrote:Sorry for this big post.![]()
Hi,pxlnpx wrote: ↑Sun Feb 10, 2019 7:54 pmthe LMA and VMA addresses look different, and the dwarf info unfortunately gets "corrupted" too (is different if embedded into a .text section). Most probably the corrupted debug-info from the .text section gets dumped into the baremetal image by "objdump -O binary ..." too (haven't tested though).
Code: Select all
$ qemu-system-aarch64 -s -S -M raspi3 -kernel bootboot.img
Code: Select all
$ aarch64-linux-gnu-gdb
Code: Select all
(gdb) set architecture aarch64
The target architecture is assumed to be aarch64
Code: Select all
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x0000000000000000 in ??()
Code: Select all
(gdb) symbol-file bootboot.elf
Reading symbols from bootboot.elf...done.
Code: Select all
(gdb) display/i $pc
1: x/i $pc
=> 0x0: ldr x0, 0x18
Code: Select all
(gdb) break bootboot_main
Breakpoint 1 at 0x840c0: file bootboot.c, line 1110.
Code: Select all
(gdb) c
Continuing.
Thread 1 hit Breakpoint 1, bootboot_main (hcl=2147483650) at bootboot.c:1110
1110 {
1: x/i $pc
=> 0x840c0 <bootboot_main>: sub sp, sp, #0xa90
No, those books are great sources. I'd suggest to get Tanenbaum's and Bos' book too, it's called Modern Operating Systems 4th edition. It is more general than the Tanenbaum book you mentioned, and instead of listing the Minix C source it has a very detailed analysis on how Minix, Linux and Windows kernels work. That is a very valuable information specially for beginners.NeoFahrenheit wrote: ↑Sat Mar 23, 2019 10:42 pmFeel free if you want to say "that book is crap! Pick another!" or anything.
Well, my question is: Do you think this tutorial is a good place to start? I already know C and C++.
Thank you. My biggest fear is how to make the Pi "see and execute" my code, but I already saw that raspberrypi-os has that info.bzt wrote: ↑Thu Mar 28, 2019 9:39 amNo, those books are great sources. I'd suggest to get Tanenbaum's and Bos' book too, it's called Modern Operating Systems 4th edition. It is more general than the Tanenbaum book you mentioned, and instead of listing the Minix C source it has a very detailed analysis on how Minix, Linux and Windows kernels work. That is a very valuable information specially for beginners.
My tutorial won't teach you how to create an operating system and it doesn't cover theory at all. Instead it's intended to help you out with specific parts, like how to read a file from disk without all the VFS stuff. Or how to get an initrd loaded on the RPi. There are more tutorials like mine, I've linked them in the main README.md check them out. There's one tutorial in particular which focuses on how Linux does things on the RPi, and how a small hobby OS can do things. Unfortunatelly unfinished, but raspberrypi-os is still extremely useful. Also linked on my main README.md.
Welcome to the club and Good luck,
bzt
Not sure what you mean by "see and execute", you simply create an elf, copy the text segment out into kernel.img and save that on the SD card. That's allNeoFahrenheit wrote: ↑Thu Mar 28, 2019 1:16 pmThank you. My biggest fear is how to make the Pi "see and execute" my code, but I already saw that raspberrypi-os has that info.
Now is the study time.
Not particularly better, I'd say different. It does not focus that much on Minix, instead it has more comparitions and more theory. It's on github btw, also here.I guess the 4th Edition of the Tanenbaum's book is better, but my library doens't have it. I'll look elsewhere where I can find it.
The Xinu sources for Pi 1gpk wrote: ↑Sat Apr 06, 2019 10:49 amAnother book that is very good is "Operating System Design, The Xinu Approach" by Douglas Comer. It cover the implementation of a small embedded OS called Xinu. Xinu also runs on Raspberry Pi, there is a port here: https://github.com/LdB-ECM/Xinu (note I had to do some changes to get it to build, there is a PR there).
Looks plenty active to me, recent commits and activity. It is an academic project so you wouldn't expect a weekly commit would you?
I don't think you understand how git works, did you really expect them to respond to your issues when you don't supply any code or a pull request to back it up, it would end up just wasting their time.LdB wrote: ↑Mon Apr 08, 2019 2:19 pmI have 3 pushes been sitting there for 6 months and it has not been updated in a year .. you can see on the issues last 3 are mine
https://github.com/xinu-os/xinu/issues
The most significant is the lan driver on the Pi3B+ which all ethernet use is dead without the lan78xx driver.
That's a bit debatable really, its hard to follow what you've done because you didn't create a fork and seem to replace every file when you do a commit. Your most recent addition, five months ago, was some stuff to do with critical sections which looks like it was written by a total beginner.
Code: Select all
static bool inCrit = true;
static uint32_t intmask = 0;
extern uint32_t disable(void); // In assembler file system/arch/arm/intutils.S
extern void restore(uint32_t mask); // In assembler file system/arch/arm/intutils.S
void ENTER_KERNEL_CRITICAL_SECTION (void)
{
if (inCrit == false) {
kprintf("Aborting .. ENTER_KERNEL_CRITICAL_SECTION called twice without leaving \n");
while (1) {}
}
intmask = disable();
inCrit = true;
}
void EXIT_KERNEL_CRITICAL_SECTION (void)
{
if (inCrit == false) {
kprintf("Aborting .. EXIT_KERNEL_CRITICAL_SECTION called twice without entering \n");
while (1) {}
}
restore(intmask);
}
There might be better options but I don't think your offering is one of them.
There's a big difference between what looks like it works in a basic test and what is usable in real code.
You wrote what you wrote don't make excuses, if you can do better then you should do so, simple as that.
I'm happy to end any discussion with you, but make sure the advice you freely hand out is accurate, or you might get another reminder.