Teil 3: Wir laden Linux fast von der SD-Karte auf RocketChip

Teil 3: Wir laden Linux fast von der SD-Karte auf RocketChip Im vorherigen Teil wurde ein mehr oder weniger funktionsfähiger Speichercontroller entwickelt, genauer gesagt - eine Wrapper über den IP Core aus Quartus, die einen Übergang auf TileLink darstellt. Heute in der Rubrik „Portieren von RocketChip auf ein weniger bekanntes chinesisches Board mit Cyclone“ sehen Sie eine funktionierende Konsole. Der Prozess hat sich etwas hingezogen: Ich dachte schon, dass ich Linux schnell starten und wir weitermachen können, aber es kam anders. In diesem Teil schlage ich vor, den Startprozess von U-Boot, BBL und die zaghaften Versuche der Initialisierung des Linux-Kernels anzusehen. Aber es gibt eine Konsole - die von U-Boot, und sie ist recht fortschrittlich, hat vieles von dem, was Sie von einer vollwertigen Konsole erwarten.

In der Hardware wird eine SD-Karte hinzugefügt, die über die SPI-Schnittstelle angeschlossen ist, sowie UART. Im Softwareteil wird das BootROM ersetzt durch xip auf sdboot und ebenfalls die folgenden Ladephasen (auf der SD-Karte) hinzugefügt.

Verfeinerung des Hardwareteils

Also, die Aufgabe: Wir müssen auf den "großen" Kern umsteigen und UART (von Raspberry) sowie den SD-Adapter anschließen (es wurde ein Board von Catalex mit sechs Pins verwendet: GND, VCC, MISO, MOSI, SCK, CS).

Im Grunde war alles ziemlich einfach. Aber bevor ich das realisierte, wurde ich ein wenig hin und her geworfen: Nach dem letzten Mal beschloss ich, dass es wieder nur nötig sei, etwas in System so etwas wie HasPeripheryUART (und entsprechend in die Implementierung) zu mischen, das gleiche für die SD-Karte - und alles wird bereit sein. Dann beschloss ich, mir anzusehen, wie es in einem "ernsthaften" Design implementiert ist. Was haben wir hier also an Ernsthaftem? Arty scheint nicht geeignet zu sein - bleibt das Monster unleahshed.DevKitConfigs. Und plötzlich stellte ich fest, dass überall irgendwelche Overlays sind, die durch Schlüsselparameter hinzugefügt werden. Ich vermute, dass das wahrscheinlich sehr flexibel und konfigurierbar ist, aber ich würde gerne wenigstens etwas zum Laufen bringen ... Haben Sie nicht etwas Ähnliches, nur einfacher und weniger perfekt? Hier stieß ich auf vera.iofpga.FPGAChip für Microsemi FPGAs und habe sofort Zitate herausgerissen und versucht, meine eigene Implementierung analog zu erstellen, da hier mehr oder weniger die gesamte "Verkabelung des Motherboards" in einer Datei ist.

Es stellte sich heraus, dass ich einfach nur hinzufügen musste zu System.scala die Zeilen

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
...

Zeile im Klassenkörper System fügt Informationen über die Frequenz hinzu, mit der dieser Teil unseres SoC im dts-Datei läuft. Soweit ich verstehe, ist DTS/DTB eine statische Analogie zur Plug-and-Play-Technologie für eingebettete Geräte: Der dts-Beschreibungsbaum wird in eine binäre dtb-Datei kompiliert und vom Bootloader an den Kernel übergeben, damit dieser die Hardware entsprechend konfigurieren kann. Interessanterweise synthetisiert alles perfekt, solange die Zeile mit tlclock nicht vorhanden ist. Das BootROM (denken Sie daran, jetzt wird es bereits sdboot) kann nicht kompiliert werden – während des Kompilierungsprozesses parst es die dts-Datei und erstellt einen Header mit dem Makro TL_CLK, das es ihm ermöglicht, die Frequenzteiler für die externen Schnittstellen korrekt zu konfigurieren.

Es wird auch notwendig sein, ein wenig an der Verdrahtung zu arbeiten:

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))

  // SD-Karte
  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
}

Registerketten wurden ehrlich gesagt einfach nach dem Vorbild einiger anderer Stellen im ursprünglichen Code hinzugefügt. Wahrscheinlich sollten sie vor Metastabilitätschützen. Möglicherweise verfügt bereits einige Blöcke über einen eigenen Schutz, aber zunächst möchte ich es zumindest auf einem "qualitativen Niveau" zum Laufen bringen. Eine interessantere Frage für mich ist, warum MISO und MOSI an verschiedenen dq physisch sind. Ich habe einfach die Ausgänge des Designs auf die freien Kontakte auf dem Stecker zugewiesen und den Spannungsauswahl-Jumper auf 3,3 V verschoben.? Ответа я пока так и не нашёл, но, похоже, остальной код рассчитывает именно на такое подключение.

SD-Adapter

Ansicht von oben:

Ansicht von unten:

Teil 3: Wir laden Linux fast von der SD-Karte auf RocketChip

Debugging der Software: Werkzeuge

Teil 3: Wir laden Linux fast von der SD-Karte auf RocketChip

Lassen Sie uns zunächst über die verfügbaren Debugging-Tools und deren Einschränkungen sprechen.

Minicom

Zunächst benötigen wir eine Möglichkeit, das auszulesen, was der Bootloader und der Kernel ausgeben. Dafür benötigen wir unter Linux (in diesem Fall – auf dem RaspberryPi) das Minicom-Programm. Im Allgemeinen sollte jedes Programm zur Arbeit mit einem seriellen Port in Ordnung sein.

Bitte beachten Sie, dass der Gerätename des Ports beim Start wie folgt angegeben werden muss:

-D /dev/ttyS0 – nach der Option. Die wichtigste Information: Zum Beenden verwenden Sie -DCtrl-A, X . Ich hatte jedoch einmal den Fall, dass diese Kombination nicht funktionierte – dann kann man einfach aus einer benachbarten SSH-Sitzung sagen:killall -KILL minicom killall -KILL minicom.

Es gibt noch ein weiteres Merkmal. Konkret auf dem Raspberry Pi gibt es zwei UARTs, und beide Ports können bereits für etwas verwendet werden: einer für Bluetooth, über den standardmäßig die Kernel-Konsole ausgegeben wird. Glücklicherweise kann dieses Verhalten umkonfiguriert werden. nach diesem Handbuch.

Speicher umschreiben

Bei der Fehlersuche musste ich manchmal zur Überprüfung der Hypothese den Bootloader (Entschuldigung) direkt in den Arbeitsspeicher vom Host aus laden. Vielleicht kann man das direkt aus GDB machen, aber ich habe letztendlich den einfachen Weg gewählt: Ich kopierte die benötigte Datei auf den Raspberry, leitete auch den Port 4444 (Telnet von OpenOCD) über SSH und verwendete den Befehl load_image. Wenn Sie ihn ausführen, scheint alles eingefroren zu sein, aber in Wirklichkeit „es schläft nicht, es blinzelt nur langsam“: es lädt die Datei, macht es aber mit einer Geschwindigkeit von ein paar Kilobyte pro Sekunde.

Besonderheiten beim Setzen von Breakpoints

Wahrscheinlich haben sich viele darüber beim Debuggen gewöhnlicher Programme keine Gedanken gemacht, aber Breakpoints werden nicht immer hardwareseitig gesetzt. Manchmal besteht das Setzen eines Breakpoints darin, eine spezielle Anweisung vorübergehend an die richtige Stelle zu schreiben direkt in den Maschinencode. Zum Beispiel funktionierte das so mit dem Standardbefehl b in GDB. Das ergibt folgendes:

  • Ein Punkt kann nicht innerhalb der BootROM gesetzt werden, da der ROM
  • ein Breakpoint auf Code, der in den Arbeitsspeicher von der SD-Karte geladen wird, kann gesetzt werden, aber man muss warten, bis er geladen ist. Andernfalls überschreiben wir ein Stück Code nicht, sondern der Bootloader überschreibt unseren Breakpoint.

Ich bin mir sicher, dass man explizit nach hardwareseitigen Breakpoints fragen kann, aber deren Anzahl ist in jedem Fall begrenzt.

Schnelles Umgehen der BootROM

In der Anfangsphase des Debuggens entsteht oft der Wunsch, die BootROM zu ändern und es noch einmal zu versuchen. Aber es gibt ein Problem: Die BootROM ist Teil des Designs, das in die FPGA geladen wird, und ihr Syntheseprozess dauert nur wenige Minuten (und das nach der fast sofortigen Kompilierung des eigentlichen BootROM-Bildes aus C und Assembler…). Glücklicherweise geht alles viel schneller: die Vorgehensweise ist folgende:

  • bootrom.mif neu generieren (ich bin zu MIF anstelle von HEX gewechselt, da ich ständig Probleme mit HEX hatte und MIF das native Format von Altera ist)
  • in Quartus sagen Processing -> Update Memory Initialization File
  • im Punkt Assembler (in der linken Spalte Aufgaben) den Befehl Start again geben

Für alles zusammen — ein paar Dutzend Sekunden.

Vorbereitung der SD-Karte

Hier ist alles relativ einfach, aber man muss Geduld haben und etwa 14 GB Speicherplatz auf der Festplatte bereitstellen:

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

Danach muss eine leere, genauer gesagt, eine die nichts Nützliches enthält, SD-Karte eingelegt werden, und man führt aus:

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

… wo sdX — das Gerät, das der Karte zugeordnet ist. ACHTUNG: Die Daten auf der Karte werden gelöscht, überschrieben und überhaupt! Es ist wahrscheinlich nicht ratsam, die gesamte Erstellung unter sudo, weil dann alle Erstellungsartefakte zu root, und die Erstellung muss unter sudo ständig erfolgen.

Am Ende erhält man eine Karte, die in GPT mit vier Partitionen markiert ist, wobei eine davon FAT mit uEnv.txt und einem bootbaren Image im FIT-Format (es enthält mehrere Unterbilder, jedes mit seiner eigenen Ladeadresse) ist, während die andere Partition leer ist und für Ext4 für Linux formatiert werden soll. Zwei weitere Partitionen sind mysteriös: auf einer lebt U-Boot (seine Offset, so viel ich weiß, ist im BootROM festgelegt), auf der anderen scheinen seine Umgebungsvariablen zu wohnen, aber die benutze ich bisher nicht.

Stufe eins, BootROM

Volksspruch sagt: „Wenn es im Programmieren Tänze mit dem Tamburin gibt, so gibt es in der Elektronik noch einen mit einem Feuerlöscher“. Es geht nicht einmal darum, dass ich einmal fast die Platine verbrannt hätte, weil ich dachte: „Nun, GND ist ja derselbe niedrigere Pegel“ (anscheinend hätte ein Widerstand doch nicht geschadet...) Es geht eher darum, dass, wenn die Hände nicht von dort kommen, die Elektronik weiterhin Überraschungen bereithält: beim Löten eines Anschlusses auf die Platine konnte ich die Kontakte einfach nicht ordentlich verlöten – im Video sieht man, wie das Lötzinn sich ganz von selbst über die gesamte Verbindung verteilt, sobald man das Lötkolben hinlegt, bei mir jedoch war es einfach „wild“. Nun, vielleicht passte das Lötzinn nicht zur Temperatur des Lötkolbens, vielleicht noch etwas anderes… Kurz gesagt, als ich sah, dass ich schon ein Dutzend Kontakte hatte, gab ich auf und begann mit dem Debuggen. Und hier begann das mysteröse: Ich schloss RX/TX vom UART an, lade die Firmware – es zeigt an:

INIT
CMD0
ERROR

Nun, alles logisch – das SD-Kartenmodul hatte ich nicht angeschlossen. Ich korrigierte die Situation, lud die Firmware... und Stille... Was ich nicht alles überdachte, und die Lösung war einfach: einer der Pins des Moduls musste an VCC angeschlossen werden. In meinem Fall benötigte das Modul 5V für die Stromversorgung, also steckte ich ohne lange nachzudenken das Kabel, das vom Modul kam, auf die gegenüberliegende Seite der Platine. Am Ende war der schief gelötete Anschluss verzogen und Ich habe einfach den UART-Kontakt verloren. facepalm.jpg Im Grunde genommen sorgt ein "schlechtes Haupt für unruhige Beine", und krumme Hände für das Haupt...

Letztendlich habe ich in Minicom das lange Erwartete gesehen

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LADEN /

Darüber hinaus bewegt sich der Ladeindikator. Es erinnert mich direkt an die Schulzeit und das langsame Laden von MinuetOS von Diskette. Nur das Laufwerk quetscht nicht.

Das Problem ist, dass nach der BOOT-Meldung nichts passiert. Das bedeutet, es ist an der Zeit, sich über OpenOCD mit Raspberry zu verbinden, um GDB auf dem Host zu verwenden und zu sehen, was das ist.

Zuerst zeigte die Verbindung über GDB sofort, dass $pc (Program Counter, Adresse der aktuellen Anweisung) leidet unter einem Absturz in 0x0 – wahrscheinlich passiert das nach einem mehrfachen Fehler. Daher fügen wir sofort nach der Meldung hinzu BOOT einen endlosen Zyklus hinzu. Das wird ihn für eine kurze Zeit aufhalten...

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;
 }

So ein schlauer Code wird "zur Zuverlässigkeit" verwendet: Ich habe irgendwo gehört, dass anscheinend ein endloser Zyklus ein undefiniertes Verhalten ist, und hier wird der Compiler wahrscheinlich nicht darauf kommen. (Ich erinnere daran, dass 0x10000 sich BootROM befindet).

Teil 3: Wir laden Linux fast von der SD-Karte auf RocketChip

Eigentlich, was sollte man noch erwarten – raues Embedded, was gibt es für Quelltexte. Aber in dem Artikel habe der Autor C-Code debugged... Krex-Fex-Pex:

(gdb) file builds/zeowaa-e115/sdboot.elf
Ein Programm wird bereits debuggt.
Sind Sie sicher, dass Sie die Datei ändern möchten? (j oder n) j
Symbole werden von builds/zeowaa-e115/sdboot.elf gelesen...fertig.

Teil 3: Wir laden Linux fast von der SD-Karte auf RocketChip

Nur muss die originale Version im ELF-Format geladen werden, nicht die MIF-Datei oder die bin.

Jetzt kann man mit dem n-ten Versuch die Adresse erraten, wo die Ausführung fortgesetzt wird (das ist ein weiterer Grund, warum der Compiler nicht geraten haben sollte, dass die Schleife unendlich ist). Der Befehl

set variable $pc=0xADDR

erlaubt es, den Wert des Registers zur Laufzeit zu ändern (in diesem Fall die Adresse der aktuellen Anweisung). Mit dieser Methode können auch Werte, die im Speicher geschrieben sind (und speicherabbildende Register), geändert werden.

Letztendlich kam ich zu dem Schluss (nicht sicher, ob korrekt), dass wir "das Abbild der SD-Karte eines anderen Systems" haben, und es nötig ist, nicht zum Anfang der geladenen Daten überzugehen, sondern um 0x89800 ein paar Byte weiter:

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

Vielleicht hat sich auch ausgewirkt, dass ich anstelle der unnötigen 4Gb-Karte nur eine 2Gb-Karte zur Hand hatte und durch Ausprobieren die Datei im Makefile ersetzt habe. DEMO_END=11718750 auf DEMO_END=3078900 (sucht nicht nach einem Sinn in der konkreten Bedeutung – den gibt es nicht, einfach jetzt passt das Bild auf die Karte).

Level zwei, U-Boot

Jetzt fallen wir immer noch, aber landen schon an der Adresse 0x0000000080089a84. Hier muss ich zugeben: Das Ganze läuft nicht „mit allen Halts“, sondern wird teilweise „nachträglich“ geschrieben, deshalb habe ich hier bereits die richtige dtb-Datei von unserem SoC eingefügt und die Einstellungen korrigiert. HiFive_U-Boot Variable CONFIG_SYS_TEXT_BASE=0x80089800 (anstatt 0x08000000), damit die Ladeadresse mit der tatsächlichen übereinstimmt. Laden wir nun das Abbild der nächsten Ebene:

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

Und sehen:

   │304     
/*                                               │
   │305      * Trap-Eintrag                                    │
   │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)                      │

Übrigens springen wir zwischen den Zeilen 308 und 309. Und das ist nicht verwunderlich, wenn man bedenkt, dass in $sp der Wert 0xfffffffe31cdc0a0. Leider „entweicht“ er ständig aufgrund der Zeile 307. Deshalb versuchen wir, einen Haltepunkt an trap_entry, zu setzen, und dann wieder zu dem 0x80089800 (Einstiegspunkt U-Boot) zu gehen und hoffen, dass es keine richtige Registeranpassung vor dem Übergang benötigt… Es scheint zu funktionieren:

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

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

So schlecht ist der Stackzeiger wirklich nicht: Er zeigt ganz woanders hin (es sei denn, wir haben noch keine Adressübersetzung, aber wir hoffen auf die einfache Variante).

Lassen Sie uns versuchen, den Zeiger auf 0x881cf950zu ersetzen. Letztendlich kommen wir dazu, dass handle_trap immer wieder aufgerufen wird und wir in _exit_trap mit dem Argument epc=2148315240 (im dezimalen Format):

(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

Setze Breakpoint auf strnlen, setzen wir fort und sehen:

(gdb) bt
#0 strnlen (s=s@entry=0x10060000 "", count=18446744073709551615) at lib/string.c:283
#1 0x00000000800cc14c in string (buf=buf@entry=0x881cbd4c "", end=end@entry=0x881cc15c "", s=0x10060000 "", field_width=, precision=, flags=) at lib/vsprintf.c:265
#2 0x00000000800cc63c in vsnprintf_internal (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=0x800d446e "s , epc x , ra lxn", fmt@entry=0x800d4458 "exception code: %d , %s , epc x , ra lxn", args=0x881cc1a0,
 args@entry=0x881cc188) at lib/vsprintf.c:619
#3 0x00000000800cca54 in vsnprintf (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc x , ra lxn", args=args@entry=0x881cc188) at lib/vsprintf.c:710
#4 0x00000000800cca68 in vscnprintf (buf=buf@entry=0x881cbd38 "exception code: 5 , ", size=size@entry=1060, fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc x , ra lxn", args=args@entry=0x881cc188) at lib/vsprintf.c:717
#5 0x00000000800ccb50 in printf (fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc x , ra lxn") at lib/vsprintf.c:792
#6 0x000000008008a9f0 in _exit_trap (regs=, epc=2148315240, code=) at arch/riscv/lib/interrupts.c:92
#7 handle_trap (mcause=, epc=, regs=) at arch/riscv/lib/interrupts.c:55
#8 0x0000000080089b10 in trap_entry () at /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S:343
Backtrace stopped: frame did not save the PC

Es sieht so aus, _exit_trap will es Debugging-Informationen über die aufgetretene Ausnahme ausgeben, aber das funktioniert nicht. Irgendetwas scheint mit unseren Quellcodes wieder nicht zu stimmen. setze Verzeichnisse ../freedom-u-sdk/HiFive_U-Boot/ Oh! Jetzt werden sie angezeigt!

Nun, lassen Sie uns noch einmal starten und wir werden die Ursache des ursprünglichen Problems, das den ersten Fehler verursacht hat, anhand des Stack-Traces sehen (mcause == 5). Wenn ich richtig verstanden habe, was hier steht, hier auf Seite 37, bedeutet diese Ausnahme Load-Zugriffsfehler. Der Grund scheint zu sein, dass hier

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

call_board_init_f:
    li  t0, -16
    li  t1, CONFIG_SYS_INIT_SP_ADDR
    and sp, t1, t0  /* erzwinge 16 Byte-Ausrichtung */

#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       /* springe zu board_init_f() */

$sp den inkorrekten Wert hat, und innerhalb von board_init_f_init_reserve ein Fehler auftritt. Es scheint, dass dies der Übeltäter ist: die Variable mit dem klaren Namen CONFIG_SYS_INIT_SP_ADDR. Sie ist in der Datei HiFive_U-Boot/include/configs/HiFive-U540.h. Irgendwann dachte ich sogar, vielleicht sollte ich den Bootloader für die CPU einfach weglassen – vielleicht wäre es einfacher, die CPU etwas zu ändern? Aber dann bemerkte ich, dass es mehr wie ein Artefakt von nicht ganz abgeschlossenen Einstellungen für eine andere Speicherkonfiguration aussieht, und ich könnte es so versuchen:#if 0diff --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 /* SDRAM Bank #1 */ -#define PHYS_SDRAM_1 - (PHYS_SDRAM_0 + PHYS_SDRAM_0_SIZE) /* SDRAM Bank #2 */ -#define PHYS_SDRAM_0_SIZE 0x80000000 /* 2 GB */ -#define PHYS_SDRAM_1_SIZE 0x10000000 /* 256 MB */ +#define PHYS_SDRAM_0_SIZE 0x40000000 /* 1 GB */ #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_0 #endif /* @@ -81,7 +78,7 @@ #define CONSOLE_ARG "console=ttyS0,115200 "/* Init Stack Pointer */ -#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 /* partway up SDRAM */

Irgendwann hat die Anzahl der Notlösungen

technologischen Befestigungen eine kritische Schwelle erreicht. Nach einigem Überlegen kam ich zu der Notwendigkeit, einen korrekten Port für meine Platine zu erstellen. Dafür muss ich eine bestimmte Anzahl von Dateien kopieren und an unsere Konfiguration anpassen. Nun, ungefähr so viel

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 +0300Erste Unterstützung für das Zeowaa A-E115FB BoardM 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

Details können im

Wie sich herausstellte, haben die Register einiger Geräte auf diesem SiFive-Board andere Adressen. Außerdem stellte sich heraus, dass U-Boot mit einem bereits aus dem Linux-Kernel bekannten Kconfig-Mechanismus konfiguriert wird – man kann zum Beispiel anordnen das Repository.

make menuconfig , und vor Ihnen erscheint eine praktische Textschnittstelle mit der Anzeige der Parameterbeschreibungen., und vor Ihnen erscheint eine benutzerfreundliche Textoberfläche mit einer Beschreibung der Parameter ? Und so weiter. Im Allgemeinen, indem ich aus den Beschreibungen der zwei Platinen die Beschreibung der dritten erstellt und alle pompösen PLL-Umeinstellungen weggelassen habe (offenbar hat das irgendwie mit der Steuerung vom Hostcomputer über PCIe zu tun, aber das ist nicht sicher), erhielt ich eine bestimmte Firmware, die mir bei passendem Wetter auf dem Mars über UART eine Nachricht ausgegeben hat, aus welchem Commit-Hash sie erstellt wurde und wie viel DRAM ich habe (aber diese Information habe ich selbst im Header angegeben).

Es ist nur schade, dass die Platine danach normalerweise auf den Prozessor-JTAG nicht mehr reagierte, und das Booten von der SD-Karte — ist in meiner Konfiguration leider nicht schnell. Andererseits gab das BootROM manchmal die Nachricht aus, dass FEHLER, das Booten nicht gelungen ist, und sofort sprang U-Boot heraus. Da wurde mir klar: Offenbar wird nach dem Neustart des Bitstreams im FPGA der Speicher nicht überschrieben, er hat nicht genug Zeit, um „abrüchtig“ zu werden usw. Kurz gesagt, ich kann einfach, sobald die Nachricht erscheint, LADEN / mit dem Debugger verbinden und befehlen set variable $pc=0x80089800, wodurch ich diesen langen Ladevorgang umgehe (naturgemäß unter der Annahme, dass es beim letzten Mal früh genug abgestürzt ist, und nicht bereits etwas über den Originalcode geladen hat).

Übrigens, ist es normal, dass der Prozessor komplett einfriert und der JTAG-Debugger mit den Nachrichten nicht verbinden kann?

Fehler: Kann hart 0 nicht anhalten
Fehler:   dmcontrol=0x80000001
Fehler:   dmstatus =0x00030c82

Moment mal! Das habe ich schon mal gesehen! Ähnliches passiert bei einem Deadlock in TileLink, und dem Autor des Speichercontrollers vertraue ich irgendwie nicht — habe es selbst geschrieben… Plötzlich, nach dem ersten erfolgreichen Neuzusammenbau des Prozessors nach der Bearbeitung des Controllers, sah ich:

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LADEN
BOOT

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

DRAM:  1 GiB
MMC:
BEFORE LOAD ENVBEFORE FDTCONTROLADDRBEFORE LOADADDRIn:    serial
Out:   serial
Err:   serial
Drücken Sie eine beliebige Taste, um das Autoboot zu stoppen:  3

Auf diese seltsame Zeile bevor In: serial Ignorieren Sie das – ich habe versucht, auf einem festhängenden Prozessor zu verstehen, ob es mit der Umgebung funktioniert. Was bedeutet "Es hängt bereits seit zehn Minuten"? Es hat es immerhin geschafft, sich zu relocieren und zum Bootmenü zu wechseln! Ein kurzer Einschub: Obwohl U-Boot in den ersten 2^24 Byte von der SD-Karte geladen wird, kopiert es sich, nachdem es gestartet wurde, an einen anderen Ort, entweder an die in der Konfigurationsüberschrift aufgezeichnete Adresse oder einfach in die hohen Adressen des RAMs, führt die Relokation der ELF-Symbole durch und übergibt die Kontrolle dorthin. Also: Es scheint, als hätten wir diese Stufe bestanden und als Bonus einen Prozessor erhalten, der danach nicht mehr festhängt.

Und warum funktioniert der Timer nicht? Es scheint, als ob die Uhr aus irgendeinem Grund überhaupt nicht läuft…

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

Was wäre, wenn ich die Zeiger manuell drehe?

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

Dann:

Drücken Sie eine beliebige Taste, um den automatischen Bootvorgang zu stoppen:  0
MMC_SPI: 0 bei 0:1 hz 20000000 modus 0

Fazit: Die Uhr läuft nicht. Wahrscheinlich funktioniert auch die Tastatureingabe wegen dessen nicht:

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:
        /* Erstes Zeichen der ANSI Escape-Sequenz 'e' */
        if (c == 'e') {
            *esc = 1;
            *key = KEY_NONE;
        }
        break;
    case 1:
        /* Zweites Zeichen von ANSI '[' */
        if (c == '[') {
...

Das Problem war, dass ich es ein wenig übertrieben habe: Ich habe dem Prozessor in der Konfiguration den Schlüssel hinzugefügt:

  case DTSTimebase => BigInt(0)

… basierend auf dem, was im Kommentar stand: "Wenn Sie es nicht wissen, lassen Sie es auf 0." Und tatsächlich WithNBigCores setzte er es genau auf 1MHz (wie übrigens auch in der U-Boot-Konfiguration angegeben). Aber ich war, verdammtes Wetter, sorgfältig und penibel: Dort weiß ich nicht, hier 25MHz! Am Ende funktioniert nichts mehr. Ich habe meine „Verbesserungen“ entfernt und…

Drücken Sie eine beliebige Taste, um den automatischen Bootvorgang zu stoppen:  0
MMC_SPI: 0 bei 0:1 hz 20000000 modus 0
## Unbekannter Typ der Partitions-Tabelle 0
libfdt fdt_path_offset() gab FDT_ERR_NOTFOUND zurück
** Keine Partitionstabelle - mmc 0 **
## Info: Eingabedaten Größe = 34 = 0x22
Lade uEnv.txt boot2...
## Fehler: "boot2" nicht definiert
HiFive-Unleashed #

Man kann sogar Befehle eingeben! Zum Beispiel, nachdem ich ein wenig herumgebastelt habe, kann man schließlich auf die Idee kommen, einzugeben mmc_spi 1 10000000 0; mmc part, die SPI-Frequenz von 20MHz auf 10MHz zu reduzieren. Warum? Nun, im Konfigurationsfile war die maximale Frequenz von 20MHz angegeben, und diese steht dort immer noch. Aber, soweit ich verstanden habe, funktionieren die Schnittstellen hier zumindest so: Der Code teilt die Frequenz des Hardwaremoduls (bei mir sind es überall 25MHz) durch die Zielwertfrequenz und setzt den erhaltenen Wert als Teiler in das entsprechende Steuerregister. Das Problem ist, dass, wenn es für den 115200Hz UART irgendwohin passt, dann wird die vollständige Teilung von 25000000 durch 20000000 1 ergeben, das heißt, es wird mit 25MHz arbeiten. Vielleicht ist das in Ordnung, aber wenn Einschränkungen gesetzt werden, dann ist das sicher für jemanden notwendig (aber das ist nicht sicher)... Insgesamt ist es einfacher, das einzustellen und weiterzumachen – weit und, leider, für lange Zeit. 25MHz, das ist kein Core i9.

Konsolenausgabe

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

Partitionskarte für MMC-Gerät 0  --   Partitionstyp: EFI

Teil    Start LBA       Ende LBA         Name
        Attribute
        Typ GUID
        Partition GUID
  1     0x00000800      0x0000ffde      "Vfat Boot"
        attrs:  0x0000000000000000
        typ:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        typ:   Daten
        guid:   76bd71fd-1694-4ff3-8197-bfa81699c2fb
  2     0x00040800      0x002efaf4      "root"
        attrs:  0x0000000000000000
        typ:   0fc63daf-8483-4772-8e79-3d69d8477de4
        typ:   Linux
        guid:   9f3adcc5-440c-4772-b7b7-283124f38bf3
  3     0x0000044c      0x000007e4      "uboot"
        attrs:  0x0000000000000000
        typ:   5b193300-fc78-40cd-8002-e86c45580b47
        guid:   bb349257-0694-4e0f-9932-c801b4d76fa3
  4     0x00000400      0x0000044b      "uboot-env"
        attrs:  0x0000000000000000
        typ:   a09354ac-cd63-11e8-9aff-70b3d592f0fa
        guid:   4db442d0-2109-435f-b858-be69629e7dbf
libfdt fdt_path_offset() gab FDT_ERR_NOTFOUND zurück
2376 Bytes in 0 ms gelesen
Starte uEnv.txt boot2...
15332118 Bytes in 0 ms gelesen
## Kernel vom FIT-Image bei 90000000 laden ...
   Verwende 'config-1' Konfiguration
   Versuche 'bbl' Kernel-Subbild
     Beschreibung:  BBL/SBI/riscv-pk
     Typ:         Kernel-Image
     Kompression:  unkomprimiert
     Datenstart:   0x900000d4
     Datengröße:    74266 Bytes = 72.5 KiB
     Architektur: RISC-V
     OS:           Linux
     Ladeadresse: 0x80000000
     Einstiegspunkt:  0x80000000
     Hash-Algorithmus: sha256
     Hash-Wert:   28972571467c4ad0cf08a81d9cf92b9dffc5a7cb2e0cd12fdbb3216cf1f19cbd
   Überprüfung der Hash-Integrität ... sha256+ OK
## FDT vom FIT-Image bei 90000000 laden ...
   Verwende 'config-1' Konfiguration
   Versuche 'fdt' fdt-Subbild
     Beschreibung:  nicht verfügbar
     Typ:         Flat Device Tree
     Kompression:  unkomprimiert
     Datenstart:   0x90e9d31c
     Datengröße:    6911 Bytes = 6.7 KiB
     Architektur: RISC-V
     Ladeadresse: 0x81f00000
     Hash-Algorithmus: sha256
     Hash-Wert:   10b0244a5a9205357772ea1c4e135a4f882409262176d8c7191238cff65bb3a8
   Überprüfung der Hash-Integrität ... sha256+ OK
   Lade fdt von 0x90e9d31c nach 0x81f00000
   Starte mit dem fdt-Blob bei 0x81f00000
## Ladbare von FIT-Image bei 90000000 laden ...
   Versuche 'kernel' Ladbare-Subbild
     Beschreibung:  Linux-Kernel
     Typ:         Kernel-Image
     Kompression:  unkomprimiert
     Datenstart:   0x900123e8
     Datengröße:    10781356 Bytes = 10.3 MiB
     Architektur: RISC-V
     OS:           Linux
     Ladeadresse: 0x80200000
     Einstiegspunkt:  nicht verfügbar
     Hash-Algorithmus: sha256
     Hash-Wert:   72a9847164f4efb2ac9bae736f86efe7e3772ab1f01ae275e427e2a5389c84f0
   Überprüfung der Hash-Integrität ... sha256+ OK
   Lade Ladbare von 0x900123e8 nach 0x80200000
## Ladbare von FIT-Image bei 90000000 laden ...
   Versuche 'ramdisk' Ladbare-Subbild
     Beschreibung:  buildroot initramfs
     Typ:         RAMDisk-Image
     Kompression:  gzip-komprimiert
     Datenstart:   0x90a5a780
     Datengröße:    4467411 Bytes = 4.3 MiB
     Architektur: RISC-V
     OS:           Linux
     Ladeadresse: 0x82000000
     Einstiegspunkt:  nicht verfügbar
     Hash-Algorithmus: sha256
     Hash-Wert:   883dfd33ca047e3ac10d5667ffdef7b8005cac58b95055c2c2beda44bec49bd0
   Überprüfung der Hash-Integrität ... sha256+ OK
   Lade Ladbare von 0x90a5a780 nach 0x82000000

Okay, wir haben ein neues Level erreicht, aber es hängt immer noch. Manchmal gibt es auch Ausnahmen. Man kann mcause sehen, indem man den Code an der angegebenen Adresse abfängt. $pc und nach si auf trap_entry. Der U-Boot-Handler kann nur für mcause = 0..4 ausgeben, also bereiten Sie sich darauf vor, sich wegen fehlerhafter Ladevorgänge in eine Endlosschleife zu begeben. Ich habe in die Konfiguration geschaut und überlegt, was ich verändert habe, und ich erinnerte mich: dort ist es in conf/rvboot-fit.txt geschrieben:

fitfile=image.fit
# darunter muss viel mit dem übereinstimmen, was im FIT (ugha) steht

Nun, lassen Sie uns alle Dateien anpassen, ersetzen wir die Kernel-Befehlszeile ungefähr so, da es Hinweise gibt, dass SIF0 — das ist die Ausgabe irgendwo über PCIe:

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

Und außerdem ändern wir den Hash-Algorithmus von SHA-256 auf MD5: Krypto-Sicherheit benötige ich nicht (zumal beim Debugging), es dauert ewig, und um Fehler bei der Integrität beim Laden zu finden, reicht MD5 völlig aus. Was haben wir also am Ende? Wir sind viel schneller durch die vorherige Stufe gekommen (aufgrund der einfacheren Hash-Berechnung), und die nächste hat sich geöffnet:

...
   Überprüfung der Hash-Integrität ... md5+ OK
   Laden von Ladeprogrammen von 0x90a5a758 nach 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() gab FDT_ERR_NOTFOUND zurück
chosen {
        linux,initrd-end = ;
        linux,initrd-start = ;
        riscv,kernel-end = ;
        riscv,kernel-start = ;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
   Laden des Kernel-Images ... OK
Kernel wird gebootet in
3

Nur die Uhren ticken nicht...

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

Ups, es scheint, dass das Zeitkorrektur-Experiment ein Placebo war, obwohl ich damals dachte, es hätte geholfen. Nein, es muss natürlich repariert werden, aber lassen Sie uns zuerst die Zeiger manuell drehen und sehen, was passiert:

0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Fortsetzen.
^C
Programm erhielt Signal SIGINT, Unterbrechung.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Fortsetzen.
^C
Programm erhielt Signal SIGINT, Unterbrechung.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Fortsetzen.

In der Zwischenzeit...

   Laden des Kernel-Images ... OK
Kernel wird gebootet in
3
2
1
0
## Anwendung starten bei 0x80000000 ...

Nein, ich werde die Zeitkorrektur automatisieren — sonst könnte er versuchen, den Timer zu kalibrieren!

Die Adresse der aktuellen Anweisung zeigt inzwischen irgendwo hin.

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

innerhalb des geladenen Berkeley Boot Loaders. Mich stört dabei persönlich die Erwähnung htif — das Host-Interface, das für den tethered Start des Kernels verwendet wird (d.h. in Kooperation mit dem Host-ARM), ich hatte standalone angenommen. Wenn man jedoch diese Funktion im Quellcode findet, sieht man, dass es nicht so schlimm ist:

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"); }
  }
}

Quest: starte die Uhr

Die Suche nach Registern im CLINT führt uns zu

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

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

Der sich an RTC anschließt, oder im geheimnisvollen MockAON, dessen Bedeutung ich anfangs überdacht habe: „Was haben wir hier? Unklar? Ausschalten!“ Da mir bis jetzt unklar ist, welche Taktmagie dort passiert, werde ich diese Logik einfach neu implementieren in System.scala:

  val rtcDivider = RegInit(0.asUInt(16.W)) // zur Sicherheit unterstütze ich bis 16GHz, ich bin optimistisch :)
  val mhzInt = p(DevKitFPGAFrequencyKey).toInt
  // Nehmen wir an, die Frequenz beträgt eine ganze Zahl in Megahertz
  rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
  outer.clintOpt.foreach { clint =>
    clint.module.io.rtcTick := rtcDivider === 0.U
  }

Auf dem Weg zum Linux-Kernel

Hier hat sich die Erzählung ohnehin schon lange hingezogen und wurde ein wenig eintönig, daher werde ich es grob umreißen:

BBL erwartete das Vorhandensein eines FDT an der Adresse 0xF0000000, aber ich hatte das bereits korrigiert! Also, suchen wir weiter… Gefunden in HiFive_U-Boot/arch/riscv/lib/boot.c, ersetzt durch 0x81F00000, angegeben in der U-Boot-Startkonfiguration.

Dann beschwerte sich BBL, dass kein Speicher vorhanden sei. Mein Weg führte zur Funktion mem_prop, was in riscv-pk/machine/fdt.c: dort erfuhr ich, dass der Knoten fdt ram als device_type = "memory" — später muss vielleicht der Prozessor-Generator angepasst werden, aber im Moment schreibe ich es einfach von Hand – ich habe diese Datei sowieso manuell übertragen.

Jetzt habe ich eine Nachricht erhalten (im formatierten Format mit Zeilenumbrüchen):

Das ist bb'ls dummy_payload. Um einen echten Kernel zu booten, konfiguriere bbl neu
mit dem Flag --with-payload=PATH und baue bbl neu. Alternativ kann
bbl im Nur-Firmware-Modus verwendet werden, indem Gerätbaumknoten für ein externes Payload hinzugefügt werden
und die QEMU-Optionen -bios und -kernel verwendet werden.

Es scheinen die Optionen korrekt angegeben zu sein riscv,kernel-start und riscv,kernel-end im DTB, aber es werden Nullen geparst. Debugging query_chosen hat gezeigt, dass BBL versucht, eine 32-Bit-Adresse zu parsen, aber ihm begegnet ein Paar <0x0 0xADDR>, und der erste Wert scheint die niedrigeren Bits zu sein. Ich habe in den Abschnitt chosen

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

und die Generierung der Werte korrigiert: nicht hinzufügen 0x0 als erstes Element.

Diese 100500 einfachen Schritte ermöglichen es dir, einfach zu sehen, wie der Pinguin abstürzt:

Versteckter Text

   Integritätsprüfung des Hashes ... md5+ OK
   Ladearte von 0x90a5a758 bis 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
        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() gab FDT_ERR_NOTFOUND zurück
chosen {
        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";
};
   Lade Kernel-Image ... OK
Kernel booten in
3
2
1
0
## Anwendung starten bei 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: Ignoriere Speicherbereich 0x80000000 - 0x80200000
[    0.000000] Linux-Version 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc-Version 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Mi 3. Jul 21:29:21 MSK 2019
[    0.000000] bootconsole [early0] aktiviert
[    0.000000] Initial ramdisk bei: 0x(____ptrval____) (16777216 bytes)
[    0.000000] Zonenbereiche:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Beweglicher Zonenstart für jeden Knoten
[    0.000000] Frühe Speicherbereichszuweisungen
[    0.000000]   Knoten   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Initmem einrichten Knoten 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Auf Knoten 0 insgesamt Seiten: 261632
[    0.000000]   DMA32-Zone: 3577 Seiten verwendet für Memmap
[    0.000000]   DMA32-Zone: 0 Seiten reserviert
[    0.000000]   DMA32-Zone: 261632 Seiten, LIFO-Charge: 63
[    0.000000] Software IO TLB: gemappt [mem 0xbb1fc000-0xbf1fc000] (64MB)

(Das Emblem wird von BBL ausgegeben, und das, was mit Zeitstempeln angezeigt wird, ist der Kernel.)

Glücklicherweise weiß ich nicht, wie es überall ist, aber auf RocketChip kann man beim Anschluss eines Debuggers über JTAG direkt Traps abfangen – der Debugger stoppt genau an dieser Stelle.

Programm hat Signal SIGTRAP empfangen, Trace-/Breakpoint-Trap.
0xffffffe0000024ca in ?? ()
(gdb) bt
#0  0xffffffe0000024ca in ?? ()
Backtrace gestoppt: vorheriger Frame identisch mit diesem Frame (beschädigter Stack?)
(gdb) file work/linux/vmlinux
Ein Programm wird bereits debuggt.
Sind Sie sicher, dass Sie die Datei ändern möchten? (y oder n) y
Lese Symbole aus work/linux/vmlinux...fertig.
(gdb) bt
#0  0xffffffe0000024ca in setup_smp () bei /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75
#1  0x0000000000000000 in ?? ()
Backtrace gestoppt: Frame hat nicht den PC gespeichert.

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); // < SIE SIND HIER
}

Wie in dem alten Witz gesagt wurde, CPU nicht gefunden, Softwareemulation läuft. Oder nicht running. Verirrt im einzigen Kern des Prozessors.

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

Ein guter Kommentar in linux/arch/riscv/kernel/setup.c — eine Art Zaunstreichen nach der Methode von Tom Sawyer. Insgesamt gab es heute scheinbar keine Gewinner, der Preis wird auf die nächste Runde verschoben...

Damit schlage ich vor, den ohnehin schon langen Artikel zu beenden.

Fortsetzung folgt. Darin wird es einen Kampf mit einem hinterhältigen Fehler geben, der sich versteckt, wenn man sich ihm langsam mit singlestep nähert.

Text-Screencast des Bootvorgangs (externer Link):
Teil 3: Wir laden Linux fast von der SD-Karte auf RocketChip

Quelle: habr.com

60GB SSD 8Gb DDR4