Je suis en train d'écrire un OS jouet pour mon raspberry pi et j'essaie de configurer l'unité de gestion de mémoire (MMU). Je veux diviser la mémoire virtuelle entre 3G:1G, donc je pense que mon code devrait être lié à 0xC0008000, tout en étant chargé à 0x8000 à l'exécution. (Le 0x8000 est l'adresse à laquelle les chargeurs d'amorçage actuels s'attendent à trouver le noyau - puisqu'ils sont conçus pour linux).
Je pense que tout est bien configuré en fouillant avec objdump, mais ça ne fonctionne pas. Après avoir fait quelques tests de débogage avec qemu, je pense que le chargeur d'amorçage ne trouve pas du tout mon code.
Je pense que le problème vient de mon script de liaison, puisque le noyau démarre correctement si je déplace le code de démarrage dans sa propre section qui est à la fois liée et chargée à 0x8000.
J'ai extrait le script et le code minimal. Comme ci-dessous,
$ cat kernel.ld
ENTRY(_start)
SECTIONS
{
/* doit == KERNLINK */
. = 0xC0008000;
.text : AT(0x8000) {
*(.text)
}
.bss : {
*(.bss)
}
.data : {
*(.data)
}
.rodata : {
*(.rodata)
}
}
-
$ cat source/entry.S
#include "mem.h"
.globl _start
_start = V2P(entry)
.globl entry
entry:
loop$:
b loop$
(Le "b loop$" ne fonctionnera pas car il est généré comme "b·c0008000" au lieu d'utiliser un branchement relatif. Mais peu importe cette partie, le problème est qu'il n'atteint jamais l'entrée).
$ cat source/mem.h
#define KERNLOAD 0x8000
#define KERNBASE 0xC0000000
#define KERNLINK (KERNBASE+KERNLOAD)
#define V2P(va) ((va) - KERNBASE)
Ce sont les trois seuls fichiers sources. Rien d'intéressant ne devrait être présent dans le Makefile, mais la sortie de make est la suivante,
$ make
arm-none-eabi-gcc -g -Wall -c -o build/entry.o source/entry.S
arm-none-eabi-ld --no-undefined -T kernel.ld build/entry.o -Map kernel.map -o build/output.elf
arm-none-eabi-objcopy build/output.elf -O binary kernel.img
Et objdump,
$ arm-none-eabi-objdump -h build/output.elf
build/output.elf: format de fichier elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000004 c0008000 00008000 00008000 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .ARM.attributes 00000014 00000000 00000000 00008004 2**0
CONTENTS, READONLY
2 .debug_line 0000003c 00000000 00000000 00008018 2**0
CONTENTS, READONLY, DEBUGGING
3 .debug_info 00000054 00000000 00000000 00008054 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_abbrev 00000014 00000000 00000000 000080a8 2**0
CONTENTS, READONLY, DEBUGGING
5 .debug_aranges 00000020 00000000 00000000 000080c0 2**3
CONTENTS, READONLY, DEBUGGING
Je commence à penser avoir négligé certains détails évidents mais précieux.
\==== mise à jour ====
Comme indiqué dans ma propre réponse ci-dessous, la confusion est causée par le débogage dans qemu. Les points d'arrêt sont définis par des adresses virtuelles. "b entry" ne fonctionne pas, car GDB pense aux adresses virtuelles alors que l'UMM n'a pas été activée et que nous fonctionnons avec des adresses physiques.
Avant d'activer l'UMM, nous devons donc utiliser "b *0x8000". Cela définit un point d'arrêt qui est correctement atteint. GDB semble encore confus cependant, car il ne montre aucune information de débogage (pas de code source, comme 0x00008004 in ?? ()
). Ce n'est pas un gros problème car j'ai la liste produite par "objdump -D".
Après avoir activé l'UMM et sauté vers main, gdb fonctionne parfaitement. Le point crucial est de sauter vers une adresse virtuelle, en utilisant un branchement absolu. b/bl
émettrait des sauts relatifs. Donc j'utilise ldr pc =main
. bx
fonctionne aussi.