Partie 3 : Nous démarrons presque Linux à partir de la carte SD sur RocketChip

Partie 3 : Nous démarrons presque Linux à partir de la carte SD sur RocketChip Dans partie précédente Un contrôleur de mémoire plus ou moins fonctionnel a été réalisé, en fait une enveloppe autour de l'IP Core de Quartus, servant de passerelle vers TileLink. Aujourd'hui, dans la rubrique « Portage de RocketChip sur une carte chinoise peu connue avec Cyclone », vous verrez une console fonctionnelle. Le processus a pris un peu de temps : je pensais lancer rapidement Linux et avancer, mais cela ne s'est pas passé comme prévu. Dans cette partie, je propose de jeter un œil au processus de démarrage de U-Boot, BBL, et aux tentatives timides d'initialisation du noyau Linux. Mais il y a une console — celle de U-Boot, et elle est assez avancée, avec beaucoup de ce que vous attendez d'une console complète.

Au niveau matériel, une carte SD sera ajoutée, connectée via l'interface SPI, ainsi qu'un UART. Dans la partie logicielle, le BootROM sera remplacé par xip sur sdboot et, en fait, les étapes de chargement suivantes seront ajoutées (sur la carte SD).

Aménagement de la partie matérielle

Ainsi, la tâche consiste à passer à un « grand » cœur et à connecter l'UART (de Raspberry) et l'adaptateur SD (une petite carte de Catalex avec six broches : GND, VCC, MISO, MOSI, SCK, CS).

En principe, tout était assez simple. Mais avant d'en avoir conscience, j'ai été un peu ballotté d'un côté à l'autre : après la dernière fois, j'ai décidé qu'il fallait encore simplement mélanger dans Système quelque chose comme HasPeripheryUART (et dans l'implémentation en conséquence), tout comme pour la carte SD — et tout serait prêt. Ensuite, j'ai décidé de voir comment cela était implémenté dans un design « sérieux ». Alors, qu'avons-nous d'important ici ? Arty ne semble pas convenir — il reste le monstre unleahshed.DevKitConfigs. Et soudain, j'ai découvert qu'il y avait des superpositions partout, ajoutées via des paramètres par clés. Je devine que cela doit être très flexible et configurable, mais j'aurais besoin de quelque chose pour commencer à faire fonctionner… N'avez-vous pas quelque chose de similaire, mais plus simple et moins raffiné ? C'est alors que je suis tombé sur vera.iofpga.FPGAChip pour FPGA Microsemi et j'ai tout de suite pris des citations pour essayer de faire ma propre implémentation par analogie, étant donné que presque toute la « plomberie de la carte mère » se trouve dans un seul fichier.

Il s'est avéré qu'il suffisait simplement d'ajouter dans System.scala les lignes

class System(implicit p: Parameters) extends RocketSubsystem
...
  with HasPeripherySPI
  with HasPeripheryUART
...
{
  val tlclock = new FixedClockResource("tlclk", p(DevKitFPGAFrequencyKey))
  ...
}

class SystemModule[+L <: System](_outer: L)
  extends RocketSubsystemModuleImp(_outer)
...
    with HasPeripheryUARTModuleImp
    with HasPeripheryGPIOModuleImp
...

Ligne dans le corps de la classe Système ajoute des informations sur la fréquence à laquelle cette partie de notre SoC fonctionne, dans le fichier dts. D'après ce que je comprends, le DTS/DTB est une sorte d'analogue statique de la technologie plug-and-play pour les appareils embarqués : l'arborescence de description dts est compilée en un fichier binaire dtb et est transmise par le chargeur au noyau, afin qu'il puisse configurer correctement le matériel. Ce qui est intéressant, c'est que sans la ligne avec tlclock , tout synthétise parfaitement, mais il ne sera pas possible de compiler BootROM (rappelez-vous, maintenant ce sera déjà sdboot) — lors du processus de compilation, il analyse le fichier dts et crée un en-tête avec la macro TL_CLK, grâce à laquelle il pourra configurer correctement les diviseurs de fréquence pour les interfaces externes.

Il faudra également corriger un peu le routage :

Platform.scala :

class PlatformIO(implicit val p: Parameters) extends Bundle {

...

  // UART
  io.uart_tx := sys.uart(0).txd
  sys.uart(0).rxd := RegNext(RegNext(io.uart_rx))

  // Carte SD
  io.sd_cs := sys.spi(0).cs(0)
  io.sd_sck := sys.spi(0).sck
  io.sd_mosi := sys.spi(0).dq(0).o
  sys.spi(0).dq(0).i := false.B
  sys.spi(0).dq(1).i := RegNext(RegNext(io.sd_miso))
  sys.spi(0).dq(2).i := false.B
  sys.spi(0).dq(3).i := false.B
}

À vrai dire, les chaînes de registre ont été ajoutées simplement par analogie avec certains autres endroits du code d'origine. Ils doivent probablement protéger contre la métastabilité. Peut-être que dans certaines blocs, il y a déjà une certaine protection, mais au départ, je veux réussir au moins à "un niveau de qualité". La question qui m'intéresse davantage est pourquoi MISO et MOSI sont sur des dq? Ответа я пока так и не нашёл, но, похоже, остальной код рассчитывает именно на такое подключение.

Physiquement, j'ai simplement assigné les sorties de conception aux contacts libres sur la broche et déplacé le jumper de sélection de tension à 3.3V.

Adaptateur SD

Vue de dessus :

Partie 3 : Nous démarrons presque Linux à partir de la carte SD sur RocketChip

Vue de dessous :

Partie 3 : Nous démarrons presque Linux à partir de la carte SD sur RocketChip

Débogage de la partie logicielle : outils

Commençons par parler des outils de débogage disponibles et de leurs limitations.

Minicom

Tout d'abord, nous avons besoin de manière à lire ce que le chargeur et le noyau affichent. Pour cela, sur Linux (dans ce cas, sur ce qui est sur RaspberryPi), nous aurons besoin du programme Minicom. En général, n'importe quel programme fonctionnant avec le port série fera l'affaire.

Veuillez noter qu'au démarrage, le nom de dispositif du port doit être indiqué comme -D /dev/ttyS0 — après l'option -D. Et la principale information : pour quitter, utilisez Ctrl-A, X. J'ai en fait eu un cas où cette combinaison n'a pas fonctionné — dans ce cas, vous pouvez simplement dire à partir d'une session SSH voisine killall -KILL minicom.

Il existe une autre particularité. Sur le Raspberry Pi, il y a deux UART, et les deux ports peuvent déjà être configurés pour quelque chose : l'un pour Bluetooth, et l'autre étant utilisé par défaut pour afficher la console du noyau. Heureusement, ce comportement peut être reconfiguré. selon ce manuel..

Réécriture de la mémoire.

Lors du débogage, pour vérifier une hypothèse, il m'est parfois arrivé de devoir charger le bootloader (désolé) dans la mémoire vive directement depuis l'hôte. Peut-être que cela peut être fait directement depuis GDB, mais j'ai finalement opté pour la solution simple : j'ai copié le fichier nécessaire sur le Raspberry, redirigé le port 4444 (telnet depuis OpenOCD) via SSH et utilisé la commande load_image.Quand vous l'exécutez, on dirait que tout est bloqué, mais en réalité «il ne dort pas, il clignote simplement lentement»: il charge le fichier, mais le fait à une vitesse de quelques kilo-octets par seconde.

Particularités de la mise en place de breakpoints.

Probablement, beaucoup n'y ont jamais pensé lors du débogage de programmes ordinaires, mais les breakpoints ne sont pas toujours matériels. Parfois, mettre un breakpoint consiste à écrire temporairement une instruction spéciale à l'endroit souhaité directement dans le code machine.Par exemple, cela fonctionnait ainsi avec la commande standard b dans GDB. Voici ce qui en découle :

  • on ne peut pas mettre de point d'arrêt à l'intérieur du BootROM, car la ROM
  • on peut mettre un point d'arrêt sur le code chargé dans la mémoire vive à partir de la carte SD, mais il faut attendre qu'il soit chargé. Sinon, ce n'est pas nous qui réécrivons un morceau de code, mais le bootloader qui réécrit notre breakpoint.

Je suis sûr qu'on peut explicitement demander à utiliser des points d'arrêt matériels, mais ils sont de toute façon en nombre limité.

Substitution rapide du BootROM.

Au début du débogage, il y a souvent l'envie de corriger le BootROM et d'essayer encore une fois. Mais il y a un problème : le BootROM fait partie du design chargé dans le FPGA, et sa synthèse prend quelques minutes (et cela après la compilation presque instantanée de l'image BootROM à partir de C et d'assembleur…). Heureusement, en réalité tout beaucoup plus rapidement: la séquence d'actions est la suivante :

  • régénérer bootrom.mif (je suis passé au MIF au lieu de HEX, car j'avais toujours des problèmes avec HEX, et MIF est le format natif d'Alter).
  • Dans Quartus, dire Traitement -> Mettre à jour le fichier d'initialisation de la mémoire
  • dans l'assembleur (dans la colonne gauche des tâches), ordonner de recommencer.

Tout cela prend quelques dizaines de secondes.

Préparation de la carte SD.

Ici, tout est relativement simple, mais il faut faire preuve de patience et disposer d'environ 14 Go d'espace disque :

git clone https://github.com/sifive/freedom-u-sdk
git submodule update --recursive --init
make

Après cela, il faut insérer une carte SD vierge, c'est-à-dire ne contenant rien d'utile, et exécuter

sudo make DISK=/dev/sdX format-boot-loader

… où sdX — est l'appareil assigné à la carte. ATTENTION : les données sur la carte seront supprimées, réécrites et en général ! Il est peu probable qu'il soit judicieux de faire toute la compilation sous sudo, car alors tous les artefacts de compilation appartiendront à root, et il faudra faire la compilation sous sudo en permanence.

Au final, on obtient une carte partitionnée en GPT avec quatre partitions, dont l'une est FAT avec uEnv.txt et une image de démarrage au format FIT (il contient plusieurs sous-images, chacune avec sa propre adresse de démarrage), l'autre partition est vierge, elle est supposée être formatée en Ext4 pour Linux. Les deux autres partitions sont mystérieuses: l'une héberge U-Boot (son décalage, autant que je comprends, est gravé dans le BootROM), sur l'autre, il semble que résident ses variables d'environnement, mais je ne les utilise pas pour l'instant.

Niveau un, BootROM

La sagesse populaire dit : « S'il y a des danses avec un tambour en programmation, alors en électronique, il y a aussi des danses avec un extincteur ». Ce n'est même pas que j'ai failli brûler la carte en pensant que « GND, c'est le même niveau bas » (apparemment, une résistance n'aurait pas fait de mal…) C'est plutôt que si les mains ne viennent pas d'où il faut, l'électronique ne cesse de faire des surprises : en soudant le connecteur sur la carte, je n'ai jamais réussi à souder correctement les contacts — dans les vidéos, on montre comment la soudure se répand toute seule sur toute la connexion, il suffit de poser le fer, chez moi, elle ne faisait que 'bricoler' n'importe comment. Eh bien, peut-être que la soudure n'était pas adaptée à la température du fer, peut-être autre chose… Bref, voyant que j'avais déjà une dizaine de contacts, j'ai abandonné et commencé le débogage. Et là, cela a commencé mystérieux: j'ai connecté RX/TX de l'UART, chargeant le firmware — il écrit

INIT
CMD0
ERROR

Eh bien, tout cela est logique — je n'ai pas connecté le module de la carte SD. Je corrige la situation, charge le firmware… Et le silence… Quoi que j'ai pensé, le problème était simple : une des broches du module devait être connectée à VCC. Dans mon cas, le module supportait 5V pour l'alimentation, donc sans trop réfléchir, j'ai connecté le fil venant du module de l'autre côté de la carte. Au final, le connecteur mal soudé s'est déformé et je viens de perdre le contact UART. facepalm.jpg En gros, « une tête vide n'apporte aucun repos aux pieds », et des mains maladroites à la tête…

Finalement, j'ai vu dans Minicom ce que j'attendais depuis longtemps

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /

De plus, l'indicateur de chargement bouge. Cela me rappelle mes années scolaires et le démarrage lent de MinuetOS à partir d'une disquette. À part que le lecteur de disquette ne grince pas.

Le problème est qu'après le message BOOT, il ne se passe rien. Il est donc temps de se connecter via OpenOCD à Raspberry, à lui GDB sur l'hôte, et de voir de quoi il s'agit.

Tout d'abord, la connexion avec GDB a immédiatement montré que $pc (compteur de programme, adresse de l'instruction actuelle) s'envole vers 0x0 — il est probable que cela se produise après une erreur multiple. Donc, juste après avoir affiché le message BOOT ajoutons une boucle infinie. Cela le retardera un moment…

diff --git a/bootrom/sdboot/sd.c b/bootrom/sdboot/sd.c
index c6b5ede..bca1b7f 100644
--- a/bootrom/sdboot/sd.c
+++ b/bootrom/sdboot/sd.c
@@ -224,6 +224,8 @@ int main(void)

        kputs("BOOT");

+    while(*(volatile char *)0x10000){}
+
        __asm__ __volatile__ ("fence.i" : : : "memory");
        return 0;
 }

Ce code astucieux est utilisé « pour la fiabilité » : j'ai entendu quelque part que, apparemment, une boucle infinie est un comportement indéfini, et ici, le compilateur ne devrait pas le deviner (Je rappelle que par 0x10000 se trouve le BootROM).

Partie 3 : Nous démarrons presque Linux à partir de la carte SD sur RocketChip

On pourrait penser, qu'attendre d'autre — du dur du embarqué, quel code source ici. Mais en fait, cet article l'auteur a débogué le code C… Tricks-trac-truqu.

(gdb) file builds/zeowaa-e115/sdboot.elf
Un programme est déjà en cours de débogage.
Êtes-vous sûr de vouloir changer de fichier ? (y ou n) y
Lecture des symboles depuis builds/zeowaa-e115/sdboot.elf...fait.

Partie 3 : Nous démarrons presque Linux à partir de la carte SD sur RocketChip

Il faut juste charger non un fichier MIF ou binaire, mais la version originale au format ELF.

Maintenant, on peut essayer de deviner l'adresse où l'exécution va se poursuivre (c'est une autre raison pour laquelle le compilateur n'aurait pas dû deviner que la boucle est infinie). La commande

set variable $pc=0xADDR

permet de changer la valeur du registre à la volée (dans ce cas — l'adresse de l'instruction actuelle). Avec elle, on peut aussi changer les valeurs enregistrées en mémoire (et les registres mappés en mémoire).

En fin de compte, je suis arrivé à la conclusion (je ne suis pas sûr que ce soit la bonne), que nous avons « une image de carte SD d'un autre système », et qu'il faut passer non au tout début des données chargées, mais à 0x89800 des octets plus loin :

diff --git a/bootrom/sdboot/head.S b/bootrom/sdboot/head.S
index 14fa740..2a6c944 100644
--- a/bootrom/sdboot/head.S
+++ b/bootrom/sdboot/head.S
@@ -13,7 +13,7 @@ _prog_start:
   smp_resume(s1, s2)
   csrr a0, mhartid
   la a1, dtb
-  li s1, PAYLOAD_DEST
+  li s1, (PAYLOAD_DEST + 0x89800)
   jr s1

   .section .rodata

Peut-être que cela a également été influencé par le fait que n'ayant pas à disposition une carte inutile de 4 Go, j'ai pris celle de 2 Go et, par tâtonnement, remplacé dans le Makefile. DEMO_END=11718750 sur DEMO_END=3078900 (ne cherchez pas de sens dans la signification spécifique — il n'y en a pas, simplement maintenant l'image tient sur la carte).

Niveau deux, U-Boot

Maintenant, nous sommes toujours « en chute », mais nous nous retrouvons déjà à l'adresse 0x0000000080089a84. Ici, je dois admettre : en réalité, l'exposé ne se fait pas « avec tous les arrêts », mais s'écrit partiellement déjà « après coup », donc ici j'ai déjà réussi à ajouter le bon fichier dtb de notre SoC, à corriger dans les paramètres. HiFive_U-Boot variable CONFIG_SYS_TEXT_BASE=0x80089800 (au lieu de 0x08000000), pour que l'adresse de chargement corresponde à la réalité. Nous chargeons maintenant déjà la carte du niveau suivant d'une autre image :

(gdb) file ..\/freedom-u-sdk\/work\/HiFive_U-Boot\/u-boot
(gdb) tui en

Et nous voyons :

   │304     *                                               │
   │305      * entrée de piège                                    │
   │306      *\/                                              │
   │307     trap_entry :                                      │
   │308         addi sp, sp, -32*REGBYTES                    │
  >│309         SREG x1, 1*REGBYTES(sp)                      │
   │310         SREG x2, 2*REGBYTES(sp)                      │
   │311         SREG x3, 3*REGBYTES(sp)                      │

Et nous sautons entre les lignes 308 et 309. Et ce n'est pas surprenant, étant donné que dans $sp se trouve la valeur 0xfffffffe31cdc0a0. Malheureusement, elle « s'échappe » constamment à cause de la ligne 307. Donc essayons de placer un point d'arrêt sur trap_entry, puis de revenir à 0x80089800 (point d'entrée U-Boot), et espérons qu'il ne nécessite pas un bon réglage des registres avant de sauter… Cela semble fonctionner :

(gdb) b trap_entry
Breakpoint 1 at 0x80089a80: file \/hdd\/trosinenko\/fpga\/freedom-u-sdk\/HiFive_U-Boot\/arch\/riscv\/cpu\/HiFive\/start.S, line 308.
(gdb) set variable $pc=0x80089800
(gdb) c
Continuing.

Breakpoint 1, trap_entry () at \/hdd\/trosinenko\/fpga\/freedom-u-sdk\/HiFive_U-Boot\/arch\/riscv\/cpu\/HiFive\/start.S:308
(gdb) p\/x $sp
$4 = 0x81cf950

Pas un très bon pointeur de pile, je dirais : il indique complètement à côté de la RAM (à moins que nous n'ayons pas encore de traduction d'adresses, mais espérons une solution simple).

Essayons de remplacer le pointeur par 0x881cf950. En fin de compte, nous en venons à ce que handle_trap est appelé encore et encore, tandis que nous plongeons dans _exit_trap avec l'argument epc=2148315240 (en valeur décimale) :

(gdb) x/10i 2148315240
   0x800cb068 :     lbu     a4,0(a5)
   0x800cb06c :     bnez    a4,0x800cb078 
   0x800cb070 :     sub     a0,a5,a0
   0x800cb074 :     ret
   0x800cb078 :     addi    a5,a5,1
   0x800cb07c :     j       0x800cb064 
   0x800cb080 : addi    sp,sp,-32
   0x800cb084 :       sd      s0,16(sp)
   0x800cb088 :       sd      ra,24(sp)
   0x800cb08c :      li      s0,0

Mettons un point d'arrêt sur strnlen, continuons et voyons :

(gdb) bt
#0 strnlen (s=s@entry=0x10060000 "", count=18446744073709551615) à lib/string.c:283
#1 0x00000000800cc14c dans string (buf=buf@entry=0x881cbd4c "", end=end@entry=0x881cc15c "", s=0x10060000 "", field_width=, precision=, flags=) à lib/vsprintf.c:265
#2 0x00000000800cc63c dans vsnprintf_internal (buf=buf@entry=0x881cbd38 "code d'exception : 5 , ", size=size@entry=1060, fmt=0x800d446e "s , epc x , ra lxn", fmt@entry=0x800d4458 "code d'exception : %d , %s , epc x , ra lxn", args=0x881cc1a0,
 args@entry=0x881cc188) à lib/vsprintf.c:619
#3 0x00000000800cca54 dans vsnprintf (buf=buf@entry=0x881cbd38 "code d'exception : 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "code d'exception : %d , %s , epc x , ra lxn", args=args@entry=0x881cc188) à lib/vsprintf.c:710
#4 0x00000000800cca68 dans vscnprintf (buf=buf@entry=0x881cbd38 "code d'exception : 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "code d'exception : %d , %s , epc x , ra lxn", args=args@entry=0x881cc188) à lib/vsprintf.c:717
#5 0x00000000800ccb50 dans printf (fmt=fmt@entry=0x800d4458 "code d'exception : %d , %s , epc x , ra lxn") à lib/vsprintf.c:792
#6 0x000000008008a9f0 dans _exit_trap (regs=, epc=2148315240, code=) à arch/riscv/lib/interrupts.c:92
#7 handle_trap (mcause=, epc=, regs=) à arch/riscv/lib/interrupts.c:55
#8 0x0000000080089b10 dans trap_entry () à /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S:343
La trace s'est arrêtée : le cadre n'a pas enregistré le PC

Il semble que _exit_trap veut afficher des informations de débogage sur l'exception survenue, mais il ne peut pas. Donc, quelque chose ne s'affiche pas dans le code source. set directories ../freedom-u-sdk/HiFive_U-Boot/ Oh ! Maintenant ça s'affiche !

Eh bien, relançons et voyons dans la trace de pile la raison du problème initial qui a causé la première erreur (mcause == 5). Si j'ai bien compris ce qui est écrit ici à la page 37, cette exception signifie erreur d'accès au chargement. La raison semble être que ici

arch/riscv/cpu/HiFive/start.S :

call_board_init_f:
    li  t0, -16
    li  t1, CONFIG_SYS_INIT_SP_ADDR
    and sp, t1, t0  /* forcer un alignement à 16 octets */

#ifdef CONFIG_DEBUG_UART
    jal debug_uart_init
#endif

call_board_init_f_0:
    mv  a0, sp
    jal board_init_f_alloc_reserve
    mv  sp, a0
    jal board_init_f_init_reserve

    mv  a0, zero    /* a0 <-- boot_flags = 0 */
    la t5, board_init_f
    jr t5       /* saut vers board_init_f() */

$sp a cette valeur incorrecte, et une erreur se produit à l'intérieur de board_init_f_init_reserve . Il semble que ce soit le coupable : la variable avec un nom sans équivoque CONFIG_SYS_INIT_SP_ADDR. Elle est définie dans le fichier HiFive_U-Boot/include/configs/HiFive-U540.h. À un moment donné, j'ai même pensé que peut-être il serait plus facile d'ajuster légèrement le processeur plutôt que de modifier le chargeur pour le processeur. Mais j'ai ensuite réalisé que cela ressemblait plus à un artefact d'une configuration inachevée pour un autre type de mémoire, et que l'on pouvait essayer de faire comme suit :#if 0- des réglages non terminés pour une autre configuration de mémoire, et on peut essayer de faire comme suit :

diff --git a/include/configs/HiFive-U540.h b/include/configs/HiFive-U540.h
index ca89383..245542c 100644
--- a/include/configs/HiFive-U540.h
+++ b/include/configs/HiFive-U540.h
@@ -65,12 +65,9 @@
 #define CONFIG_SYS_SDRAM_BASE  PHYS_SDRAM_0
 #endif
 #if 1
-/*#define CONFIG_NR_DRAM_BANKS 1*/
+#define CONFIG_NR_DRAM_BANKS   1
 #define PHYS_SDRAM_0   0x80000000              /* Banque SDRAM #1 */
-#define PHYS_SDRAM_1   
-       (PHYS_SDRAM_0 + PHYS_SDRAM_0_SIZE)      /* Banque SDRAM #2 */
-#define PHYS_SDRAM_0_SIZE      0x80000000      /* 2 Go */
-#define PHYS_SDRAM_1_SIZE      0x10000000      /* 256 Mo */
+#define PHYS_SDRAM_0_SIZE      0x40000000      /* 1 Go */
 #define CONFIG_SYS_SDRAM_BASE  PHYS_SDRAM_0
 #endif
 /*
@@ -81,7 +78,7 @@
 #define CONSOLE_ARG                            "console=ttyS0,115200 "

 /* Initialiser le pointeur de pile */
-#define CONFIG_SYS_INIT_SP_ADDR                (0x08000000 + 0x001D0000 - 
+#define CONFIG_SYS_INIT_SP_ADDR                (0x80000000 + 0x001D0000 - 
                                        GENERATED_GBL_DATA_SIZE)

 #define CONFIG_SYS_LOAD_ADDR           0xa0000000      /* en haut de la SDRAM */

À un moment donné, le nombre de béquilles de fixation technologique a atteint un seuil critique. Après quelques réflexions, j'en suis venu à la nécessité de réaliser un port correct pour ma carte. Pour cela, il faut copier et adapter un certain nombre de fichiers à notre configuration.

Eh bien, approximativement, voilà un petit échantillon

trosinenko@trosinenko-pc:/hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot$ git show --name-status
commit 39cd67d59c16ac87b46b51ac1fb58f16f1eb1048 (HEAD -> zeowaa-1gb)
Author: Anatoly Trosinenko 
Date:   Tue Jul 2 17:13:16 2019 +0300

    Support initial pour la carte Zeowaa A-E115FB

M       arch/riscv/Kconfig
A       arch/riscv/cpu/zeowaa-1gb/Makefile
A       arch/riscv/cpu/zeowaa-1gb/cpu.c
A       arch/riscv/cpu/zeowaa-1gb/start.S
A       arch/riscv/cpu/zeowaa-1gb/timer.c
A       arch/riscv/cpu/zeowaa-1gb/u-boot.lds
M       arch/riscv/dts/Makefile
A       arch/riscv/dts/zeowaa-1gb.dts
A       board/Zeowaa/zeowaa-1gb/Kconfig
A       board/Zeowaa/zeowaa-1gb/MAINTAINERS
A       board/Zeowaa/zeowaa-1gb/Makefile
A       board/Zeowaa/zeowaa-1gb/Zeowaa-A-E115FB.c
A       configs/zeowaa-1gb_defconfig
A       include/configs/zeowaa-1gb.h

Les détails peuvent être consultés dans dépôts.

Il s'est avéré que sur cette carte SiFive, les registres de certains périphériques ont des adresses différentes. Il s'est également avéré que U-Boot est configuré par le même mécanisme Kconfig que celui que l'on connaît du noyau Linux — par exemple, on peut donner l'ordre make menuconfig, et une interface texte conviviale apparaîtra avec la description des paramètres. ? En gros, en combinant les descriptions de deux cartes pour créer celle d'une troisième, et en supprimant les réglages exagérés de PLL (je suppose que cela a un rapport avec la gestion depuis l'ordinateur hôte via PCIe, mais je n'en suis pas sûr), j'ai obtenu un firmware qui, par temps favorable sur Mars, m'affichait via UART le hash du commit dont il était construit, ainsi que la quantité de DRAM (mais cette information, c'est moi qui l'ai définie dans l'en-tête).

Il est dommage qu'après cela, la carte cessait généralement de répondre via le JTAG du processeur, et le démarrage depuis la carte SD — eh bien, dans ma configuration, ce n'est pas rapide. D'un autre côté, parfois le BootROM signalait que ERREUR, le démarrage a échoué, et U-Boot apparaissait immédiatement. C'est alors que j'ai réalisé : apparemment, après le redémarrage, le bitstream dans le FPGA ne rafraîchit pas la mémoire, il n'a pas le temps de « se désynchroniser », etc. En gros, on peut simplement, dès l'apparition du message LOADING / se connecter avec le débogueur et donner la commande set variable $pc=0x80089800, contournant ainsi ce long démarrage (évidemment, en supposant qu'il ait échoué assez tôt la dernière fois et qu'il n'ait pas réussi à charger quelque chose par-dessus le code original).

D'ailleurs, est-ce normal que le processeur se fige complètement, et que le débogueur JTAG ne puisse se connecter avec les messages

Error: unable to halt hart 0
Error:   dmcontrol=0x80000001
Error:   dmstatus =0x00030c82

Attendez une minute ! Je l'ai déjà vu ! Quelque chose de similaire se produit lors d'un deadlock TileLink, et je n'ai pas vraiment confiance en l'auteur du contrôleur de mémoire - il l'a écrit lui-même... Étrangement, après la première reconstruction réussie du processeur après la modification du contrôleur, j'ai vu :

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING
BOOT

U-Boot 2018.09-g39cd67d-dirty (03 juillet 2019 - 13:50:33 +0300)

DRAM:  1 GiB
MMC:
BEFORE LOAD ENVBEFORE FDTCONTROLADDRBEFORE LOADADDRIn:    serial
Out:   serial
Err:   serial
Appuyez sur n'importe quelle touche pour arrêter le démarrage automatique :  3

Cette étrange ligne avant In: serial Ne vous en faites pas — c'est moi qui essayais de comprendre sur un processeur gelé si cela fonctionnait correctement avec l'environnement. Que signifie « Ça fait déjà dix minutes que ça reste bloqué » ? Au moins, il a réussi à se relocaliser et à passer au menu de démarrage ! Petite parenthèse : bien que U-Boot se charge parmi les premiers 2^24 octets de la carte SD, une fois lancé, il se copie ailleurs à l'adresse soit écrite dans l'en-tête de configuration, soit simplement dans les adresses supérieures de la mémoire vive, effectue la relocalisation des symboles ELF et cède le contrôle à cet endroit. Donc, il semble que ce niveau ait été franchi et que, en prime, nous avons un processeur qui ne reste pas bloqué après cela.

Alors, pourquoi le minuteur ne fonctionne pas ? Il semble que l'horloge, pour une raison quelconque, ne tourne pas du tout…

(gdb) x/x 0x0200bff8
0x200bff8:      0x00000000

Et si on faisait tourner les aiguilles manuellement ?

(gdb) set variable *0x0200bff8=310000000
(gdb) c

Alors :

Appuyez sur n'importe quelle touche pour arrêter l'autoboot :  0
MMC_SPI : 0 à 0:1 hz 20000000 mode 0

Conclusion : l'horloge ne fonctionne pas. Peut-être que cela explique également pourquoi l'entrée du clavier ne fonctionne pas :

HiFive_U-Boot/cmd/bootmenu.c :

static void bootmenu_loop(struct bootmenu_data *menu,
        enum bootmenu_key *key, int *esc)
{
    int c;

    while (!tstc()) {
        WATCHDOG_RESET();
        mdelay(10);
    }

    c = getc();

    switch (*esc) {
    case 0:
        /* Premier caractère de la séquence d'échappement ANSI 'e' */
        if (c == 'e') {
            *esc = 1;
            *key = KEY_NONE;
        }
        break;
    case 1:
        /* Deuxième caractère de ANSI '[' */
        if (c == '[') {
...

Le problème était que j'avais un peu trop complexifié les choses : j'avais ajouté dans le config du processeur la clé :

  case DTSTimebase => BigInt(0)

… en me basant sur le fait qu'il était dit dans le commentaire « si vous ne savez pas, laissez 0 ». Et bien WithNBigCores il était justement fixé à 1 MHz (comme d'ailleurs mentionné dans le config de U-Boot). Mais je suis, enfin, minutieux et pointilleux : ici je ne sais pas, là 25 MHz ! Au final, rien ne fonctionne. J'ai retiré mes « améliorations » et…

Appuyez sur n'importe quelle touche pour arrêter l'autoboot :  0
MMC_SPI : 0 à 0:1 hz 20000000 mode 0
## Type de table de partition inconnu 0
libfdt fdt_path_offset() retourné FDT_ERR_NOTFOUND
** Pas de table de partition - mmc 0 **
## Info : taille des données d'entrée = 34 = 0x22
Exécution de uEnv.txt boot2...
## Erreur : "boot2" non défini
HiFive-Unleashed #

On peut même entrer des commandes ! Par exemple, après avoir un peu fouillé, on peut finalement deviner d'entrer mmc_spi 1 10000000 0; mmc part, réduisant la fréquence SPI de 20 MHz à 10 MHz. Pourquoi ? Eh bien, dans la configuration, la fréquence maximale était définie à 20 MHz, et elle y figure encore. Mais, si j'ai bien compris, les interfaces, du moins ici, fonctionnent ainsi : le code divise la fréquence du bloc matériel (chez moi — partout 25 MHz) par la fréquence cible, et définit cette valeur comme diviseur dans le registre de contrôle approprié. Le problème est que si pour un UART à 115200 Hz, cela pourrait convenir, en divisant directement 25000000 par 20000000, on obtient 1, c'est-à-dire que cela fonctionnera à 25 MHz. Cela peut être acceptable, mais si des limites sont définies, c'est qu'il y a une raison (mais ce n'est pas certain)... En gros, il est plus facile de mettre en place cela et d’avancer — loin et, hélas, pour longtemps. 25 MHz — ce n'est pas un Core i9.

Sortie de la console

HiFive-Unleashed # env edit mmcsetup
edit: mmc_spi 1 10000000 0; mmc part
HiFive-Unleashed # boot
MMC_SPI: 1 à 0:1 hz 10000000 mode 0

Carte des partitions pour l'appareil MMC 0  --   Type de partition: EFI

Part    LBA de départ       LBA de fin         Nom
        Attributs
        Type GUID
        Partition GUID
  1     0x00000800      0x0000ffde      "Vfat Boot"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        type:   data
        guid:   76bd71fd-1694-4ff3-8197-bfa81699c2fb
  2     0x00040800      0x002efaf4      "root"
        attrs:  0x0000000000000000
        type:   0fc63daf-8483-4772-8e79-3d69d8477de4
        type:   linux
        guid:   9f3adcc5-440c-4772-b7b7-283124f38bf3
  3     0x0000044c      0x000007e4      "uboot"
        attrs:  0x0000000000000000
        type:   5b193300-fc78-40cd-8002-e86c45580b47
        guid:   bb349257-0694-4e0f-9932-c801b4d76fa3
  4     0x00000400      0x0000044b      "uboot-env"
        attrs:  0x0000000000000000
        type:   a09354ac-cd63-11e8-9aff-70b3d592f0fa
        guid:   4db442d0-2109-435f-b858-be69629e7dbf
libfdt fdt_path_offset() a retourné FDT_ERR_NOTFOUND
2376 octets lus en 0 ms
Exécution de uEnv.txt boot2...
15332118 octets lus en 0 ms
## Chargement du noyau depuis l'image FIT à 90000000 ...
   Utilisation de la configuration 'config-1'
   Essai du sous-élément de noyau 'bbl'
     Description:  BBL/SBI/riscv-pk
     Type:         Image de noyau
     Compression:  non compressé
     Début des données:   0x900000d4
     Taille des données:    74266 Octets = 72.5 KiB
     Architecture: RISC-V
     OS:           Linux
     Adresse de chargement: 0x80000000
     Point d'entrée:  0x80000000
     Algo de hachage:    sha256
     Valeur de hachage:   28972571467c4ad0cf08a81d9cf92b9dffc5a7cb2e0cd12fdbb3216cf1f19cbd
   Vérification de l'intégrité du hachage ... sha256+ OK
## Chargement de fdt depuis l'image FIT à 90000000 ...
   Utilisation de la configuration 'config-1'
   Essai du sous-élément fdt 'fdt'
     Description:  indisponible
     Type:         Flat Device Tree
     Compression:  non compressé
     Début des données:   0x90e9d31c
     Taille des données:    6911 Octets = 6.7 KiB
     Architecture: RISC-V
     Adresse de chargement: 0x81f00000
     Algo de hachage:    sha256
     Valeur de hachage:   10b0244a5a9205357772ea1c4e135a4f882409262176d8c7191238cff65bb3a8
   Vérification de l'intégrité du hachage ... sha256+ OK
   Chargement de fdt de 0x90e9d31c à 0x81f00000
   Démarrage à l'aide du blob fdt à 0x81f00000
## Chargement des éléments chargeables depuis l'image FIT à 90000000 ...
   Essai du sous-élément chargés 'noyau'
     Description:  noyau Linux
     Type:         Image de noyau
     Compression:  non compressé
     Début des données:   0x900123e8
     Taille des données:    10781356 Octets = 10.3 MiB
     Architecture: RISC-V
     OS:           Linux
     Adresse de chargement: 0x80200000
     Point d'entrée:  indisponible
     Algo de hachage:    sha256
     Valeur de hachage:   72a9847164f4efb2ac9bae736f86efe7e3772ab1f01ae275e427e2a5389c84f0
   Vérification de l'intégrité du hachage ... sha256+ OK
   Chargement des éléments chargeables depuis 0x900123e8 à 0x80200000
## Chargement des éléments chargeables depuis l'image FIT à 90000000 ...
   Essai du sous-élément chargés 'ramdisk'
     Description:  buildroot initramfs
     Type:         Image RAMDisk
     Compression:  compressé gzip
     Début des données:   0x90a5a780
     Taille des données:    4467411 Octets = 4.3 MiB
     Architecture: RISC-V
     OS:           Linux
     Adresse de chargement: 0x82000000
     Point d'entrée:  indisponible
     Algo de hachage:    sha256
     Valeur de hachage:   883dfd33ca047e3ac10d5667ffdef7b8005cac58b95055c2c2beda44bec49bd0
   Vérification de l'intégrité du hachage ... sha256+ OK
   Chargement des éléments chargeables depuis 0x90a5a780 à 0x82000000

D'accord, nous sommes passés à un nouveau niveau, mais cela bloque toujours. Parfois, il génère même des exceptions. Vous pouvez voir mcause en interceptant le code à l'adresse indiquée. $pc et après si se retrouver sur trap_entry. Le gestionnaire U-Boot ne peut afficher que pour mcause = 0..4, préparez-vous à entrer dans une boucle sur un chargement incorrect. Je suis allé dans la configuration, j'ai commencé à regarder ce que j'avais changé et je me suis rappelé : c'est inscrit là-bas, conf/rvboot-fit.txt il est écrit :

fitfile=image.fit
# en dessous doit correspondre à ce qui est dans FIT (ugha)

Eh bien, mettons tous les fichiers en conformité, remplaçons la ligne de commande du noyau à peu près de cette manière, car il y a des soupçons que SIF0 — cela signifie une sortie quelque part sur le PCIe :

-bootargs=console=ttySIF0,921600 debug
+bootargs=console=ttyS0,125200 debug

Et, tant qu'on y est, changeons l'algorithme de hachage de SHA-256 à MD5 : je n'ai pas besoin de cryptographie (surtout pendant le débogage), cela prend énormément de temps, et pour la détection des erreurs d'intégrité au chargement, MD5 est largement suffisant. Alors, au final ? Nous passons le niveau précédent beaucoup plus rapidement (grâce à un hachage plus simple), et le suivant s'est ouvert :

...
   Vérification de l'intégrité du hachage ... md5+ OK
   Chargement des fichiers à partir de 0x90a5a758 jusqu'à 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
        linux,initrd-end = ;
        linux,initrd-start = ;
        riscv,kernel-end = ;
        riscv,kernel-start = ;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
libfdt fdt_path_offset() a retourné FDT_ERR_NOTFOUND
chosen {
        linux,initrd-end = ;
        linux,initrd-start = ;
        riscv,kernel-end = ;
        riscv,kernel-start = ;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
   Chargement de l'image du noyau ... OK
Démarrage du noyau dans
3

Sauf que l'horloge ne tourne pas…

(gdb) x/x 0x0200bff8
0x200bff8:      0x00000000

Oups, il semble que corriger l'heure était un placebo, bien que cela m'ait semblé utile à l'époque. Non, il faut bien le réparer, mais commençons par ajuster les aiguilles manuellement et voyons ce que ça donne :

0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Continuing.
^C
Le programme a reçu le signal SIGINT, Interruption.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Continuing.
^C
Le programme a reçu le signal SIGINT, Interruption.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Continuing.

En attendant…

   Chargement de l'image du noyau ... OK
Démarrage du noyau dans
3
2
1
0
## Démarrage de l'application à 0x80000000 ...

Non, je vais automatiser le réglage de l'horloge — sinon, peut-être qu'il va penser à calibrer le minuteur !

Et l'adresse de l'instruction actuelle pointait quant à elle vers

0000000080001c20 :
    80001c20:   1141                    addi    sp,sp,-16
    80001c22:   e022                    sd      s0,0(sp)
    80001c24:   842a                    mv      s0,a0
    80001c26:   00005517                auipc   a0,0x5
    80001c2a:   0ca50513                addi    a0,a0,202 # 80006cf0 
    80001c2e:   e406                    sd      ra,8(sp)
    80001c30:   f7fff0ef                jal     ra,80001bae 
    80001c34:   8522                    mv      a0,s0
    80001c36:   267000ef                jal     ra,8000269c 
    80001c3a:   00010797                auipc   a5,0x10
    80001c3e:   41e78793                addi    a5,a5,1054 # 80012058 
    80001c42:   639c                    ld      a5,0(a5)
    80001c44:   c399                    beqz    a5,80001c4a 
    80001c46:   72c000ef                jal     ra,80002372 
    80001c4a:   45a1                    li      a1,8
    80001c4c:   4501                    li      a0,0
    80001c4e:   dc7ff0ef                jal     ra,80001a14 
    80001c52:   10500073                wfi
    80001c56:   bff5                    j       80001c52

dans le chargeur de démarrage Berkeley Boot Loader. Personnellement, je suis perplexe par la mention htif — host interface, utilisé pour le démarrage tethered du noyau (c’est-à-dire en coopération avec l’ARM hôte), je pensais que c'était en mode standalone. Cependant, si vous trouvez cette fonction dans le code source, il est clair que ce n’est pas si grave :

void poweroff(uint16_t code)
{
  printm("Power offrn");
  finisher_exit(code);
  if (htif) {
    htif_poweroff();
  } else {
    send_ipi_many(0, IPI_HALT);
    while (1) { asm volatile ("wfin"); }
  }
}

Quête : lancez les horloges

La recherche des registres dans CLINT nous mène à

    val io = IO(new Bundle {
      val rtcTick = Bool(INPUT)
    })

    val time = RegInit(UInt(0, width = timeWidth))
    when (io.rtcTick) { time := time + UInt(1) }

Qui se connecte au RTC, ou dans le mystérieux MockAON, au sujet duquel j'avais initialement pensé : « Qu'est-ce que c'est ici ? Peu clair ? Débranchons ! » Puisque je ne comprends toujours pas quelle magie d'horloge s'y déroule, je vais simplement réimplémenter cette logique dans System.scala:

  val rtcDivider = RegInit(0.asUInt(16.W)) // par précaution je vais supporter jusqu'à 16 GHz, je suis optimiste :)
  val mhzInt = p(DevKitFPGAFrequencyKey).toInt
  // Supposons que la fréquence soit un nombre entier en mégahertz
  rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
  outer.clintOpt.foreach { clint =>
    clint.module.io.rtcTick := rtcDivider === 0.U
  }

En parcourant le noyau Linux

L'histoire ici est déjà trop longue et un peu monotone, donc je vais la survoler :

BBL supposait la présence du FDT à l'adresse 0xF0000000, mais je l'avais déjà corrigé ! Eh bien, cherchons encore... Trouvé dans HiFive_U-Boot/arch/riscv/lib/boot.c, remplacé par 0x81F00000, spécifié dans la configuration du démarrage de U-Boot.

Puis, BBL s'est plaint qu'il n'y avait pas assez de mémoire. Mon chemin m'a conduit à la fonction mem_prop, ce qui dans riscv-pk/machine/fdt.c: de là, j'ai appris qu'il fallait marquer le nœud fdt ram comme device_type = "memory" — ensuite, peut-être qu'il faudra ajuster le générateur de processeur, mais pour l'instant je vais juste le remplir à la main — de toute façon, j'ai transféré ce fichier manuellement.

J'ai maintenant reçu un message (fournit au format structuré, avec des retours de chariot) :

This is bbl's dummy_payload. To boot a real kernel, reconfigure bbl
with the flag --with-payload=PATH, then rebuild bbl. Alternatively,
bbl can be used in firmware-only mode by adding device-tree nodes
for an external payload and use QEMU's -bios and -kernel options.

Il semble que les options soient indiquées comme il se doit riscv,kernel-start et riscv,kernel-end dans le DTB, mais des zéros sont analysés. Débogage query_chosen a montré que BBL essaie d'analyser une adresse 32 bits, mais se retrouve avec une paire <0x0 0xADDR>, et la première valeur semble être les bits de poids faible. J'ai ajouté dans la section chosen

chosen {
      #address-cells = ;
      #size-cells = ;
      ...
}

et ajusté la génération des valeurs : ne pas ajouter 0x0 comme premier élément.

Ces 100500 étapes simples permettront de voir facilement comment le pingouin tombe :

Texte caché

   Vérification de l'intégrité du hachage ... md5+ OK
   Chargement des objets à partir de 0x90a5a758 vers 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
choisi {
        linux,initrd-end = ;
        linux,initrd-start = ;
        riscv,kernel-end = ;
        riscv,kernel-start = ;
        #address-cells = ;
        #size-cells = ;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
        stdout-path = "uart0:38400n8";
};
libfdt fdt_path_offset() a retourné FDT_ERR_NOTFOUND
choisi {
        linux,initrd-end = ;
        linux,initrd-start = ;
        riscv,kernel-end = ;
        riscv,kernel-start = ;
        #address-cells = ;
        #size-cells = ;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
        stdout-path = "uart0:38400n8";
};
   Chargement de l'image du noyau ... OK
Démarrage du noyau dans
3
2
1
0
## Démarrage de l'application à 0x80000000 ...
bbl loader

                SIFIVE, INC.

         5555555555555555555555555
        5555                   5555
       5555                     5555
      5555                       5555
     5555       5555555555555555555555
    5555       555555555555555555555555
   5555                             5555
  5555                               5555
 5555                                 5555
5555555555555555555555555555          55555
 55555           555555555           55555
   55555           55555           55555
     55555           5           55555
       55555                   55555
         55555               55555
           55555           55555
             55555       55555
               55555   55555
                 555555555
                   55555
                     5

           SiFive RISC-V Core IP
[    0.000000] OF: fdt: Ignorer la plage mémoire 0x80000000 - 0x80200000
[    0.000000] Version de Linux 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc version 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Mer Jul 3 21:29:21 MSK 2019
[    0.000000] console de démarrage [early0] activée
[    0.000000] Ramdisk initial à : 0x(____ptrval____) (16777216 octets)
[    0.000000] Plages de zone :
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Départ de la zone mobile pour chaque nœud
[    0.000000] Plages de nœuds de mémoire précoce
[    0.000000]   nœud   0 : [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Configuration de la mémoire initiale nœud 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Au nœud 0, nombre total de pages : 261632
[    0.000000]   zone DMA32 : 3577 pages utilisées pour memmap
[    0.000000]   zone DMA32 : 0 page réservée
[    0.000000]   zone DMA32 : 261632 pages, lot LIFO : 63
[    0.000000] TLB IO logiciel : mappé [mem 0xbb1fc000-0xbf1fc000] (64 Mo)

(l'emblème est affiché par BBL, et ce qui concerne les horodatages — le noyau).

Heureusement, je ne sais pas comment partout, mais sur RocketChip, lors de la connexion du débogueur via JTAG, on peut capter des trap dès la sortie de la boîte — le débogueur s'arrêtera exactement à ce moment-là.

Le programme a reçu le signal SIGTRAP, piège de trace/d'arrêt.
0xffffffe0000024ca dans ?? ()
(gdb) bt
#0  0xffffffe0000024ca dans ?? ()
Trace de retour arrêtée : le cadre précédent est identique à ce cadre (pile corrompue ?)
(gdb) fichier work/linux/vmlinux
Un programme est déjà en cours de débogage.
Êtes-vous sûr de vouloir changer le fichier ? (y ou n) y
Lecture des symboles à partir de work/linux/vmlinux... terminé.
(gdb) bt
#0  0xffffffe0000024ca dans setup_smp () à /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75
#1  0x0000000000000000 dans ?? ()
Trace de retour arrêtée : le cadre n'a pas enregistré le PC.

freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c :

void __init setup_smp(void)
{
    struct device_node *dn = NULL;
    int hart;
    bool found_boot_cpu = false;
    int cpuid = 1;

    while ((dn = of_find_node_by_type(dn, "cpu"))) {
        hart = riscv_of_processor_hartid(dn);
        if (hart < 0)
            continue;

        if (hart == cpuid_to_hartid_map(0)) {
            BUG_ON(found_boot_cpu);
            found_boot_cpu = 1;
            continue;
        }

        cpuid_to_hartid_map(cpuid) = hart;
        set_cpu_possible(cpuid, true);
        set_cpu_present(cpuid, true);
        cpuid++;
    }

    BUG_ON(!found_boot_cpu); // < VOUS ÊTES ICI
}

Comme le disait une vieille blague, CPU non trouvé, exécution de l'émulation logicielle. Ou pas. Égaré dans un seul cœur de processeur.

/* The lucky hart to first increment this variable will boot the other cores */
atomic_t hart_lottery;
unsigned long boot_cpu_hartid;

Un bon commentaire dans linux/arch/riscv/kernel/setup.c — c'est comme peindre une clôture selon la méthode de Tom Sawyer. En gros, aujourd'hui, il n'y a étonnamment pas de gagnants, le prix est reporté au prochain tirage...

Je propose de conclure cet article déjà trop long.

À suivre. Il y aura un combat avec une erreur sournoise qui parvient à se cacher si on l'approche lentement avec singlestep.

Screencast textuel de chargement (lien externe):
Partie 3 : Nous démarrons presque Linux à partir de la carte SD sur RocketChip

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster