Parte 3: Stiamo quasi caricando Linux da una scheda SD su RocketChip

Parte 3: Stiamo quasi caricando Linux da una scheda SD su RocketChip In parte precedente è stato realizzato un controllore della memoria più o meno funzionante, o meglio, una wrapper sopra l'IP Core di Quartus, che funge da adattatore per TileLink. Oggi nella rubrica "Portiamo RocketChip su una scheda cinese poco conosciuta con Cyclone" vedrete una console funzionante. Il processo si è un po' allungato: pensavo di avviare rapidamente Linux e di andare avanti, ma non è andata così. In questa parte propongo di dare un'occhiata al processo di avvio di U-Boot, BBL, e ai timidi tentativi del kernel Linux di inizializzarsi. Ma c'è una console — quella di U-Boot, e abbastanza avanzata, con molte delle cose che ci si aspetta da una console completa.

Nella parte hardware sarà aggiunta una scheda SD, collegata tramite interfaccia SPI, oltre a UART. Nella parte software il BootROM sarà sostituito con xip in sdboot e, in effetti, saranno aggiunte le seguenti fasi di avvio (sulla scheda SD).

Affinamento della parte hardware

Quindi, compito: passare al nucleo "grande" e collegare UART (da Raspberry) e l'adattatore SD (è stata utilizzata una scheda da Catalex con sei pin: GND, VCC, MISO, MOSI, SCK, CS).

In linea di massima, tutto è stato piuttosto semplice. Ma prima di realizzarlo, mi sono un po' confuso: dopo l'ultima volta ho pensato che dovessi semplicemente aggiungere a Sistema qualcosa come HasPeripheryUART (e implementarlo di conseguenza), lo stesso per la scheda SD — e tutto sarebbe stato pronto. Poi ho deciso di vedere come fosse implementato in un design "serio". Allora, cosa abbiamo di serio qui? Arty, a quanto pare, non va bene — rimane il mostro unleahshed.DevKitConfigs. E improvvisamente ho scoperto che ci sono overlay ovunque, che vengono aggiunti tramite parametri con chiavi. Intuisco che probabilmente è molto flessibile e configurabile, ma vorrei poter avviare almeno qualcosa per cominciare... E per caso non avete qualcosa di simile, solo più semplice e malandato?.. Qui mi sono imbattuto in vera.iofpga.FPGAChip per il FPGA Microsemi e subito l'ho smontato in citazioni, cercando di fare la mia implementazione per analogia, dato che qui è più o meno tutto il "cablaggio della scheda madre" in un unico file.

Si è rivelato, infatti, che bisogna semplicemente aggiungere a System.scala le righe

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

La riga nel corpo della classe Sistema aggiunge informazioni sulla frequenza a cui opera questa parte del nostro SoC nel file dts. Per quanto ho capito, DTS/DTB è una sorta di analogia statica della tecnologia plug-and-play per dispositivi embedded: l'albero di descrizione dts viene compilato in un file binario dtb e passato dal bootloader al kernel affinché possa configurare correttamente l'hardware. È interessante notare che senza la riga con tlclock si sintetizza tutto perfettamente, ma non sarà possibile compilare BootROM (ricordo che ora sarà già sdboot) - durante il processo di compilazione analizza il file dts e crea un header con la macro TL_CLK, grazie alla quale potrà configurare correttamente i divisori di frequenza per le interfacce esterne.

Sarà anche necessario apportare alcune modifiche alla "traccia":

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

Le catene di registri, a dire il vero, sono state aggiunte semplicemente per analogia con alcuni altri punti del codice originale. È probabile che debbano proteggere da metastabilità. Forse in alcuni blocchi ci sia già una loro protezione, ma per iniziare si desidera far girare tutto almeno "a un buon livello". Una domanda che mi interessa di più è perché MISO e MOSI siano su dq? Ответа я пока так и не нашёл, но, похоже, остальной код рассчитывает именно на такое подключение.

Fisicamente, ho semplicemente assegnato i pin del design ai contatti liberi sul connettore e ho spostato il jumper per la selezione della tensione su 3.3V.

Adattatore SD

Vista dall'alto:

Parte 3: Stiamo quasi caricando Linux da una scheda SD su RocketChip

Vista dal basso:

Parte 3: Stiamo quasi caricando Linux da una scheda SD su RocketChip

Debugging della parte software: strumenti

Iniziamo a parlare degli strumenti di debug disponibili e delle loro limitazioni.

Minicom

Innanzitutto, avremo bisogno di un modo per leggere ciò che il bootloader e il kernel stanno stampando. Per questo, su Linux (in questo caso, su quello che è sul RaspberryPi) avremo bisogno del programma Minicom. In generale, qualsiasi programma per gestire la porta seriale andrà bene.

Si noti che all'avvio il nome del dispositivo della porta deve essere specificato come -D /dev/ttyS0 — dopo l'opzione -D. E l'informazione principale: per uscire usate Ctrl-A, X. In realtà, ho avuto un caso in cui questa combinazione non ha funzionato — allora si può semplicemente dire da una sessione SSH adiacente killall -KILL minicom.

C'è anche un'altra caratteristica. In particolare sul RaspberryPi ci sono due UART, e entrambe le porte possono già essere configurate per qualcosa: una per il Bluetooth, attraverso l'altra la console del kernel viene visualizzata per impostazione predefinita. Fortunatamente, questo comportamento può essere riconfigurato seguendo questo manuale.

Scrittura in memoria

Durante il debug, per verificare l'ipotesi, a volte dovevo caricare il bootloader (scusa) direttamente nella RAM dall'host. Forse si può fare direttamente da GDB, ma alla fine ho scelto la soluzione più semplice: ho copiato il file necessario sul Raspberry, ho inoltrato anche la porta 4444 tramite SSH (telnet da OpenOCD) e ho usato il comando load_image. Quando lo esegui, sembra che tutto si sia bloccato, ma in realtà «non dorme, sta solo lampeggiando lentamente»: sta caricando il file, ma lo fa a una velocità di un paio di kilobyte al secondo.

Caratteristiche dell'impostazione dei breakpoint

Probabilmente molti non ci hanno mai pensato durante il debug di programmi normali, ma i breakpoint non sempre vengono impostati a livello hardware. A volte impostare un breakpoint consiste nel sovrascrivere temporaneamente un'istruzione speciale nel punto giusto direttamente nel codice macchina. Ad esempio, così ha funzionato il mio comando standard b in GDB. Ecco cosa ne deriva:

  • non si può impostare un breakpoint all'interno del BootROM, perché il ROM
  • può impostare un breakpoint sul codice caricato nella RAM dalla scheda SD, ma bisogna aspettare che venga caricato. Altrimenti non siamo noi a sovrascrivere un pezzo di codice, ma il bootloader sovrascriverà il nostro breakpoint

Sono sicuro che si può richiedere esplicitamente di usare breakpoint hardware, ma ce ne sono comunque in numero limitato.

Sostituzione rapida del BootROM

Nella fase iniziale del debug spesso sorge il desiderio di correggere il BootROM e riprovare. Ma c'è un problema: il BootROM è parte del design, caricato nella FPGA, e la sua sintesi richiede alcuni minuti (e questo soltanto dopo la quasi istantanea compilazione dell'immagine del BootROM da C e Assembler…). Fortunatamente, in realtà tutto molto più velocemente: la sequenza di azioni è la seguente:

  • rigenerare bootrom.mif (sono passato a MIF invece di HEX, perché con HEX ho sempre avuto problemi, mentre MIF è il formato nativo di Altera)
  • in Quartus dire Processing -> Update Memory Initialization File
  • nel punto Assembler (nella colonna di sinistra Tasks) dare il comando Start again

Il tutto impiega solo una manciata di secondi.

Preparazione della scheda SD

Qui tutto è relativamente semplice, ma è necessario avere pazienza e circa 14 GB di spazio su disco:

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

Dopo di che è necessario inserire una scheda SD pulita, ovvero che non contenga nulla di utile, e eseguire

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

… dove sdX è il dispositivo assegnato alla scheda. ATTENZIONE: i dati sulla scheda verranno eliminati, sovrascritti e così via! Difficilmente ha senso effettuare l'intera compilazione da sudo, perché altrimenti tutti gli artefatti della compilazione apparterranno a root, e la compilazione dovrà essere effettuata da sudo continuamente.

Alla fine si ottiene una scheda partizionata in GPT con quattro partizioni, in una delle quali c'è FAT con uEnv.txt e un'immagine di avvio in formato FIT (contiene diverse sottoimmagini, ciascuna con il proprio indirizzo di avvio), l'altra partizione è pulita, e si prevede di formattarla in Ext4 per Linux. Le altre due partizioni sono misteriose: in una vive U-Boot (il suo offset, per quanto ne so, è codificato in BootROM), nell'altra sembrano vivere le sue variabili ambientali, ma per ora non le utilizzo.

Livello primo, BootROM

La saggezza popolare dice: «Se nella programmazione ci sono danze con tamburi, nell'elettronica ci sono anche danze con gli estintori». Non è nemmeno che una volta stavo per bruciare una scheda, pensando che «Beh, GND è lo stesso di un livello basso» (probabilmente, una resistenza non avrebbe guastato…) Si tratta piuttosto del fatto che se le mani non arrivano da dove dovrebbero, l'elettronica continua a riservare sorprese: saldando un connettore sulla scheda, non sono riuscito a saldare correttamente i contatti — nei video mostrano come la saldatura si distribuisce da sola su tutta la connessione, basta toccare con il saldatore, a me invece si «accumulava» in modo casuale. Beh, forse la saldatura non era adatta alla temperatura del saldatore, forse qualcos'altro… Insomma, vedendo che avevo già una dozzina di contatti, ho rinunciato e ho iniziato a fare debug. E qui è iniziato il misterioso: ho collegato RX/TX dall'UART, carico il firmware — e scrive

INIT
CMD0
ERROR

Ecco, tutto logico — non ho collegato il modulo della scheda SD. Correggo la situazione, carico il firmware… E silenzio… Ho pensato di tutto, ma la cosa era semplice: uno dei pin del modulo doveva essere collegato a VCC. Nel mio caso, il modulo supportava 5V per l'alimentazione, quindi, non ci ho pensato sopra, ho collegato il cavo che proveniva dal modulo sull'altro lato della scheda. Alla fine, il connettore mal saldato si è storto, e ho semplicemente perso il contatto UART. facepalm.jpg In generale, «una testa malandata non dà pace alle gambe», e le mani goffe alla testa...

Alla fine ho visto in Minicom il tanto atteso

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /

Inoltre, l'indicatore di caricamento si muove e gira. Ricorda i tempi della scuola e il caricamento lento di MinuetOS da floppy disk. A parte il fatto che il lettore non scricchiola.

Il problema è che dopo il messaggio BOOT non succede nulla. È quindi il momento di collegarsi tramite OpenOCD su Raspberry, con GDB sull'host, e vedere di cosa si tratta.

In primo luogo, la connessione tramite GDB ha subito mostrato che $pc (program counter, indirizzo dell'istruzione corrente) va a finire in 0x0 — probabilmente questo accade dopo un errore multiplo. Quindi, subito dopo aver emesso il messaggio BOOT aggiungeremo un ciclo infinito. Questo lo ritarderà per un po'...

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

Un codice così ingegnoso è usato "per affidabilità": ho sentito da qualche parte che, in teoria, il ciclo infinito può causare comportamento indefinito, e qui il compilatore difficilmente se ne accorgerà (Ricordo che 0x10000 si trova nel BootROM).

Parte 3: Stiamo quasi caricando Linux da una scheda SD su RocketChip

A prima vista, che ci si può aspettare di più — un embedded severo, che tipo di sorgente? Ma infatti nell quell'articolo l'autore ha debuggato codice C… Crex-fex-pex:

(gdb) file builds/zeowaa-e115/sdboot.elf
Un programma è già in fase di debug.
Sei sicuro di voler cambiare il file? (y or n) y
Lettura dei simboli da builds/zeowaa-e115/sdboot.elf... fatto.

Parte 3: Stiamo quasi caricando Linux da una scheda SD su RocketChip

Devi caricare non un file MIF e nemmeno un bin, ma la versione originale in formato ELF.

Ora si può tentare di indovinare l'indirizzo dove la esecuzione continuerà (questo è un altro motivo per cui il compilatore non doveva rendersi conto che il ciclo fosse infinito). Il comando

set variable $pc=0xADDR

permette di cambiare il valore del registro al volo (in questo caso — l'indirizzo dell'istruzione corrente). Con esso, puoi anche modificare i valori memorizzati in memoria (e i registri mappati in memoria).

Alla fine sono giunto alla conclusione (non sono sicuro che sia corretta) che abbiamo "un'immagine della scheda SD di un altro sistema", e bisogna andare non all'inizio dei dati caricati, ma a 0x89800 byte più avanti:

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

Probabilmente, questo è dovuto anche al fatto che, non avendo a disposizione la scheda da 4Gb, ho preso quella da 2Gb e, a tentoni, ho sostituito nel Makefile. DEMO_END=11718750 in DEMO_END=3078900 (non cercate un significato specifico — non ce n'è, semplicemente ora l'immagine si adatta alla scheda).

Livello due, U-Boot

Ora stiamo ancora 'cadendo', ma ci troviamo già all'indirizzo 0x0000000080089a84. Qui devo ammettere: in realtà, la narrazione non procede 'con tutte le fermate', ma viene scritta parzialmente 'dopo', quindi qui ho già avuto tempo di inserire il file dtb corretto per il nostro SoC, e di modificare nelle impostazioni. HiFive_U-Boot variabile CONFIG_SYS_TEXT_BASE=0x80089800 (invece di 0x08000000), affinché l'indirizzo di caricamento coincida con quello reale. Ora carichiamo la scheda del livello successivo con un'altra immagine:

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

E vediamo:

   │304     
/*                                               │
   │305      * trap entry                                    │
   │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)                      │

Inoltre, saltiamo tra le righe 308 e 309. E non sorprende, considerando che in $sp c'è il valore 0xfffffffe31cdc0a0. Purtroppo, scappa continuamente a causa della riga 307. Pertanto, proveremo a impostare un punto di arresto su trap_entry, e poi tornare di nuovo su 0x80089800 (il punto di ingresso di U-Boot), e speriamo che non richieda una corretta impostazione dei registri prima della transizione... Sembra funzionare:

(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

Non è un grande puntatore dello stack, a dirla tutta: punta completamente al di fuori della RAM (a meno che, naturalmente, non abbiamo già la traduzione degli indirizzi, ma speriamo in una soluzione semplice).

Proveremo a sostituire il puntatore con 0x881cf950. Alla fine arriviamo al punto che handle_trap viene chiamato continuamente, mentre ci allontaniamo verso _exit_trap con argomento epc=2148315240 (in forma decimale):

(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

Impostiamo un breakpoint su strnlen, continuiamo e vediamo:

(gdb) bt
#0 strnlen (s=s@entry=0x10060000 "", count=18446744073709551615) in lib/string.c:283
#1 0x00000000800cc14c in string (buf=buf@entry=0x881cbd4c "", end=end@entry=0x881cc15c "", s=0x10060000 "", field_width=, precision=, flags=) in 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) in 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) in 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) in lib/vsprintf.c:717
#5 0x00000000800ccb50 in printf (fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc x , ra lxn") in lib/vsprintf.c:792
#6 0x000000008008a9f0 in _exit_trap (regs=, epc=2148315240, code=) in arch/riscv/lib/interrupts.c:92
#7 handle_trap (mcause=, epc=, regs=) in arch/riscv/lib/interrupts.c:55
#8 0x0000000080089b10 in trap_entry () in /hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot/arch/riscv/cpu/HiFive/start.S:343
Backtrace stopped: frame did not save the PC

Sembra che _exit_trap voglia fornire informazioni di debug sull'eccezione verificatasi, ma non ci riesce. Quindi, i sorgenti non vengono nuovamente visualizzati. set directories ../freedom-u-sdk/HiFive_U-Boot/ Oh! Ora sono visibili!

Bene, facciamolo partire di nuovo e vediamo tramite il backtrace il motivo del problema originale che ha causato il primo errore (mcause == 5). Se ho capito bene ciò che è scritto qui a pagina 37, allora questa eccezione significa Load access fault. La causa, apparentemente, è che qui

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

call_board_init_f:
    li  t0, -16
    li  t1, CONFIG_SYS_INIT_SP_ADDR
    and sp, t1, t0  /* forza l'allineamento a 16 byte */

#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       /* salta a board_init_f() */

$sp ha quel valore errato, e all'interno board_init_f_init_reserve si verifica un errore. Sembra proprio essere il colpevole: la variabile con un nome inequivocabile CONFIG_SYS_INIT_SP_ADDR. È definita nel file HiFive_U-Boot/include/configs/HiFive-U540.h. A un certo punto ho persino pensato, e se, lascia stare, modificare il caricatore per il processore — magari è più facile correggere un po' il processore? Ma poi ho visto che somiglia più a un artefatto di impostazioni non completamente impostate per un'altra configurazione della memoria, e si può provare a fare così:#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 */

A un certo punto il numero di soluzioni temporanee

di rinforzo tecnologico ha raggiunto una soglia critica. Dopo un po' di riflessione, ho compreso la necessità di effettuare un porting corretto sulla mia scheda. Per questo è necessario copiare e modificare per la nostra configurazione un certo numero di file. Circa, ecco un po'

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 +0300Supporto iniziale per la scheda Zeowaa A-E115FBM 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

I dettagli possono essere visualizzati in

Come si è scoperto, su questa scheda SiFive alcuni registri di dispositivi hanno indirizzi diversi. Inoltre, è emerso che U-Boot viene configurato tramite il già conosciuto meccanismo Kconfig del kernel Linux — ad esempio, è possibile ordinare repository.

make menuconfig , e davanti a te apparirà un'interfaccia testuale comoda con la visualizzazione delle descrizioni dei parametri., e vi apparirà un'interfaccia testuale comoda con la visualizzazione delle descrizioni dei parametri per ? In sintesi, combinando le descrizioni di due schede per creare una terza, escludendo varie impostazioni di PLL piuttosto pompose (probabilmente collegate alla gestione dal computer host tramite PCIe, ma non è certo), ho ottenuto un certo firmware che, con il giusto tempo su Marte, mi inviava tramite UART un messaggio riguardo l'hash del commit da cui era stato assemblato, e riguardo a quanto DRAM avessi (ma questa informazione l'avevo già inserita io nell'intestazione).

Peccato solo che dopo ciò la scheda di solito smetteva di rispondere tramite JTAG del processore, mentre il caricamento dalla scheda SD, purtroppo, nella mia configurazione non è veloce. D'altra parte, a volte il BootROM mostrava un messaggio che ERROR, non sono riuscito a caricare, e subito appariva U-Boot. A quel punto mi è apparso chiaro: evidentemente, dopo il riavvio, il bitstream nella FPGA non viene sovrascritto e non ha il tempo di "disattivarsi", ecc. Insomma, è possibile semplicemente, all'apparizione del messaggio LOADING / collegarsi con un debugger e impartire il comando set variable $pc=0x80089800, bypassando così questo lungo caricamento (ovviamente, supponendo che la volta precedente si sia bloccato piuttosto presto e non abbia avuto il tempo di caricare qualcosa sopra il codice originale).

A proposito, è normale che il processore si blocchi completamente e che non possa collegarsi un debugger JTAG con i messaggi

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

Aspetta! Questo lo ho già visto! Qualcosa di simile accade in caso di deadlock nel TileLink, e non mi fido molto dell'autore del controller di memoria — l'ho scritto io stesso… Improvvisamente, dopo il primo successo nella ricompilazione del processore dopo la modifica del controller, ho visto:

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING
BOOT

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

DRAM:  1 GiB
MMC:
BEFORE LOAD ENVBEFORE FDTCONTROLADDRBEFORE LOADADDRIn:    serial
Out:   serial
Err:   serial
Hit any key to stop autoboot:  3

Su questa strana riga prima In: serial non prestare attenzione — stavo cercando di capire su un processore bloccato se funzionasse correttamente con l'ambiente. Cosa significa, «È bloccato da dieci minuti»? È riuscito almeno a rilocarsi e ad accedere al menu di avvio! Una piccola digressione: anche se U-Boot si carica nei primi 2^24 byte dalla scheda SD, una volta avviato, si copia altrove all'indirizzo, che sia scritto nell'intestazione di configurazione o semplicemente negli indirizzi superiori della memoria RAM, effettua la rilocazione dei simboli ELF e passa il controllo. Ecco: sembra che questo livello sia stato superato e come bonus abbiamo un processore che non si blocca completamente dopo questo.

Allora, perché il timer non funziona? Sembra che l'orologio non vada proprio...

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

E se girassi le lancette manualmente?

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

Allora:

Premi un tasto per interrompere l'autoboot:  0
MMC_SPI: 0 a 0:1 hz 20000000 modalità 0

Conclusione: l'orologio non va. Probabilmente, per lo stesso motivo, non funziona nemmeno l'input da tastiera:

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:
        /* Primo carattere della sequenza di escape ANSI 'e' */
        if (c == 'e') {
            *esc = 1;
            *key = KEY_NONE;
        }
        break;
    case 1:
        /* Secondo carattere di ANSI '[' */
        if (c == '[') {
...

Il problema si è rivelato essere che ho esagerato un po': ho aggiunto nella configurazione del processore la chiave:

  case DTSTimebase => BigInt(0)

… basandomi sul fatto che nel commento era detto «se non sai, lascia 0». E infatti WithNBigCores proprio lo impostava a 1MHz (come, tra l'altro, indicato nella configurazione di U-Boot). Ma io, accidenti, sono preciso e scrupoloso: lì non so, qui 25MHz! In fin dei conti, niente funziona. Ho tolto i miei "miglioramenti" e...

Premi un tasto per interrompere l'autoboot:  0
MMC_SPI: 0 a 0:1 hz 20000000 modalità 0
## Tipo di tabella partizione sconosciuto 0
libfdt fdt_path_offset() restituito FDT_ERR_NOTFOUND
** Nessuna tabella partizione - mmc 0 **
## Info: dimensione dati in input = 34 = 0x22
Esecuzione di uEnv.txt boot2...
## Errore: "boot2" non definito
HiFive-Unleashed #

Si possono persino inserire comandi! Ad esempio, dopo un po' di tentativi, si può finalmente intuire di inserire mmc_spi 1 10000000 0; mmc part, riducendo la frequenza SPI da 20MHz a 10MHz. Perché? Bene, nel configurazione era scritta la frequenza massima di 20MHz, e così è ancora adesso. Ma, per quanto ho capito, le interfacce, almeno qui, funzionano in questo modo: il codice divide la frequenza dell'hardware (ho 25MHz ovunque) per la frequenza target, e imposta il valore risultante come divisore nel registro di controllo corrispondente. E il problema è che se per un UART a 115200Hz va bene, ma se si divide esattamente 25000000 per 20000000 si ottiene 1, cioè funzionerà a 25MHz. Forse va bene così, ma se vengono imposti dei limiti, significa che a qualcuno serve (anche se non è sicuro)... Insomma, è più facile impostare e andare avanti — lontano e, ahimè, a lungo. 25MHz non è proprio un Core i9.

Output della console

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

Mappa delle partizioni per il dispositivo MMC 0  --   Tipo di partizione: EFI

Part    Inizio LBA       Fine LBA         Nome
        Attributi
        Tipo 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() restituito FDT_ERR_NOTFOUND
2376 bytes letti in 0 ms
Esecuzione di uEnv.txt boot2...
15332118 bytes letti in 0 ms
## Caricamento del kernel dall'immagine FIT a 90000000 ...
   Utilizzando la configurazione 'config-1'
   Prova il sottogruppo del kernel 'bbl'
     Descrizione:  BBL/SBI/riscv-pk
     Tipo:         Immagine del kernel
     Compressione:  non compressa
     Inizio dati:   0x900000d4
     Dimensione dati:    74266 Bytes = 72.5 KiB
     Architettura: RISC-V
     OS:           Linux
     Indirizzo di caricamento: 0x80000000
     Punto d'ingresso:  0x80000000
     Algoritmo hash:    sha256
     Valore hash:   28972571467c4ad0cf08a81d9cf92b9dffc5a7cb2e0cd12fdbb3216cf1f19cbd
   Verifica dell'integrità dell'hash ... sha256+ OK
## Caricamento fdt dall'immagine FIT a 90000000 ...
   Utilizzando la configurazione 'config-1'
   Prova il sottogruppo fdt 'fdt'
     Descrizione:  non disponibile
     Tipo:         Flat Device Tree
     Compressione:  non compressa
     Inizio dati:   0x90e9d31c
     Dimensione dati:    6911 Bytes = 6.7 KiB
     Architettura: RISC-V
     Indirizzo di caricamento: 0x81f00000
     Algoritmo hash:    sha256
     Valore hash:   10b0244a5a9205357772ea1c4e135a4f882409262176d8c7191238cff65bb3a8
   Verifica dell'integrità dell'hash ... sha256+ OK
   Caricamento fdt da 0x90e9d31c a 0x81f00000
   Avvio utilizzando il blob fdt a 0x81f00000
## Caricamento delle risorse caricabili dall'immagine FIT a 90000000 ...
   Prova il sottogruppo 'kernel' delle risorse caricabili
     Descrizione:  kernel Linux
     Tipo:         Immagine del kernel
     Compressione:  non compressa
     Inizio dati:   0x900123e8
     Dimensione dati:    10781356 Bytes = 10.3 MiB
     Architettura: RISC-V
     OS:           Linux
     Indirizzo di caricamento: 0x80200000
     Punto d'ingresso:  non disponibile
     Algoritmo hash:    sha256
     Valore hash:   72a9847164f4efb2ac9bae736f86efe7e3772ab1f01ae275e427e2a5389c84f0
   Verifica dell'integrità dell'hash ... sha256+ OK
   Caricamento delle risorse caricabili da 0x900123e8 a 0x80200000
## Caricamento delle risorse caricabili dall'immagine FIT a 90000000 ...
   Prova il sottogruppo 'ramdisk' delle risorse caricabili
     Descrizione:  buildroot initramfs
     Tipo:         Immagine RAMDisk
     Compressione:  compressa gzip
     Inizio dati:   0x90a5a780
     Dimensione dati:    4467411 Bytes = 4.3 MiB
     Architettura: RISC-V
     OS:           Linux
     Indirizzo di caricamento: 0x82000000
     Punto d'ingresso:  non disponibile
     Algoritmo hash:    sha256
     Valore hash:   883dfd33ca047e3ac10d5667ffdef7b8005cac58b95055c2c2beda44bec49bd0
   Verifica dell'integrità dell'hash ... sha256+ OK
   Caricamento delle risorse caricabili da 0x90a5a780 a 0x82000000

Va bene, siamo passati a un nuovo livello, ma si blocca ancora. A volte lancia anche eccezioni. Puoi vedere mcause, aspettando il codice all'indirizzo specificato $pc e dopo si trovare su trap_entry. L'elaboratore stesso di U-Boot può stampare solo per mcause = 0..4, quindi preparatevi a dover affrontare un caricamento non corretto. Qui sono andato a controllare la configurazione, ho cominciato a vedere cosa avevo cambiato, e ho ricordato: c'era scritto in conf/rvboot-fit.txt :

fitfile=image.fit
# sotto deve corrispondere a quanto in FIT (ugha)

Bene, allineiamo tutti i file, sostituendo la riga di comando del kernel più o meno così, poiché ci sono sospetti che SIF0 — è un'uscita da qualche parte tramite PCIe:

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

E per comodità cambieremo l'algoritmo di hashing da SHA-256 a MD5: non mi serve una grande resistenza crittografica (soprattutto durante il debug), si dice che impieghi un tempo folle, e per rilevare errori di integrità al caricamento, MD5 è più che sufficiente. E quindi, che cosa risulta? Siamo riusciti a superare il livello precedente notevolmente più velocemente (grazie a un hashing più semplice), e si è aperto il livello successivo:

...
   Verificando l'integrità dell'hash ... md5+ OK
   Caricamento dei file caricabili da 0x90a5a758 a 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() restituito 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";
};
   Caricamento dell'immagine del kernel ... OK
Avvio del kernel in
3

Solo che l'orologio non ticchetta...

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

Ops, sembra che la correzione dell'orologio sia stata un placebo, anche se allora mi sembrava che avesse funzionato. Certo, bisogna ripararlo, ma iniziamo a ruotare le lancette manualmente e vediamo cosa succede:

0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Continuando.
^C
Il programma ha ricevuto il segnale SIGINT, Interruzione.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Continuando.
^C
Il programma ha ricevuto il segnale SIGINT, Interruzione.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Continuando.

Nel frattempo...

   Caricamento dell'immagine del kernel ... OK
Avvio del kernel in
3
2
1
0
## Avvio dell'applicazione a 0x80000000 ...

No, andrò ad automatizzare l'orologio — se no, potrebbe decidere di calibrare il timer!

E l'indirizzo dell'istruzione corrente nel frattempo punta da qualche parte in

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

dentro del Berkeley Boot Loader caricato. Personalmente, sono sorpreso dalla menzione htif — interfaccia host utilizzata per l'avvio collegato del kernel (cioè in cooperazione con l'host ARM), pensavo fosse standalone. Tuttavia, se si trova questa funzione nel sorgente, si vede che non è tutto così male:

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

Ricerca: avviare l'orologio

La ricerca dei registri in CLINT ci porta a

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

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

Che si collega all'RTC, o al misterioso MockAON, di cui inizialmente ho pensato: «Quindi, cos'è qui? Non chiaro? Spegniamo!» Poiché non capisco ancora bene quale sia la magia del clock, quindi semplicemente rilancerò questa logica in System.scala:

  val rtcDivider = RegInit(0.asUInt(16.W)) // per precauzione supporterò fino a 16GHz, sono ottimista :)
  val mhzInt = p(DevKitFPGAFrequencyKey).toInt
  // Supponiamo che la frequenza sia un numero intero di megahertz
  rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
  outer.clintOpt.foreach { clint =>
    clint.module.io.rtcTick := rtcDivider === 0.U
  }

Procedendo verso il kernel Linux

Qui il racconto si è già protratto ed è diventato un po' monotono, quindi lo descriverò a grandi linee:

BBL prevedeva la presenza di un FDT all'indirizzo 0xF0000000, e io l'avevo già corretto! Bene, cerchiamo ancora... Trovato in HiFive_U-Boot/arch/riscv/lib/boot.c, sostituito con 0x81F00000, indicato nella configurazione di avvio di U-Boot.

Poi BBL si lamentava che non c'era memoria. La mia strada conduceva alla funzione mem_prop, che si trova in riscv-pk/machine/fdt.c: da lì ho capito che era necessario etichettare il nodo fdt ram come device_type = "memory" — poi, forse sarà necessario correggere il generatore di processore, ma per ora lo scriverò semplicemente a mano — comunque ho trasferito questo file manualmente.

Ora ho ricevuto un messaggio (riportato in formato formattato, con ritorni a capo):

Questo è il dummy_payload di bbl. Per avviare un vero kernel, riconfigura bbl
con il flag --with-payload=PATH, poi ricostruisci bbl. In alternativa,
bbl può essere usato in modalità solo firmware aggiungendo nodi device-tree
per un payload esterno e usando le opzioni -bios e -kernel di QEMU.

Sembra che le opzioni siano specificate correttamente riscv,kernel-start e riscv,kernel-end nel DTB, ma i valori vengono analizzati come zero. Debugging query_chosen ha mostrato che BBL sta cercando di analizzare un indirizzo di 32 bit, mentre ottiene una coppia <0x0 0xADDR>, e il primo valore sembra avere i bit meno significativi. Ho aggiunto alla sezione chosen

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

e ho corretto la generazione dei valori: non aggiungere 0x0 come primo elemento.

Questi 100500 passaggi semplici ti permetteranno di vedere facilmente e rapidamente come cade il pinguino:

Testo nascosto

   Verifica dell'integrità degli hash ... md5+ OK
   Caricamento dei loadables da 0x90a5a758 a 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() ha restituito FDT_ERR_NOTFOUND
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";
};
   Caricamento dell'immagine del kernel ... OK
Avvio del kernel in
3
2
1
0
## Avvio dell'applicazione a 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: Ignorando l'intervallo di memoria 0x80000000 - 0x80200000
[    0.000000] Versione di Linux 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc versione 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Mer Lug 3 21:29:21 MSK 2019
[    0.000000] bootconsole [early0] abilitato
[    0.000000] Ramdisk iniziale a: 0x(____ptrval____) (16777216 byte)
[    0.000000] Intervalli di zona:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normale   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Inizio della zona mobile per ogni nodo
[    0.000000] Intervalli di nodo di memoria iniziale
[    0.000000]   nodo   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Impostazione nodi di memoria iniziale 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Su nodo 0 totalpages: 261632
[    0.000000]   Zona DMA32: 3577 pagine usate per memmap
[    0.000000]   Zona DMA32: 0 pagine riservate
[    0.000000]   Zona DMA32: 261632 pagine, LIFO batch:63
[    0.000000] IO TLB software: mappato [mem 0xbb1fc000-0xbf1fc000] (64MB)

(l'emblema è visualizzato da BBL, mentre ciò che riguarda i timestamp è il kernel).

Fortunatamente, non so se ovunque, ma su RocketChip, collegando il debugger tramite JTAG, puoi intercettare i trap direttamente — il debugger si fermerà esattamente in quel punto.

Il programma ha ricevuto il segnale SIGTRAP, Trace/breakpoint trap.
0xffffffe0000024ca in ?? ()
(gdb) bt
#0  0xffffffe0000024ca in ?? ()
Backtrace fermato: frame precedente identico a questo frame (stack corrotto?)
(gdb) file work/linux/vmlinux
Un programma è già in fase di debug.
Sei sicuro di voler cambiare il file? (y o n) y
Lettura dei simboli da work/linux/vmlinux... fatto.
(gdb) bt
#0  0xffffffe0000024ca in setup_smp () a /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75
#1  0x0000000000000000 in ?? ()
Backtrace fermato: il frame non ha salvato il 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); // < SEI QUI
}

Come si diceva in un vecchio aneddoto, CPU non trovata, esecuzione dell'emulazione software. O forse non in esecuzione. Perso in un singolo core del processore.

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

Un buon commento in linux/arch/riscv/kernel/setup.c — una sorta di pittura di una recinzione secondo il metodo di Tom Sawyer. In generale, oggi non ci sono vincitori, il premio viene rimandato al prossimo turno...

Propongo di concludere questo articolo già troppo lungo.

Continua. Ci sarà una lotta con un errore astuto che riesce a nascondersi, se ci si avvicina lentamente con il singlestep.

Screencast di caricamento (link esterno):
Parte 3: Stiamo quasi caricando Linux da una scheda SD su RocketChip

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster