Parte 3: Quasi caricando Linux dalla scheda SD al RocketChip

Parte 3: Quasi caricando Linux dalla scheda SD al RocketChip В sezione precedente è stato implementato un controller di memoria più o meno funzionante, o meglio, un wrapper over IP Core di Quartus, che è un adattatore per TileLink. Oggi, nella sezione "Stiamo portando RocketChip su una scheda cinese poco conosciuta con Cyclone" vedrai una console funzionante. Il processo è durato un po’ più a lungo: stavo già pensando che avrei lanciato velocemente Linux e sarei andato avanti, ma non è stato così. In questa parte, propongo di esaminare il processo di lancio di U-Boot, BBL e i timidi tentativi di inizializzazione del kernel Linux. Ma esiste una console, U-Boot, ed è piuttosto avanzata, con molto di ciò che ti aspetteresti da una console a tutti gli effetti.

L'hardware includerà una scheda SD collegata tramite l'interfaccia SPI e una UART. Nella parte software, BootROM verrà sostituita con xip su sdboot e, infatti, sono state aggiunte le seguenti fasi di caricamento (sulla scheda SD).

Finitura dell'hardware

Quindi, il compito: devi passare a un core "grande" e collegare un UART (da Raspberry) e un adattatore SD (abbiamo usato una scheda Catalex con sei pin: GND, VCC, MISO, MOSI, SCK, CS) .

In linea di principio, tutto era abbastanza semplice. Ma prima di rendermene conto, sono stato sbattuto un po' da una parte all'altra: dopo la volta precedente, ho deciso che ancora una volta dovevo solo mescolare System qualcosa di simile a HasPeripheryUART (e implementazione di conseguenza), lo stesso per la scheda SD - e tutto sarà pronto. Poi ho deciso di vedere come veniva implementato in un progetto “serio”. Allora, cosa c'è di serio in questo? Apparentemente Arty non si adatta: il mostro rimane unleahshed.DevKitConfigs. E all'improvviso si è scoperto che c'erano delle sovrapposizioni ovunque, che venivano aggiunte tramite parametri tramite tasti. Immagino che questo sia probabilmente molto flessibile e configurabile, ma vorrei almeno eseguire qualcosa prima... Non hai la stessa cosa, solo più semplice e fastidiosa? È stato allora che mi sono imbattuto vera.iofpga.FPGAChip per FPGA Microsemi e l'ho immediatamente smontato per preventivi e ho provato a realizzare la mia implementazione per analogia, fortunatamente c'è più o meno l'intero "layout della scheda madre" in un unico file.

Si è scoperto che devi solo aggiungere System.scala linea

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

Riga nel corpo della classe System aggiunge informazioni sulla frequenza con cui opera questa parte del nostro SoC nel file dts. Per quanto ho capito, DTS/DTB è un analogo statico della tecnologia plug-and-play per dispositivi embedded: l'albero di descrizione dts viene compilato in un file dtb binario e trasferito dal bootloader al kernel in modo che possa configurare correttamente il hardware. È interessante notare che, senza la linea con tlclock tutto si sintetizza perfettamente, ma compilando la BootROM (permettetemi di ricordarvi, ora questo sarà già sdboot) non funzionerà: durante il processo di compilazione analizza il file dts e crea un'intestazione con una macro TL_CLK, grazie al quale sarà in grado di configurare correttamente i divisori di frequenza per le interfacce esterne.

Dovrai anche correggere leggermente il “cablaggio”:

Piattaforma.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. Molto probabilmente, dovrebbero proteggersi metastabilità. Forse dentro un po ' i blocchi hanno già la propria protezione, ma prima voglio lanciarla almeno "ad un livello di alta qualità". Una domanda più interessante per me è perché MISO e MOSI si reggono in modo diverso dq? Non ho ancora trovato la risposta, ma sembra che il resto del codice si basi proprio su tale connessione.

Fisicamente ho semplicemente assegnato i pin di progettazione ai contatti liberi sul blocco e ho spostato il ponticello di selezione della tensione su 3.3 V.

Adattatore SD

Vista dall'alto:

Parte 3: Quasi caricando Linux dalla scheda SD al RocketChip

Vista dal basso:

Parte 3: Quasi caricando Linux dalla scheda SD al RocketChip

Debug del software: strumenti

Innanzitutto, parliamo degli strumenti di debug disponibili e dei loro limiti.

minicom

Per prima cosa dovremo leggere in qualche modo l'output del bootloader e del kernel. Per fare ciò su Linux (in questo caso su RaspberryPi), abbiamo bisogno del programma Minicom. In generale, va bene qualsiasi programma che funzioni con una porta seriale.

Tieni presente che all'avvio il nome del dispositivo della porta deve essere specificato come -D /dev/ttyS0 - dopo l'opzione -D. Bene, l'informazione principale: uscire, usare Ctrl-A, X. In realtà ho avuto un caso in cui questa combinazione non ha funzionato, quindi puoi semplicemente dirlo da una sessione SSH vicina killall -KILL minicom.

C'è un'altra caratteristica. Nello specifico il RaspberryPi ha due UART ed entrambe le porte possono già essere adattate per qualcosa: una per Bluetooth, l'altra di default emette la console del kernel. Fortunatamente, questo comportamento può essere ignorato secondo questo manuale.

Riscrittura della memoria

Durante il debug, per verificare un'ipotesi, a volte dovevo farlo caricare il bootloader (scusate) nella RAM direttamente dall'host. Forse questo si può fare direttamente da GDB, ma alla fine ho seguito un percorso semplice: ho copiato il file necessario su Raspberry, ho inoltrato anche la porta 4444 via SSH (telnet da OpenOCD) e ho usato il comando load_image. Quando lo fai, sembra che tutto sia congelato, ma in realtà “non dorme, lampeggia solo lentamente”: Scarica il file, lo fa semplicemente alla velocità di un paio di kilobyte al secondo.

Funzionalità di installazione dei punti di interruzione

Molte persone probabilmente non hanno dovuto pensare a questo durante il debug di programmi regolari, ma i punti di interruzione non sono sempre impostati nell'hardware. A volte l'impostazione di un punto di interruzione implica la scrittura temporanea di istruzioni speciali nel posto giusto direttamente nel codice macchina. Ad esempio, ecco come funzionava il mio comando standard b nel GDB. Ecco quanto segue:

  • non puoi inserire un punto all'interno di BootROM perché ROM
  • È possibile impostare un punto di interruzione sul codice caricato nella RAM dalla scheda SD, ma è necessario attendere fino al caricamento. Altrimenti non riscriveremo un pezzo di codice, ma il caricatore riscriverà il nostro punto di interruzione

Sono sicuro che puoi chiedere esplicitamente di utilizzare i punti di interruzione hardware, ma ce n'è comunque un numero limitato.

Sostituzione rapida della BootROM

Nella fase iniziale del debug, spesso si desidera riparare la BootROM e riprovare. Ma c'è un problema: la BootROM è parte del progetto caricato nell'FPGA e la sua sintesi è questione di pochi minuti (e questo avviene dopo la compilazione quasi istantanea dell'immagine BootROM stessa da C e Assembler...). Fortunatamente, in realtà tutto più veloce: la sequenza delle azioni è la seguente:

  • rigenerare bootrom.mif (sono passato a MIF anziché HEX, perché ho sempre avuto dei problemi con HEX e MIF è il formato nativo di Alter)
  • in Quarto dicono Processing -> Update Memory Initialization File
  • sulla voce Assembler (nella colonna sinistra di Attività) il comando Avvia di nuovo

Tutto su tutto: un paio di decine di secondi.

Preparazione della scheda SD

Qui tutto è relativamente semplice, ma devi essere paziente e avere circa 14 GB di spazio su disco:

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

Dopodiché è necessario inserire una scheda SD pulita, o meglio, che non contenga nulla di necessario, ed eseguire

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

… Dove sdX — dispositivo assegnato alla carta. ATTENZIONE: i dati presenti sulla card verranno cancellati, sovrascritti e in generale! Non vale quasi la pena eseguire l'intero assemblaggio da sotto sudoperché in tal caso tutti gli artefatti di costruzione apparterranno a root, e il montaggio dovrà essere fatto da sotto sudo costantemente.

Il risultato è una carta contrassegnata in GPT con quattro partizioni, una delle quali ha FAT uEnv.txt e un'immagine avviabile in formato FIT (contiene diverse immagini secondarie, ciascuna con il proprio indirizzo di download), l'altra partizione è vuota, dovrebbe essere formattata in Ext4 per Linux. Altre due sezioni - misterioso: U-Boot vive su uno (il suo offset, per quanto ho capito, è codificato in BootROM), dall'altro, a quanto pare, le sue variabili di ambiente vivono, ma non le uso ancora.

Livello uno, BootROM

La saggezza popolare dice: "Se nella programmazione si balla con un tamburello, nell'elettronica si balla anche con un estintore". Non si tratta nemmeno del fatto che una volta ho quasi bruciato il tabellone, decidendo che "Bene, GND è allo stesso livello basso". (a quanto pare, una resistenza non farebbe male, dopotutto...) Si tratta più del fatto che se le mani non crescono da lì, l'elettronica non smette mai di sorprendere: durante la saldatura del connettore sulla scheda, non sono ancora riuscito a saldare correttamente i contatti - il video mostra come la saldatura si diffonde direttamente su tutta la connessione basta applicare un saldatore, per me ha “schiaffeggiato” a caso. Beh, forse la saldatura non era adatta alla temperatura del saldatore, forse qualcos'altro... In generale, quando ho visto che avevo già una dozzina di contatti, ho rinunciato e ho iniziato a eseguire il debug. E poi è iniziato enigmatico: Ho collegato RX/TX da UART, carico il firmware - dice

INIT
CMD0
ERROR

Bene, tutto è logico: non ho collegato il modulo della scheda SD. Correggiamo la situazione, carichiamo il firmware... E silenzio... Perché non ho cambiato idea, ma la scatoletta si è appena aperta: uno dei pin del modulo doveva essere collegato a VCC. Nel mio caso, il modulo supportava 5 V per l'alimentazione, quindi, senza pensarci due volte, ho collegato il cavo proveniente dal modulo al lato opposto della scheda. Di conseguenza, il connettore saldato storto si è distorto e Il contatto UART è stato semplicemente perso. facepalm.jpg In generale, “una testa cattiva non dà riposo alle gambe”, e le mani storte non danno riposo alla testa...

Di conseguenza, ho visto il tanto atteso

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /

Inoltre, si muove e l'indicatore di caricamento gira. Ricordo subito i miei giorni di scuola e il piacevole caricamento di MinuetOS da un floppy disk. A meno che l'unità non macina.

Il problema è che dopo il messaggio BOOT non succede nulla. Ciò significa che è ora di connettersi tramite OpenOCD a Raspberry, a GDB sull'host e vedere di cosa si tratta.

Innanzitutto, la connessione tramite GDB lo ha dimostrato immediatamente $pc (contatore del programma, indirizzo dell'istruzione corrente) vola verso 0x0 - questo probabilmente accade dopo più errori. Pertanto, immediatamente dopo l'emissione del messaggio BOOT Aggiungiamo 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ì complicato viene utilizzato "per affidabilità": ho sentito da qualche parte che un ciclo infinito è un comportamento indefinito, ma è improbabile che il compilatore indovini (ti ricordo che secondo 0x10000 che si trova nella BootROM).

Parte 3: Quasi caricando Linux dalla scheda SD al RocketChip

Sembrerebbe, cos'altro aspettarsi: hard embedded, che tipo di codici sorgente ci sono? Ma in quell'articolo l'autore stava eseguendo il debug del codice C... Kreks-fex-pex:

(gdb) file builds/zeowaa-e115/sdboot.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from builds/zeowaa-e115/sdboot.elf...done.

Parte 3: Quasi caricando Linux dalla scheda SD al RocketChip

Devi solo scaricare non il file MIF o il cestino, ma la versione originale in formato ELF.

Ora potete indovinare all'ennesimo tentativo l'indirizzo dove proseguirà l'esecuzione (questo è un altro motivo per cui il compilatore non avrebbe dovuto indovinare che il ciclo è infinito). Squadra

set variable $pc=0xADDR

consente di modificare al volo il valore del registro (in questo caso, l'indirizzo dell'istruzione corrente). Con il suo aiuto, puoi modificare i valori scritti in memoria (e i registri mappati in memoria).

Alla fine, sono giunto alla conclusione (non sono sicuro di quale sia corretta) che abbiamo "un'immagine della scheda SD del sistema sbagliato" e non dobbiamo andare all'inizio dei dati scaricati, ma a 0x89800 byte ulteriormente:

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

Forse su questo ha influito anche il fatto che non avendo a portata di mano una scheda da 4Gb non necessaria, ne ho presa una da 2Gb e l'ho sostituita a caso nel Makefile DEMO_END=11718750 su DEMO_END=3078900 (non cercare il significato in un significato specifico: non ce n'è, è solo che ora l'immagine è posizionata sulla carta).

Livello due, U-Boot

Adesso stiamo ancora “cadendo”, ma siamo già nel posto giusto 0x0000000080089a84. Qui devo ammetterlo: in effetti la presentazione non va “di tutto punto”, ma è scritta parzialmente “dopo”, quindi ecco che sono già riuscito ad inserire il file dtb corretto dal nostro SoC, correggetelo nelle impostazioni HiFive_U-Boot variabile CONFIG_SYS_TEXT_BASE=0x80089800 (invece di 0x08000000) in modo che l'indirizzo di download corrisponda a quello effettivo. Ora carichiamo la mappa del livello successivo, 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 è sorprendente, dato che in $sp sta il significato 0xfffffffe31cdc0a0. Ahimè, anche questo “scappa” costantemente a causa della riga 307. Proviamo quindi a impostare un punto di interruzione su trap_entry, e poi torna a 0x80089800 (punto di ingresso di U-Boot), e speriamo che non richieda la corretta impostazione dei registri prima di saltare... Sembra che funzioni:

(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

Lo stack pointer è così così, francamente: punta a bypassare del tutto la RAM (a meno che, ovviamente, non abbiamo già la traduzione degli indirizzi, ma speriamo in un'opzione semplice).

Proviamo a sostituire il puntatore con 0x881cf950. Di conseguenza, arriviamo alla conclusione che handle_trap chiamato e chiamato, e allo stesso tempo entriamo _exit_trap con un argomento epc=2148315240 (in decimale):

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

Imposta il punto di interruzione su strnlen, proseguiamo e vediamo:

(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=<optimized out>, precision=<optimized out>, flags=<optimized out>) 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 %08x , ra %08lxn", fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lxn", 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 %08x , ra %08lxn", 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 %08x , ra %08lxn", args=args@entry=0x881cc188) at lib/vsprintf.c:717
#5  0x00000000800ccb50 in printf (fmt=fmt@entry=0x800d4458 "exception code: %d , %s , epc %08x , ra %08lxn") at lib/vsprintf.c:792
#6  0x000000008008a9f0 in _exit_trap (regs=<optimized out>, epc=2148315240, code=<optimized out>) at arch/riscv/lib/interrupts.c:92
#7  handle_trap (mcause=<optimized out>, epc=<optimized out>, regs=<optimized out>) 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

Sembra _exit_trap vuole fornire informazioni di debug sull'eccezione che si è verificata, ma non può. Quindi, in qualche modo le nostre fonti non vengono più visualizzate. set directories ../freedom-u-sdk/HiFive_U-Boot/ DI! Ora visualizzato!

Bene, eseguiamolo di nuovo e vediamo dallo stack trace la causa del problema originale che ha causato il primo errore (mcause == 5). Se ho capito bene cosa c'è scritto qui a pagina 37, questa eccezione significa Load access fault. Il motivo sembra essere quello 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  /* force 16 byte alignment */

#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       /* jump to board_init_f() */

$sp ha lo stesso significato errato, e dentro board_init_f_init_reserve si verifica un errore. Sembra che il colpevole sia questo: una variabile con un nome inequivocabile CONFIG_SYS_INIT_SP_ADDR. È definito nel file HiFive_U-Boot/include/configs/HiFive-U540.h. Ad un certo punto ho anche pensato che forse avrei dovuto aggiungere un boot loader per il processore - forse sarebbe più semplice sistemare un po' il processore? Ma poi ho visto che era più simile ad un manufatto non del tutto completato#if 0-impostazioni specifiche per una diversa configurazione di memoria e puoi provare a farlo:

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              /* 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 */

Ad un certo punto il numero di stampelle elementi di fissaggio tecnologici ha raggiunto un punto critico. Dopo aver faticato un po', sono arrivato alla necessità di realizzare la porta corretta per la mia tavola. Per fare ciò, dobbiamo copiare e modificare un numero di file per adattarli alla nostra configurazione.

Bene, approssimativamente, ecco un tavolino

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

    Initial support for Zeowaa A-E115FB board

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

I dettagli possono essere trovati in repository.

Come si è scoperto, su questa scheda SiFive i registri di alcuni dispositivi hanno indirizzi diversi. Si è anche scoperto che U-Boot è configurato utilizzando il meccanismo Kconfig, già familiare dal kernel Linux: ad esempio, puoi comandare make menuconfig, e davanti a te apparirà una comoda interfaccia testuale che mostra le descrizioni dei parametri tramite ? eccetera. In generale, dopo aver messo insieme una descrizione della terza dalle descrizioni di due schede, lanciando da lì ogni sorta di pretenziose riconfigurazioni PLL (a quanto pare, questo è in qualche modo collegato al controllo dal computer host tramite PCIe, ma questo non è certo) , ho ricevuto del firmware che, con il tempo giusto su Marte, mi ha dato un messaggio tramite UART su quale hash di commit è stato compilato e su quanta DRAM ho (ma io stesso ho scritto queste informazioni nell'intestazione).

L'unico peccato è che dopo questo la scheda di solito smette di rispondere tramite il processore JTAG, e il caricamento da una scheda SD, ahimè, non è veloce nella mia configurazione. D'altra parte, a volte BootROM dava il messaggio " ERROR, non è stato possibile avviarsi e U-Boot è apparso immediatamente. Fu allora che mi resi conto: a quanto pare, dopo aver riavviato il bitstream nell'FPGA, la memoria non viene cancellata, non ha il tempo di "disallenarsi", ecc. In breve, puoi semplicemente quando appare un messaggio LOADING / connettersi con un debugger e un comando set variable $pc=0x80089800, aggirando così questo lungo caricamento (ovviamente, presupponendo che l'ultima volta si sia rotto abbastanza presto e non abbia avuto il tempo di caricare qualcosa sopra il codice originale).

A proposito, è generalmente normale che il processore si blocchi completamente e il debugger JTAG non possa connettersi ad esso con i messaggi?

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

Quindi aspetta! L'ho già visto! Qualcosa di simile accade quando TileLink è in stallo e per qualche motivo non mi fido dell'autore del controller di memoria - l'ho scritto io stesso... All'improvviso, dopo la prima ricostruzione riuscita del processore dopo aver modificato il 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

A questa strana frase prima In: serial non prestare attenzione: stavo cercando di capire su un processore sospeso se funziona correttamente con l'ambiente. Cosa intendi con "È rimasto così per dieci minuti"? Almeno è riuscito a riposizionarsi e ad accedere al menu di avvio! Una piccola digressione: nonostante U-Boot venga caricato nei primi 2^24 byte dalla scheda SD, all'avvio si copia ad un indirizzo più lontano, scritto nell'header di configurazione, o semplicemente agli indirizzi più alti della RAM , ed esegue la rilocazione ELF dei caratteri e trasferisce lì il controllo. Quindi: sembra che abbiamo superato questo livello e abbiamo ricevuto un bonus per il fatto che il processore non si è bloccato saldamente dopo.

Allora perché il timer non funziona? Sembra che l'orologio non funzioni per qualche motivo...

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

Cosa succede se giri le frecce manualmente?

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

poi:

Hit any key to stop autoboot:  0
MMC_SPI: 0 at 0:1 hz 20000000 mode 0

Conclusione: il tempo non stringe. Questo è probabilmente il motivo per cui l'input da tastiera non funziona:

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:
        /* First char of ANSI escape sequence 'e' */
        if (c == 'e') {
            *esc = 1;
            *key = KEY_NONE;
        }
        break;
    case 1:
        /* Second char of ANSI '[' */
        if (c == '[') {
...

Il problema si è rivelato che sono stato un po' troppo intelligente: ho aggiunto la chiave alla configurazione del processore:

  case DTSTimebase => BigInt(0)

... basandosi sul fatto che il commento diceva “se non lo sai, lascia 0”. E dopo tutto WithNBigCores L'ho appena impostato su 1 MHz (come, tra l'altro, era indicato nella configurazione di U-Boot). Ma cavolo, sono pulito e meticoloso: lì non so, qui sono 25MHz! Alla fine, non funziona nulla. Ho rimosso i miei "miglioramenti" e...

Hit any key to stop autoboot:  0
MMC_SPI: 0 at 0:1 hz 20000000 mode 0
## Unknown partition table type 0
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
** No partition table - mmc 0 **
## Info: input data size = 34 = 0x22
Running uEnv.txt boot2...
## Error: "boot2" not defined
HiFive-Unleashed #

Puoi anche inserire comandi! Ad esempio, dopo aver curiosato un po', puoi finalmente indovinare come entrare mmc_spi 1 10000000 0; mmc part, riducendo la frequenza SPI da 20 MHz a 10 MHz. Perché? Bene, la frequenza massima di 20 MHz è stata scritta nella configurazione ed è ancora scritta lì. Ma, a quanto ho capito, le interfacce, almeno qui, funzionano così: il codice divide la frequenza dell'unità hardware (la mia è 25 MHz ovunque) per il target, e imposta il valore risultante come divisore nel controllo corrispondente Registrati. Il problema è che se per un UART da 115200 Hz c'è approssimativamente ciò che è necessario, se dividi 25000000 per 20000000 otterrai 1, cioè funzionerà a 25 MHz. Forse questo è normale, ma se vengono stabilite delle restrizioni, significa che qualcuno ne ha bisogno (ma questo non è certo)... In generale, è più facile metterlo da parte e andare avanti - lontano e, ahimè, per molto tempo. 25 MHz non è un Core i9.

Uscita della console

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

Partition Map for MMC device 0  --   Partition Type: EFI

Part    Start LBA       End LBA         Name
        Attributes
        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() returned FDT_ERR_NOTFOUND
2376 bytes read in 0 ms
Running uEnv.txt boot2...
15332118 bytes read in 0 ms
## Loading kernel from FIT Image at 90000000 ...
   Using 'config-1' configuration
   Trying 'bbl' kernel subimage
     Description:  BBL/SBI/riscv-pk
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x900000d4
     Data Size:    74266 Bytes = 72.5 KiB
     Architecture: RISC-V
     OS:           Linux
     Load Address: 0x80000000
     Entry Point:  0x80000000
     Hash algo:    sha256
     Hash value:   28972571467c4ad0cf08a81d9cf92b9dffc5a7cb2e0cd12fdbb3216cf1f19cbd
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 90000000 ...
   Using 'config-1' configuration
   Trying 'fdt' fdt subimage
     Description:  unavailable
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x90e9d31c
     Data Size:    6911 Bytes = 6.7 KiB
     Architecture: RISC-V
     Load Address: 0x81f00000
     Hash algo:    sha256
     Hash value:   10b0244a5a9205357772ea1c4e135a4f882409262176d8c7191238cff65bb3a8
   Verifying Hash Integrity ... sha256+ OK
   Loading fdt from 0x90e9d31c to 0x81f00000
   Booting using the fdt blob at 0x81f00000
## Loading loadables from FIT Image at 90000000 ...
   Trying 'kernel' loadables subimage
     Description:  Linux kernel
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x900123e8
     Data Size:    10781356 Bytes = 10.3 MiB
     Architecture: RISC-V
     OS:           Linux
     Load Address: 0x80200000
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   72a9847164f4efb2ac9bae736f86efe7e3772ab1f01ae275e427e2a5389c84f0
   Verifying Hash Integrity ... sha256+ OK
   Loading loadables from 0x900123e8 to 0x80200000
## Loading loadables from FIT Image at 90000000 ...
   Trying 'ramdisk' loadables subimage
     Description:  buildroot initramfs
     Type:         RAMDisk Image
     Compression:  gzip compressed
     Data Start:   0x90a5a780
     Data Size:    4467411 Bytes = 4.3 MiB
     Architecture: RISC-V
     OS:           Linux
     Load Address: 0x82000000
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   883dfd33ca047e3ac10d5667ffdef7b8005cac58b95055c2c2beda44bec49bd0
   Verifying Hash Integrity ... sha256+ OK
   Loading loadables from 0x90a5a780 to 0x82000000

Ok, abbiamo raggiunto il livello successivo, ma fa ancora freddo. E a volte cosparge anche di eccezioni. Puoi vedere mcause restando in attesa del codice all'indirizzo specificato $pc e dopo si essere su trap_entry. Il gestore U-Boot stesso può restituire solo mcause = 0..4, quindi preparati a rimanere bloccato in un avvio errato. Poi sono entrato nella configurazione, ho iniziato a guardare cosa stavo cambiando e mi sono ricordato: ecco conf/rvboot-fit.txt è scritto:

fitfile=image.fit
# below much match what's in FIT (ugha)

Bene, rendiamo conformi tutti i file, sostituiamo la riga di comando del kernel con qualcosa di simile, poiché ci sono sospetti che SIF0 - questo è l'output da qualche parte tramite PCIe:

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

E cambiamo l'algoritmo di hashing da SHA-256 a MD5: non ho bisogno della forza crittografica (soprattutto durante il debug), ci vuole un tempo terribilmente lungo e per rilevare errori di integrità durante il caricamento, MD5 è troppo facile. Qual è il risultato finale? Abbiamo iniziato a completare il livello precedente notevolmente più velocemente (grazie all'hashing più semplice) e si è aperto quello successivo:

...
   Verifying Hash Integrity ... md5+ OK
   Loading loadables from 0x90a5a758 to 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
        linux,initrd-end = <0x00000000 0x83000000>;
        linux,initrd-start = <0x00000000 0x82000000>;
        riscv,kernel-end = <0x00000000 0x80a00000>;
        riscv,kernel-start = <0x00000000 0x80200000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
chosen {
        linux,initrd-end = <0x00000000 0x83000000>;
        linux,initrd-start = <0x00000000 0x82000000>;
        riscv,kernel-end = <0x00000000 0x80a00000>;
        riscv,kernel-start = <0x00000000 0x80200000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
};
   Loading Kernel Image ... OK
Booting kernel in
3

Ma il tempo non scorre...

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

Ops, sembra che la correzione dell'orologio si sia rivelata un placebo, anche se all'epoca mi sembrava che fosse d'aiuto. No, ovviamente è da sistemare, ma prima giriamo manualmente le frecce e vediamo cosa succede:

0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Continuing.

Nel frattempo ...

   Loading Kernel Image ... OK
Booting kernel in
3
2
1
0
## Starting application at 0x80000000 ...

No, andrò ad automatizzare l'orologio, altrimenti forse deciderà di calibrare il timer lì!

Nel frattempo, l'indirizzo dell'istruzione attuale punta da qualche parte

0000000080001c20 <poweroff>:
    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 <softfloat_countLeadingZeros8+0x558>
    80001c2e:   e406                    sd      ra,8(sp)
    80001c30:   f7fff0ef                jal     ra,80001bae <printm>
    80001c34:   8522                    mv      a0,s0
    80001c36:   267000ef                jal     ra,8000269c <finisher_exit>
    80001c3a:   00010797                auipc   a5,0x10
    80001c3e:   41e78793                addi    a5,a5,1054 # 80012058 <htif>
    80001c42:   639c                    ld      a5,0(a5)
    80001c44:   c399                    beqz    a5,80001c4a <poweroff+0x2a>
    80001c46:   72c000ef                jal     ra,80002372 <htif_poweroff>
    80001c4a:   45a1                    li      a1,8
    80001c4c:   4501                    li      a0,0
    80001c4e:   dc7ff0ef                jal     ra,80001a14 <send_ipi_many>
    80001c52:   10500073                wfi
    80001c56:   bff5                    j       80001c52 <poweroff+0x32>

all'interno del Berkeley Boot Loader caricato. Personalmente, ciò che mi confonde è la menzione htif — Interfaccia host utilizzata per il lancio tethered del kernel (ovvero, in collaborazione con l'host ARM), ho assunto standalone. Tuttavia, se trovi questa funzione nel codice sorgente, puoi vedere che non tutto è poi così male:

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

Missione: avvia 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) }

Il che si collega a RTC, ovvero al misterioso MockAON, a cui inizialmente avevo pensato: “Allora, cosa abbiamo qui? Non chiaro? Spegniamolo!" Dato che ancora non capisco che tipo di magia dell'orologio stia accadendo lì, reimplementerò semplicemente questa logica in System.scala:

  val rtcDivider = RegInit(0.asUInt(16.W)) // на всякий случай поддержу до 16ГГц, я оптимист :)
  val mhzInt = p(DevKitFPGAFrequencyKey).toInt
  // Преположим, частота равна целому числу мегагерц
  rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
  outer.clintOpt.foreach { clint =>
    clint.module.io.rtcTick := rtcDivider === 0.U
  }

Ci dirigiamo verso il kernel Linux

Qui la storia si è già trascinata ed è diventata un po’ monotona, quindi la descriverò da cima a fondo:

BBL ha ipotizzato la presenza di FDT a 0xF0000000, ma l'ho già corretto! Bene, diamo un'occhiata di nuovo... Trovato in HiFive_U-Boot/arch/riscv/lib/boot.c, sostituito da 0x81F00000, specificato nella configurazione di avvio di U-Boot.

Quindi la BBL si è lamentata del fatto che non c'era memoria. La mia strada risiedeva nella funzione mem_propche riscv-pk/machine/fdt.c: da lì ho imparato che è necessario contrassegnare il nodo ram fdt come device_type = "memory" - quindi, forse, sarà necessario correggere il generatore del processore, ma per ora lo scriverò semplicemente manualmente - comunque, ho trasferito manualmente questo file.

Ora ho ricevuto il messaggio (fornito in forma formattata, con ritorni a capo):

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.

Sembra che le opzioni siano indicate secondo necessità riscv,kernel-start и riscv,kernel-end in DTB, ma gli zeri vengono analizzati. Debug query_chosen ha mostrato che BBL sta tentando di analizzare un indirizzo a 32 bit, ma ne incontra una coppia <0x0 0xADDR>e il primo valore sembra essere quello meno significativo. Aggiunto alla sezione chosen

chosen {
      #address-cells = <1>;
      #size-cells = <0>;
      ...
}

e corretta la generazione dei valori: non aggiungere 0x0 il primo elemento.

Questi 100500 semplici passaggi renderanno facile osservare la caduta di un pinguino:

Testo nascosto

   Verifying Hash Integrity ... md5+ OK
   Loading loadables from 0x90a5a758 to 0x82000000
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
chosen {
        linux,initrd-end = <0x83000000>;
        linux,initrd-start = <0x82000000>;
        riscv,kernel-end = <0x80a00000>;
        riscv,kernel-start = <0x80200000>;
        #address-cells = <0x00000001>;
        #size-cells = <0x00000000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
        stdout-path = "uart0:38400n8";
};
libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND
chosen {
        linux,initrd-end = <0x83000000>;
        linux,initrd-start = <0x82000000>;
        riscv,kernel-end = <0x80a00000>;
        riscv,kernel-start = <0x80200000>;
        #address-cells = <0x00000001>;
        #size-cells = <0x00000000>;
        bootargs = "debug console=tty0 console=ttyS0,125200 root=/dev/mmcblk0p2 rootwait";
        stdout-path = "uart0:38400n8";
};
   Loading Kernel Image ... OK
Booting kernel in
3
2
1
0
## Starting application at 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: Ignoring memory range 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 Wed Jul 3 21:29:21 MSK 2019
[    0.000000] bootconsole [early0] enabled
[    0.000000] Initial ramdisk at: 0x(____ptrval____) (16777216 bytes)
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] On node 0 totalpages: 261632
[    0.000000]   DMA32 zone: 3577 pages used for memmap
[    0.000000]   DMA32 zone: 0 pages reserved
[    0.000000]   DMA32 zone: 261632 pages, LIFO batch:63
[    0.000000] software IO TLB: mapped [mem 0xbb1fc000-0xbf1fc000] (64MB)

(il logo viene visualizzato da BBL e quello con i timestamp viene visualizzato dal kernel).

Fortunatamente, non so come sia ovunque, ma su RocketChip, quando colleghi il debugger tramite JTAG, puoi catturare le trappole fuori dalla scatola: il debugger si fermerà esattamente in questo punto.

Program received signal SIGTRAP, Trace/breakpoint trap.
0xffffffe0000024ca in ?? ()
(gdb) bt
#0  0xffffffe0000024ca in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) file work/linux/vmlinux
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from work/linux/vmlinux...done.
(gdb) bt
#0  0xffffffe0000024ca in setup_smp () at /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75
#1  0x0000000000000000 in ?? ()
Backtrace stopped: frame did not save the 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); // < ВЫ НАХОДИТЕСЬ ЗДЕСЬ
}

Come diceva una vecchia barzelletta, CPU non trovata, emulazione software in esecuzione. Bene, o non correre. 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;

Bel commento dentro linux/arch/riscv/kernel/setup.c - una sorta di pittura di una recinzione utilizzando il metodo Tom Sawyer. In generale, per qualche motivo non ci sono stati vincitori oggi, il premio viene trasferito all'estrazione successiva...

Qui mi propongo di concludere il già lungo articolo.

Continua. Ci sarà un combattimento con un astuto insetto che riesce a nascondersi se ti avvicini lentamente con un solo passo.

Screencast per il download del testo (link esterno):
Parte 3: Quasi caricando Linux dalla scheda SD al RocketChip

Fonte: habr.com

Aggiungi un commento