Pjesa 3: Pothuajse ngarkojmë Linux nga karta SD në RocketChip

Pjesa 3: Pothuajse ngarkojmë Linux nga karta SD në RocketChippjesën e mëparshme u realizua një kontrollues më shumë-më pak funksional, konkretisht – një mbulesë mbi IP Core nga Quartus, e cila shërben si një kalim në TileLink. Sot në rubrikën "Portimi i RocketChip në një kartelë të panjohur kineze me Cyclone" do të shihni një konsolë funksionale. Procesi u zgjat pak: mendova se do ta nisa shpejt Linux-in dhe do të vazhdoja, por nuk ishte ashtu. Në këtë pjesë propozoj të shikoni procesin e ngarkimit të U-Boot, BBL dhe përpjekjet e ngadaltë të kernelit Linux për tu inicializuar. Por konsola është aty – ajo e U-Boot-it, dhe mjaft e avancuar, me shumë nga ato që prisni nga një konsolë e plotë.

Në pjesën harduerike do të shtohet një kartë SD, e lidhur përmes një interfesi SPI, si dhe UART. Në pjesën softuerike, BootROM do të zëvendësohet me xipsdboot dhe, konkretisht, do të shtohen fazat e ngarkimit të mëposhtme (në kartelën SD).

Përmirësimi i pjesës harduerike

Pra, detyra: duhet të kalojmë në një bërthamë "të madhe" dhe të lidhim UART (nga Raspberry) dhe adaptorin SD (mënyra e përdorur ishte një kartë nga Catalex me gjashtë pinë: GND, VCC, MISO, MOSI, SCK, CS).

Në thelb, gjithçka ishte mjaft e thjeshtë. Por para se ta kuptoja, u ndjeva paksa i tunduar: pas herës së kaluar mendova se duhet vetëm të përzihem në Sistemi diçka si HasPeripheryUART (dhe në implementimin përkatës), e njëjta gjë për kartelën SD — dhe gjithçka do të ishte gati. Më pas vendosa të shikoj se si është realizuar në një dizajn "serioz". Pra, çfarë kemi këtu nga serioziteti? Arty, duke dukur, nuk i përshtatet — mbetet monstruoz unleahshed.DevKitConfigs. Dhe papritmas zbulova se atje kishte disa overlay-e, të cilat shtohen përmes parametrave me çelësa. Mësoj se ndoshta kjo është shumë fleksibël dhe konfigurueshme, por do më duhet diçka për të filluar… A e keni një të ngjashme, vetëm më të thjeshtë dhe më të paqëndrueshme?.. Aty unë zbulova vera.iofpga.FPGAChip për FPGA-të Microsemi dhe menjëherë fillova të përdorja citata për të provuar të bëja implementimin tim në bazë të saj, për fat të mirë këtu kishte më shumë-më pak tërë "shtrirjen e pllakës sistemike" në një skedar.

Doli se, me të vërtetë, duhet thjesht të shtojmë në System.scala rreshtat

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

Rreshti në trupin e klasës Sistemi shton informacion rreth frekuencës me të cilën funksionon kjo pjesë e SoC tonë, në skedarin dts. Sa kam kuptuar, DTS/DTB është një equivalent statik i teknologjisë plug-and-play për pajisjet e integruara: struktura e përshkrimit dts kompilohen në një skedar binar dtb dhe dërgohen ngarkuesit në kernel për të mundësuar konfigurimin e duhur të harduerit. E çuditshme, pa rreshtin me tlclock gjithçka sintetizohet mirë, por nuk mund të kompiloj BootROM (të cilin ju kujtoj se tani do të jetë sdboot) — gjatë procesit të kompilimit ai analizon skedarin dts dhe krijon një header me makro TL_CLK, falë të cilit ai do të mund të konfiguronte saktësisht ndarësit e frekuencës për interfesat e jashtme.

Gjithashtu do të kërkohet të bëjmë disa ndryshime në "shtrirje":

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

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

Rregjistrat e zhveshur, për të qenë të sinqertë, janë shtuar thjesht sipas analogjisë me disa vende të tjera të kodit origjinal. Më shumë gjasa, ata duhet të mbrojnë nga metastabiliteti. Ndoshta, në disa blloqe tashmë ka mbrojtjen e saj, por për fillim dua të nisim të paktën "në një nivel të cilësisë". Një pyetje më interesante për mua është – pse MISO dhe MOSI janë lidhur në diferente dq? Ответа я пока так и не нашёл, но, похоже, остальной код рассчитывает именно на такое подключение.

Fizikisht, unë thjesht e vendosa projektin në kontakte të lira në shënjën dhe e ndryshova jumper-in e zgjedhjes së tensionit në 3.3V.

Adaptori SD

Pamja nga lart:

Pjesa 3: Pothuajse ngarkojmë Linux nga karta SD në RocketChip

Pamja nga poshtë:

Pjesa 3: Pothuajse ngarkojmë Linux nga karta SD në RocketChip

Debugging i pjesës softuerike: mjetet

Për të filluar, le të flasim për mjetet e debugimit në dispozicion dhe kufizimet e tyre.

Minicom

Së pari, do të na nevojitet një mënyrë për të lexuar atë që shkruan ngarkuesi dhe kernel. Për këtë, në Linux (në këtë rast – në atë që është në RaspberryPi) do na nevojitet programi Minicom. Në përgjithësi, çdo program për të punuar me port serial do të bëjë.

Vini re që kur e nisim, emri i pajisjes së portit duhet të jepet si -D /dev/ttyS0 — pas opsionit -D. Dhe informacioni kryesor: për të dalë përdorni Ctrl-A, X. Në fakt, kam pasur një rast kur kjo kombinim nuk funksionoi – atëherë mund të themi nga një sesion tjetër SSH thjesht killall -KILL minicom.

Ka është edhe një veçori tjetër. Konkrëtisht në RaspberryPi ka dy UART, dhe të dy portet mund të jenë tashmë të përshtatura për ndonjë gjë: një për Bluetooth, ndërsa tjetri në mënyrë default jep konsolën e bërthamës. Për fat, ky qëndrim mund të rregullohet. sipër këtij manuali..

Riprogramimi i memories.

Gjatë debugging-ut, për të verifikuar hipotezat, ndonjëherë më është dashur të ngarkoj ngarkuesin. (më falni) në memorien operative direkt nga hosti. Ndoshta, kjo mund të bëhet drejtpërdrejt nga GDB, por në fund e mora rrugën e thjeshtë: kopjova skedarin e nevojshëm në Raspberry, e kalova përmes SSH gjithashtu portin 4444 (telnet nga OpenOCD) dhe përdora komandën load_image.Kur e ekzekutoni, duket sikur gjithçka ka ngecur, por në të vërtetë «nuk fle, thjesht pulson ngadalë»: po ngarkohet skedari, thjesht po e bën me një shpejtësi disa kilobajt në sekondë.

Veçoritë e vendosjes së breakpoint-eve.

Ndoshta, shumë njerëz nuk kanë menduar ndonjëherë për këtë kur debug-ojnë programet e zakonshme, por pikat ndaluese nuk vendosen gjithmonë në mënyrë harduerike. Ndonjëherë vendosja e një breakpoint-i përfshin regjistrimin e përkohshëm të një instrukcioni të veçantë në vendin e nevojshëm direct në kodin e makinës.Për shembull, kështu vepronte komanda standarde b në GDB. Ja se çfarë ndodh:

  • nuk mund të vendosni një pikë brenda BootROM, sepse ROM
  • mund të vendosni një pikë ndalimi mbi kodin e ngarkuar në memorien operative nga SD karta, por duhet pritur që ai të ngarkohet. Ndryshe, nuk do të nevojitet të riprogramojmë një copë kod, por ngarkuesi do të riprogramojë breakpoint-in tonë.

Jam i sigurt se mund të kërkoni qartë për të përdorur piketat ndaluese harduerike, por ato janë në çdo rast të numëruara.

Ndërrimi i shpejtë i BootROM.

Në fazën fillestare të debugging-ut shpesh shfaqet dëshira për të rregulluar BootROM dhe të provojnë përsëri. Por ka një problem: BootROM është një pjesë e dizajnit, e ngarkuar në FPGA, dhe sintetizimi i saj zgjat disa minuta (dhe kjo pas një kompilimi pothuajse të menjëhershëm të imazhit të BootROM nga C dhe Assembler…). Për fat, në të vërtetë gjithçka është shumë më shpejt: sekuenca e veprimeve është e tillë:

  • për të ripjellur bootrom.mif (unë kalova në MIF në vend të HEX, sepse me HEX kam pasur gjithmonë disa probleme, ndërsa MIF është formati i natyrshëm i Altera)
  • në Quartus thoni Processing -> Update Memory Initialization File
  • në pikën Assembler (në kolonën e majtë të Detyrave) urdhëroni Filloni përsëri.

Për të gjithë këtë - disa dhjetëra sekonda.

Përgatitja e kartës SD.

Këtu gjithçka është relativisht e thjeshtë, por duhet të keni durim dhe rreth 14Gb hapësirë në disk:

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

Pas kësaj, duhet të fusni një kartë SD të pastër, për të qenë saktë, që nuk përmban asgjë të nevojshme, dhe të ekzekutoni

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

… ku sdX - pajisja e caktuar për kartën. KUJDES: të dhënat në kartë do të fshihen, të ri-përshkruhen dhe gjithashtu! Nuk është shumë e mirë të bëni të gjithë ndërtimin nga sudo, sepse atëherë të gjitha artefaktet e ndërtimit do të jenë në pronësi të root, dhe ndërtimi do të duhet të bëhet nga sudo përhershëm.

Përfundimisht, del një kartë e shënuar në GPT me katër ndarje, një nga të cilat është FAT me uEnv.txt dhe imazhin e ngarkueshëm në formatin FIT (ai përmban disa nën-imazhe, secili me adresën e ngarkimit të tij), ndarja tjetër - e pastër, e cila pritet të formatohet në Ext4 për Linux. Dy ndarjet e tjera - misterioze:në një qëndron U-Boot (ç offset-i, sa kuptoj, është i koduar në BootROM), në tjetrën, duket se qëndrojnë variablat e tij të mjedisit, por unë ende nuk i përdor.

Niveli i parë, BootROM.

Një urtësi popullore thotë: «Nëse në programim ka vallëzime me tambur, në elektronika është edhe me të sjellë zjarr». Është fjala për atë se një herë thuajse ia vura flakën bordit, duke menduar se «No GND - është niveli më i ulët» (dukshëm, një rezistor në të vërtetë do të ndihmonte…) Fjala është më tepër se nëse duar nuk rriten nga drejtimi i duhur, elektronika vazhdon të ofrojë befasi: duke ngjitur një konektor në bord, nuk arrita kurrë të pin lidhjet normalisht - në video tregojnë se si spina vetë përhapet në të gjithë lidhjen, vetëm vendosni hekurin, ndërsa unë e kam pasur, «se bashkuar» si ka rënë. Ndoshta, mund të mos kishte përshtatur temperatura për hekurin; ndoshta diçka tjetër… Në përgjithësi, duke ri-shikuar se kam pasur një dhjetëzinë kontaktet, u dorëzova dhe fillova debug-imin. Dhe këtu erdhi misteri:lidha RX/TX nga UART-i, ngarko firmware-n - ai shkruan

INIT
CMD0
ERROR.

Pra, çdo gjë është logjike - moduli SD nuk e kam lidhur. E rregullojmë situatën, ngarko firmware-n… Dhe heshtje… Çfarë s’kam menduar, ndërsa kutia hapeshin thjesht: një nga pinet e modulit duhej të lidhej me VCC. Në rastin tim, moduli mbështeste 5V për energjimin, kështu që, pa menduar shumë, e lidha kabllin e tërhequr nga moduli në anën e kundërt të bordit. Në fund, konektori i ngjitur keq u deformua dhe просто потерялся контакт UART. facepalm.jpg В общем, «дурная голова ногам покоя не даёт», а кривые руки — голове…

В итоге я увидил в Minicom долгожданное

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /

Более того, оно шевелится крутится индикатор загрузки. Прямо вспоминаются школьные годы и неспешная загрузка MinuetOS с дискеты. Разве что дисковод не скрежещет.

Проблема в том, что после сообщения BOOT не происходит ничего. Значит, самое время подключиться через OpenOCD на Raspberry, к нему GDB на хосте, и посмотреть, что же это такое.

Во-первых, подключение с помощью GDB тут же показало, что $pc (program counter, адрес текущей инструкции) улетает в 0x0 — вероятно, это происходит после множественной ошибки. Поэтому, сразу после выдачи сообщения BOOT добавим бесконечный цикл. Это его ненадолго задержит…

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

Такой хитрый код используется «для надёжности»: я где-то слышал, что, вроде бы, бесконечный цикл — это Undefined Behavior, а тут компилятор вряд ли догадается (Напоминаю, что по 0x10000 находится BootROM).

Pjesa 3: Pothuajse ngarkojmë Linux nga karta SD në RocketChip

Казалось бы, а что ещё ожидать — суровый embedded, какие уж тут исходники. Но ведь в u shqyrtua shkurtimisht teoria e funksionimit të etiketa RFID, dhe u hapen dhe shqyrtuan disa nga etiketat më të zakonshme dhe të arritshme në atë kohë. автор отлаживал сишный код… Крекс-фекс-пекс:

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

Pjesa 3: Pothuajse ngarkojmë Linux nga karta SD në RocketChip

Только нужно грузить не MIF-файл и не bin, а оригинальную версию в формате ELF.

Теперь можно с энной попытки угадать адрес, где выполнение продолжится (это ещё одна причина, почему компилятор не должен был догадаться, что цикл — бесконечный). Команда

set variable $pc=0xADDR

позволяет поменять значение регистра на ходу (в данном случае — адрес текущей инструкции). С её же помощью можно менять значения, записанные в память (и memory-mapped регистры).

В конечном итоге я пришёл к выводу (не уверен, что правильному), что у нас «образ sd-карты не той системы», и переходить нужно не на самое начало загруженных данных, а на 0x89800 байтов дальше:

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

Возможно, на этом также сказалось то, что не имея под рукой ненужной карты на 4Gb, я взял на 2Gb и методом тыка заменил в Makefile DEMO_END=11718750DEMO_END=3078900 (не ищите смысл в конкретном значении — его нет, просто теперь образ помещается на карточку).

Уровень второй, U-Boot

Теперь мы всё ещё «падаем», но оказываемся уже по адресу 0x0000000080089a84. Тут я вынужден признаться: на самом деле, изложение идёт не «со всеми остановками», а частично пишется уже «опосля», поэтому здесь я уже успел подложить правильный dtb-файл от нашего SoC, поправить в настройках HiFive_U-Boot ndryshoren CONFIG_SYS_TEXT_BASE=0x80089800 (вместо 0x08000000), чтобы адрес загрузки совпадал с фактическим. Загружаем теперь уже карту следующего уровня другой образ:

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

И видим:

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

Причём мы прыгаем между строчками 308 и 309. И неудивительно, учитывая, что в $sp лежит значение 0xfffffffe31cdc0a0. Увы, оно ещё и постоянно «убегает» из-за строчки 307. Поэтому попробуем поставить точку останова на trap_entry, а потом снова перейти на 0x80089800 (точку входа U-Boot), и будем надеяться, что оно не требует правильного выставления регистров перед переходом… Похоже, работает:

(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

Так себе указатель стека, прямо скажем: указывает вообще мимо оперативки (если, конечно, у нас ещё нет трансляции адресов, но будем надеяться на простой вариант).

Попробуем заменить указатель на 0x881cf950. В итоге приходим к тому, что handle_trap вызывается и вызывается, при этом уходим в _exit_trap me argumentin epc=2148315240 (в десятичном виде):

(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

Ставим breakpoint на strnlen, продолжаем и видим:

(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

Похоже, _exit_trap хочет выдать отладочную информацию про произошедшее исключение, но у него не получается. Так, что-то у нас исходники опять не отображаются. set directories ../freedom-u-sdk/HiFive_U-Boot/ О! Теперь отображаются!

Что же, запустим ещё раз, и увидим по стек-трейсу причину исходной проблемы, вызвавшей первую ошибку (mcause == 5). Если я правильно понял, что написано këtu на стр. 37, то это исключение означает Load access fault. Причина, по-видимому, в том, что вот здесь

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 имеет то самое некорректное значение, и внутри board_init_f_init_reserve возникает ошибка. Похоже, вот и виновник: переменная с недвусмысленным названием CONFIG_SYS_INIT_SP_ADDR. Она определена в файле HiFive_U-Boot/include/configs/HiFive-U540.h. В какой-то момент я даже подумал, а может, ну его, допиливать загрузчик под процессор — может, легче чуть поправить процессор? Но потом я увидел, что это больше похоже на артефакт от не до конца за-#if 0-енных настроек под другую конфигурацию памяти, и можно попробовать сделать так:

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

В какой-то момент количество костылей технологического крепежа достигло критической отметки. Немного помучавшись, я пришёл к необходимости сделать корректный порт на свою плату. Для этого нужно скопировать и поправить под нашу конфигурацию некоторое количество файлов.

Ну, приблизительно, вот столечко

trosinenko@trosinenko-pc:/hdd/trosinenko/fpga/freedom-u-sdk/HiFive_U-Boot$ git show --name-status
commit 39cd67d59c16ac87b46b51ac1fb58f16f1eb1048 (HEAD -> zeowaa-1gb)
Author: Anatoly Trosinenko <anatoly.trosinenko@gmail.com>
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

Подробности можно посмотреть в repozitorit.

Как оказалось, на этой SiFive-овской плате регистры некоторых устройств имеют другие адреса. А ещё оказалось, что U-Boot конфигурируется уже знакомым по ядру Linux механизмом Kconfig — например, можно скомандовать make menuconfig, и перед вами появится удобный текстовый интерфейс с показом описаний параметров по ? и т.д. В общем, слепив из описаний двух плат описание третьей, выкинув оттуда всякие пафосные перенастройки PLL (видимо, это как-то связано с управлением с хостового компьютера по PCIe, но это не точно), я получил некоторую прошивку, которая при правильной погоде на Марсе выдавала мне по UART сообщение о том, из какого хеша коммита она собрана, и о том, сколько у меня DRAM (но эту информацию я сам же в хедере и прописал).

Жаль только, что после этого плата обычно переставала отвечать по процессорному JTAG, а загрузка с SD-карты — дело, увы, в моей конфигурации не быстрое. С другой стороны, иногда BootROM выдавал сообщение, что ERROR, не удалось загрузиться, и тут же выскакивал U-Boot. Тут-то до меня и дошло: видимо, после перезагрузки bitstream в ПЛИС память не перетирается, не успевает «растренироваться» и т.д. Короче, можно просто при появлении сообщения LOADING / подключаться отладчиком и командовать set variable $pc=0x80089800, минуя тем самым эту долгую загрузку (конечно, в предположении, что оно в прошлый раз сломалось достаточно рано, и не успело поверх оригинального кода что-то загрузить).

Кстати, а это вообще нормально, что процессор напрочь виснет, и к нему не может подключиться JTAG-отладчик с сообщениями

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

Так, постойте! Я это уже видел! Что-то подобное происходит при дедлоке TileLink, а автору контроллера памяти я как-то не доверяю — сам же писал… Внезапно, после первой же удачной пересборки процессора после редактирования контроллера я увидел:

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

На эту странную строчку перед In: serial не обращайте внимания — это я пытался на виснущем процессоре понять, корректно ли оно работает с environment. Что значит, «Уже десять минут так висит»? Оно хотя бы сумело релоцироваться и перейти к загрузочному меню! Небольшое отступление: хоть U-Boot и грузится в числе первых 2^24 байт с SD-карты, запустившись, он копирует себя куда подальше по адресу, то ли записанному в конфигурационном хедере, то ли просто в старшие адреса оперативной памяти, производит релокацию ELF-символов, и передаёт туда управление. Так вот: похоже, этот уровень прошли и бонусом получили процессор, не виснущий намертво после этого.

Итак, почему не работает таймер? Похоже, часы в принципе почему-то не идут…

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

А что, если стрелки вручную покрутить?

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

Atëherë:

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

Вывод: часы не идут. Вероятно, из-за этого же и не работает ввод с клавиатуры:

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 == '[') {
...

Проблема оказалась в том, что я малость перемудрил: я добавил в конфиг процессора ключ:

  case DTSTimebase => BigInt(0)

… ориентируясь на то, что в комментарии было сказано «если не знаете — оставьте 0». И ведь WithNBigCores как раз проставляло его в 1MHz (как, кстати, и было указано в конфиге U-Boot). Но я же, блин, аккуратный и дотошный: там я не знаю, тут 25MHz! В итоге ничего не работает. Убрал свои «улучшения» и…

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 #

Можно даже вводить команды! Например, немного поковырявшись, можно, наконец, догадаться ввести mmc_spi 1 10000000 0; mmc part, уменьшив частоту SPI с 20MHz до 10MHz. Почему? Ну, в конфиге была написана максимальная частота 20MHz, она же там и сейчас написана. Но, насколько я понял, интерфейсы, по крайней мере здесь, работают так: код делит частоту аппаратного блока (у меня — везде 25MHz) на целевую, и выставляет получившееся значение в качестве делителя в соответствующий управляющий регистр. Проблема в том, что если для 115200Hz UART-а будет приблизительно то, что нужно, то если нацело поделить 25000000 на 20000000 получится 1, т.е. работать оно будет на 25MHz. Может, это и нормально, но если ограничения выставляют, значит, это кому-нибудь нужно (но это не точно)… В общем, легче проставить и пойти дальше — далеко и, увы, надолго. 25MHz — это вам не Core i9.

Вывод консоли

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

Mirë, ne kaluam në një nivel të ri, por akoma e ka problem. Dhe ndonjëherë nxjerr edhe përjashtime. Mund ta shohësh mcause-n, duke kapur kodin në adresën e dhënë. $pc dhe pas si të jesh në trap_entry. Vetë procesori nga U-Boot di të shfaqë vetëm për mcause = 0..4, prandaj përgatituni të bini në cikël për ngarkimin e gabuar. Këtu hyra në konfigurim, fillova të shikoja çfarë kam ndryshuar dhe më kujtohej: aty në conf/rvboot-fit.txt shkruar:

fitfile=image.fit
# më poshtë duhet të përputhet me ato që janë në FIT (ugha)

Çfarë do të bëjmë, do t'i sjellim të gjitha skedarët në përputhje, do ta zëvendësojmë komandën e bërthamës në këtë mënyrë, pasi kemi dyshime se SIF0 — kjo është dalja diku përmes PCIe:

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

Dhe për një ditë të tërë do të ndryshojmë algoritmin e heqjes së hash-it nga SHA-256 në MD5: nuk më nevojitet që të jem kriptografikisht i qëndrueshëm (më shumë kur jemi në debug), dhe kap te njëjtat gabime të integritetit dhe MD5 — është mjaft. Pra, çfarë përfundimi kemi? Ne e kaluam nivelin e mëparshëm dukshëm më shpejt (për shkak të heqjes më të thjeshtë të hash-it) dhe hapëm të ardhshëm:

...
   Verifying Hash Integrity ... md5+ OK
   Loading loadables from 0x90a5a758 to 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() returned 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";
};
   Loading Kernel Image ... OK
Booting kernel in
3

Por ora, orët nuk po lëvizin...

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

Ups, duket se rregullimi i orës ishte një placebo, megjithëse më dukej se ndihmoi. Jo, sigurisht që duhet ta rregulloj, por le të fillojmë duke rrotulluar duar manualisht dhe të shohim se çfarë ndodh:

0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=1000000
(gdb) c
Duke vazhduar.
^C
Programi mori sinjalin SIGINT, Prek.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=2000000
(gdb) c
Duke vazhduar.
^C
Programi mori sinjalin SIGINT, Prek.
0x00000000bff6dbb0 in ?? ()
(gdb) set variable *0x0200bff8=3000000
(gdb) c
Duke vazhduar.

Ndërkohë...

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

Jo, do të shkoj të automatizoj orën — ndoshta, mund të mendojë të kalibrojë ndonjëherë timer-in!

Ndërkohë, adresa e instruksionit aktual tregohet diku në

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

në brenda Berkeley Boot Loader që është bootuar. Personal, më shqetëson përmendja htif — interfaci i hostit, që përdoret për nisjen e lidhur të bërthamës (dmth në bashkëpunim me ARM-in host), unë e parashikoja standalone. Megjithatë, nëse e gjej këtë funksion në burimet, dallohet se gjërat nuk janë aq keq:

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

Misioni: nis orën

Kërkimi i regjistrave në CLINT na çon te

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

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

I cili lidhet me RTC, ose me MockAON-in misterioz, për të cilin unë fillimisht mendoja: "Çfarë është kjo këtu? E paqartë? E çaktivizojmë!" Sepse edhe tani nuk e kuptoj se çfarë magjie taktesh ndodh atje, kështu që thjesht do ta riimplementoj këtë logjikë në System.scala:

  val rtcDivider = RegInit(0.asUInt(16.W)) // për çdo rast do ta mbështes deri në 16GHz, unë jam optimist :)
  val mhzInt = p(DevKitFPGAFrequencyKey).toInt
  // Supozoni se frekuenca është një numër i plotë megahercash
  rtcDivider := Mux(rtcDivider === (mhzInt - 1).U, 0.U, rtcDivider + 1.U)
  outer.clintOpt.foreach { clint =>
    clint.module.io.rtcTick := rtcDivider === 0.U
  }

Duke u përpjekur për të hyrë në bërthamën Linux

Këtu tregimi tashmë është shtyrë dhe është bërë pak monotone, prandaj do ta përshkruaj sipërfaqësisht:

BBL supozonte praninë e FDT në adresën 0xF0000000, dhe unë tashmë e kam korrigjuar! Çfarë të bëjmë… Le ta kërkojmë përsëri… Gjetur në HiFive_U-Boot/arch/riscv/lib/boot.c, e zëvendësova me 0x81F00000, e cila është e shënuar në konfigurimin e-ngarkesës U-Boot.

Pastaj BBL u ankua se nuk kishte memorie. Rruga ime të çonte te funksioni mem_prop, që në riscv-pk/machine/fdt.c: nga aty mësova se duhet të shënoj node-n fdt ram si device_type = "memory" — pastaj, ndoshta, do të duhet të rregulloj gjeneruesin e procesorit, por për momentin thjesht do ta shkruaj me dorë — gjithsesi, këtë skedar e kam transferuar manualisht.

Tani kam marrë mesazhin (i dhënë në formë të formatizuar, me kthime karte):

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.

Duket se, dhe janë shënuar ashtu siç duhet opsionet riscv,kernel-start dhe riscv,kernel-end në DTB, por janë analizuar zeros. Debugimi query_chosen tregoi se BBL po përpiqej të analizohej një adresë 32-bit, dhe i duhej një <0x0 0xADDR>, dhe vlera e parë duket se është reduktimi më i vogël. E shkrova në seksionin chosen

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

dhe rregullova gjenerimin e vlerave: për të mos e shkruar si elementin e parë. 0x0 Këto 100500 hapa të thjeshtë do të lejojnë lehtësisht dhe thjesht të shohim se si bie pinguini:

Эти 100500 простых шагов позволят легко и просто посмотреть, как падает пингвин:

Teksti i fshehur

   Verifikimi i Integritetit të Hash-it ... md5+ OK
   Ngarkimi i komponentëve nga 0x90a5a758 në 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() ktheu 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";
};
   Ngarkimi i Imazhit të Kernel-it ... OK
Bootimi i kernel-it në
3
2
1
0
## Duke nisur aplikacionin në 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: Duke injoroni zonën e memories 0x80000000 - 0x80200000
[    0.000000] Versioni Linux 4.19.0-sifive-1+ (trosinenko@trosinenko-pc) (gcc version 8.3.0 (Buildroot 2019.02-07449-g4eddd28f99)) #1 SMP Mër 3 Korrik 21:29:21 MSK 2019
[    0.000000] bootconsole [early0] aktivizuar
[    0.000000] Ramdisk fillestar në: 0x(____ptrval____) (16777216 bytes)
[    0.000000] Zonat e memories:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   [mem 0x00000000c0000000-0x00000bffffffffff]
[    0.000000] Zona e lëvizshme fillon për çdo nod
[    0.000000] Rrugët e hershme të memories për nodin
[    0.000000]   nodi   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Konfigurimi i memories për nodin 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Në nodin 0 totalpages: 261632
[    0.000000]   zona DMA32: 3577 faqe të përdorura për memmap
[    0.000000]   zona DMA32: 0 faqe të rezervuara
[    0.000000]   zona DMA32: 261632 faqe, LIFO batch:63
[    0.000000] softueri IO TLB: mapuar [mem 0xbb1fc000-0xbf1fc000] (64MB)

(emblema shfaqet BBL, ndërsa ajo që ka me etiketat e kohës — është bërthama).

Fatmirësisht, nuk e di si është gjithandej, por në RocketChip, duke u lidhur me debugguer-it përmes JTAG, mund të kapësh trap-at nga kutia — debugguer do të ndalet saktësisht në këtë pikë.

Programi mori sinjal SIGTRAP, dhe rregullimi/kapja e gabimeve.
0xffffffe0000024ca në ?? ()
(gdb) bt
#0  0xffffffe0000024ca në ?? ()
Backtrace ndaluar: çadra e mëparshme identike me këtë çadër (staku i korruptuar?)
(gdb) file work/linux/vmlinux
Një program po debuggueshët tashmë.
A jeni të sigurt që dëshironi të ndryshoni skedarin? (y apo n) y
Leximi i simboleve nga work/linux/vmlinux...përfunduar.
(gdb) bt
#0  0xffffffe0000024ca në setup_smp () në /hdd/trosinenko/fpga/freedom-u-sdk/linux/arch/riscv/kernel/smpboot.c:75
#1  0x0000000000000000 në ?? ()
Backtrace ndaluar: çadra nuk ruajti PC-në

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); // < JENI KËTU
}

Siç thuhet në një anekdotë të vjetër, CPU nuk u gjet, po ekzekutohet emulimi softuerik. Ose ndoshta jo ekzekutohet. Humbur në bërthamën e vetme të procesorit.

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

Një koment i mirë në linux/arch/riscv/kernel/setup.c — një lloj pikture e gardhit sipas metodës së Tom Sawyer. Përndryshe, sot nuk kishte fitues, çmimi do të kalojë në raundin e ardhshëm…

Në këtë pikë propozoj të përfundoj artikullin e sapo zgjedhur.

Vazhdimi vijon. Atje do të ketë një përballje me një gabim të zgjuar, që arrin të fshihet, nëse i qasesh ngadalë me singlestep-in.

Teksti i skrinimit të ngarkesës (Lidhje e jashtme):
Pjesa 3: Pothuajse ngarkojmë Linux nga karta SD në RocketChip

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster