ส่วนที่ 3: เกือบจะโหลด Linux จากการ์ด SD ไปยัง RocketChip

ส่วนที่ 3: เกือบจะโหลด Linux จากการ์ด SD ไปยัง RocketChip В ส่วนก่อนหน้า มีการนำตัวควบคุมหน่วยความจำที่ใช้งานได้ไม่มากก็น้อยมาใช้หรือเป็น wrapper บน IP Core จาก Quartus ซึ่งเป็นอะแดปเตอร์สำหรับ TileLink วันนี้ ในส่วน “เรากำลังย้าย RocketChip ไปยังบอร์ดภาษาจีนที่ไม่ค่อยมีใครรู้จักด้วย Cyclone” คุณจะเห็นคอนโซลที่ใช้งานได้ กระบวนการนี้ใช้เวลานานกว่าเล็กน้อย: ฉันคิดว่าจะเปิดตัว Linux อย่างรวดเร็วและเดินหน้าต่อไป แต่นั่นไม่ใช่กรณีนี้ ในส่วนนี้ผมขอเสนอให้ดูกระบวนการเปิดตัว U-Boot, BBL และความพยายามอันขี้ขลาดของเคอร์เนล Linux ในการเริ่มต้น แต่มีคอนโซล - U-Boot และค่อนข้างล้ำหน้าซึ่งมีสิ่งที่คุณคาดหวังจากคอนโซลที่มีคุณสมบัติครบถ้วนมากมาย

ฮาร์ดแวร์จะมีการ์ด SD ที่เชื่อมต่อผ่านอินเทอร์เฟซ SPI และ UART ในส่วนของซอฟต์แวร์ BootROM จะถูกแทนที่ด้วย xip บน sdboot และในความเป็นจริง มีการเพิ่มขั้นตอนการโหลดต่อไปนี้ (บนการ์ด SD)

เสร็จสิ้นฮาร์ดแวร์

ดังนั้นงาน: คุณต้องเปลี่ยนไปใช้คอร์ "ใหญ่" และเชื่อมต่อ UART (จาก Raspberry) และอะแดปเตอร์ SD (เราใช้การ์ดจาก Catalex ที่มีหกพิน: GND, VCC, MISO, MOSI, SCK, CS) .

โดยหลักการแล้ว ทุกอย่างค่อนข้างง่าย แต่ก่อนที่จะรู้ตัวฉันก็ถูกโยนจากด้านหนึ่งไปอีกด้านเล็กน้อย: หลังจากครั้งก่อนฉันตัดสินใจว่าจะต้องผสมอีกครั้ง System สิ่งที่ต้องการ HasPeripheryUART (และการใช้งานตามนั้น) เช่นเดียวกับการ์ด SD - และทุกอย่างจะพร้อม จากนั้นฉันก็ตัดสินใจว่าจะนำไปปฏิบัติอย่างไรในการออกแบบที่ "จริงจัง" แล้วมันเกี่ยวอะไรกับเรื่องนี้ล่ะ? เห็นได้ชัดว่า Arty ไม่พอดี - สัตว์ประหลาดยังคงอยู่ unleahshed.DevKitConfigs- และทันใดนั้นปรากฎว่ามีโอเวอร์เลย์อยู่ทุกหนทุกแห่งซึ่งถูกเพิ่มผ่านพารามิเตอร์ด้วยคีย์ ฉันเดาว่านี่อาจจะยืดหยุ่นและกำหนดค่าได้มาก แต่อย่างน้อยฉันก็อยากจะใช้งานบางอย่างก่อน... คุณไม่มีอะไรเหมือนกันหรอก มีแต่ง่ายกว่าและน่ารำคาญกว่าเหรอ?.. ตอนนั้นผมเจอ vera.iofpga.FPGAChip สำหรับ Microsemi FPGA และแยกมันออกทันทีเพื่อเสนอราคาและพยายามนำไปใช้โดยการเปรียบเทียบ โชคดีที่มี "เค้าโครงเมนบอร์ด" ทั้งหมดในไฟล์เดียวไม่มากก็น้อย

ปรากฎว่าคุณแค่ต้องเพิ่มจริงๆ System.scala เส้น

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

เส้นในเนื้อหาของชั้นเรียน System เพิ่มข้อมูลเกี่ยวกับความถี่ที่ส่วนนี้ของ SoC ของเราทำงานไปยังไฟล์ dts เท่าที่ฉันเข้าใจ DTS/DTB เป็นอะนาล็อกแบบคงที่ของเทคโนโลยี Plug-and-Play สำหรับอุปกรณ์ฝังตัว: แผนผังคำอธิบาย dts ถูกคอมไพล์เป็นไฟล์ dtb ไบนารีและถ่ายโอนโดย bootloader ไปยังเคอร์เนลเพื่อให้สามารถกำหนดค่าได้อย่างถูกต้อง ฮาร์ดแวร์. ที่น่าสนใจไม่มีบรรทัดด้วย tlclock ทุกอย่างสังเคราะห์ได้อย่างสมบูรณ์แบบ แต่กำลังรวบรวม BootROM (ฉันขอเตือนคุณตอนนี้จะเป็นไปแล้ว sdboot) จะไม่ทำงาน - ในระหว่างกระบวนการคอมไพล์ ไฟล์จะแยกวิเคราะห์ไฟล์ dts และสร้างส่วนหัวด้วยมาโคร TL_CLKต้องขอบคุณที่เขาสามารถกำหนดค่าตัวแบ่งความถี่สำหรับอินเทอร์เฟซภายนอกได้อย่างถูกต้อง

คุณจะต้องแก้ไข "การเดินสาย" เล็กน้อยด้วย:

แพลตฟอร์ม.สกาลา:

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
}

พูดตามตรงแล้ว register chains ถูกเพิ่มเข้ามาง่ายๆ โดยการเปรียบเทียบกับที่อื่นๆ ในโค้ดต้นฉบับ เป็นไปได้มากที่พวกเขาควรป้องกัน การแพร่กระจาย- บางทีก็เข้า. ของบางส่วน บล็อกมีการป้องกันอยู่แล้ว แต่ก่อนอื่นฉันต้องการเปิดตัวอย่างน้อย "ในระดับคุณภาพสูง" คำถามที่น่าสนใจกว่าสำหรับฉันคือเหตุใด MISO และ MOSI จึงมีความแตกต่างกัน dq- ฉันยังไม่พบคำตอบ แต่ดูเหมือนว่าโค้ดที่เหลือจะขึ้นอยู่กับการเชื่อมต่อดังกล่าว

ในทางกายภาพ ฉันเพียงกำหนดพินการออกแบบให้กับหน้าสัมผัสอิสระบนบล็อก และย้ายจัมเปอร์เลือกแรงดันไฟฟ้าไปที่ 3.3V

อะแดปเตอร์ SD

ดูจากด้านบน:

ส่วนที่ 3: เกือบจะโหลด Linux จากการ์ด SD ไปยัง RocketChip

มุมมองด้านล่าง:

ส่วนที่ 3: เกือบจะโหลด Linux จากการ์ด SD ไปยัง RocketChip

การดีบักซอฟต์แวร์: เครื่องมือ

ก่อนอื่น เรามาพูดถึงเครื่องมือแก้ไขจุดบกพร่องที่มีอยู่และข้อจำกัดของเครื่องมือเหล่านี้กัน

มินิคอม

ก่อนอื่นเราจะต้องอ่านว่า bootloader และเคอร์เนลเอาท์พุตคืออะไร ในการดำเนินการนี้บน Linux (ในกรณีนี้คือใน RaspberryPi) เราจำเป็นต้องมีโปรแกรม Minicom โดยทั่วไปแล้ว โปรแกรมใดๆ ที่ทำงานร่วมกับพอร์ตอนุกรมก็สามารถทำได้

โปรดทราบว่าเมื่อเริ่มต้นจะต้องระบุชื่ออุปกรณ์พอร์ตเป็น -D /dev/ttyS0 - หลังจากตัวเลือก -D- ข้อมูลหลัก: เพื่อออกใช้ Ctrl-A, X- จริง ๆ แล้วฉันมีกรณีที่ชุดค่าผสมนี้ไม่ได้ผล - จากนั้นคุณก็สามารถพูดจากเซสชัน SSH ใกล้เคียงได้ killall -KILL minicom.

มีอีกหนึ่งคุณสมบัติ โดยเฉพาะอย่างยิ่ง RaspberryPi มี UART สองตัว และทั้งสองพอร์ตสามารถปรับใช้กับบางอย่างได้แล้ว: พอร์ตหนึ่งสำหรับ Bluetooth และอีกพอร์ตหนึ่งจะส่งสัญญาณเอาต์พุตคอนโซลเคอร์เนลโดยค่าเริ่มต้น โชคดีที่พฤติกรรมนี้สามารถแก้ไขได้ ตามคู่มือนี้.

หน่วยความจำเขียนใหม่

เมื่อทำการดีบัก เพื่อทดสอบสมมติฐาน บางครั้งฉันต้องทำ โหลดบูตโหลดเดอร์ (ขออภัย) เข้าสู่ RAM โดยตรงจากโฮสต์ บางทีสิ่งนี้สามารถทำได้โดยตรงจาก GDB แต่ในที่สุดฉันก็ทำตามเส้นทางง่ายๆ: ฉันคัดลอกไฟล์ที่จำเป็นไปยัง Raspberry แล้วส่งต่อพอร์ต 4444 ผ่าน SSH (telnet จาก OpenOCD) และใช้คำสั่ง load_image- เมื่อคุณทำมัน ดูเหมือนว่าทุกอย่างจะถูกแช่แข็ง แต่ในความเป็นจริง “มันไม่หลับ มันแค่กระพริบช้าๆ”: มันจะดาวน์โหลดไฟล์ เพียงทำความเร็วสองสามกิโลไบต์ต่อวินาที

คุณสมบัติของการติดตั้งเบรกพอยต์

หลายคนอาจไม่จำเป็นต้องคิดถึงเรื่องนี้เมื่อทำการดีบักโปรแกรมทั่วไป แต่จุดพักไม่ได้ถูกกำหนดไว้ในฮาร์ดแวร์เสมอไป บางครั้งการตั้งค่าเบรกพอยต์เกี่ยวข้องกับการจดคำสั่งพิเศษไว้ชั่วคราวในตำแหน่งที่ถูกต้อง ลงในรหัสเครื่องโดยตรง- ตัวอย่างเช่น นี่คือวิธีการทำงานของคำสั่งมาตรฐานของฉัน b ในจีดีบี นี่คือสิ่งต่อไปนี้:

  • คุณไม่สามารถใส่จุดใน BootROM ได้เพราะ ROM
  • คุณสามารถตั้งค่าเบรกพอยต์บนโค้ดที่โหลดลงใน RAM จากการ์ด SD ได้ แต่คุณต้องรอจนกว่าจะโหลด มิฉะนั้น เราจะไม่เขียนโค้ดใหม่สักชิ้น แต่ตัวโหลดจะเขียนเบรกพอยต์ของเราใหม่

ฉันแน่ใจว่าคุณสามารถขอใช้เบรกพอยต์ฮาร์ดแวร์ได้อย่างชัดเจน แต่อย่างไรก็ตาม มีจำนวนจำกัดอยู่แล้ว

การเปลี่ยน BootROM อย่างรวดเร็ว

ในระยะเริ่มต้นของการแก้ไขข้อบกพร่อง มักมีความปรารถนาที่จะแก้ไข BootROM แล้วลองอีกครั้ง แต่มีปัญหา: BootROM เป็นส่วนหนึ่งของการออกแบบที่โหลดลงใน FPGA และการสังเคราะห์จะใช้เวลาไม่กี่นาที (และนี่คือหลังจากการคอมไพล์อิมเมจ BootROM จาก C และ Assembler เกือบจะในทันที...) โชคดีที่ในความเป็นจริงทุกสิ่ง เร็วกว่ามาก: ลำดับของการกระทำมีดังนี้:

  • สร้าง bootrom.mif ใหม่ (ฉันเปลี่ยนมาใช้ MIF แทนที่จะเป็น HEX เพราะฉันมักจะมีปัญหากับ HEX อยู่เสมอ และ MIF เป็นรูปแบบดั้งเดิมของ Alter)
  • ใน Quartus พูด Processing -> Update Memory Initialization File
  • ในรายการแอสเซมเบลอร์ (ในคอลัมน์ด้านซ้ายของงาน) คำสั่งเริ่มใหม่อีกครั้ง

ทุกอย่างเกี่ยวกับทุกสิ่ง - สองสามสิบวินาที

กำลังเตรียมการ์ด SD

ทุกอย่างที่นี่ค่อนข้างง่าย แต่คุณต้องอดทนและมีพื้นที่ดิสก์ประมาณ 14Gb:

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

หลังจากนั้นคุณจะต้องใส่การ์ด SD ที่สะอาดหรือไม่มีการ์ด SD ที่จำเป็นแล้วดำเนินการ

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

… ที่ไหน sdX — อุปกรณ์ที่กำหนดให้กับการ์ด ข้อควรระวัง: ข้อมูลในการ์ดจะถูกลบ เขียนทับ และโดยทั่วไป! การประกอบทั้งหมดจากด้านล่างแทบจะไม่คุ้มเลย sudoเพราะสิ่งประดิษฐ์ที่สร้างขึ้นทั้งหมดจะเป็นของ rootและจะต้องประกอบจากด้านล่าง sudo เสมอต้นเสมอปลาย.

ผลลัพธ์ที่ได้คือการ์ดที่มาร์กอัปใน GPT โดยมีพาร์ติชั่น 4 พาร์ติชั่น ซึ่งหนึ่งในนั้นมี FAT ด้วย uEnv.txt และอิมเมจที่สามารถบู๊ตได้ในรูปแบบ FIT (ประกอบด้วยอิมเมจย่อยหลายอิมเมจ โดยแต่ละอิมเมจมีที่อยู่ดาวน์โหลดของตัวเอง) ส่วนอีกพาร์ติชั่นว่างเปล่า ควรฟอร์แมตใน Ext4 สำหรับ Linux อีกสองส่วน - ลึกลับ: U-Boot อาศัยอยู่บนอันหนึ่ง (เท่าที่ฉันเข้าใจออฟเซ็ตนั้นถูกฮาร์ดโค้ดลงใน BootROM) ในอีกอันหนึ่งดูเหมือนว่าตัวแปรสภาพแวดล้อมจะทำงานอยู่ แต่ฉันยังไม่ได้ใช้มัน

ระดับหนึ่ง BootROM

ภูมิปัญญายอดนิยมกล่าวว่า: “หากในการเขียนโปรแกรมมีการเต้นรำด้วยแทมบูรีน อิเล็กทรอนิกส์ก็ต้องเต้นรำด้วยถังดับเพลิงด้วย” มันไม่ได้เกี่ยวกับความจริงที่ว่าเมื่อฉันเกือบจะเผากระดานโดยตัดสินใจว่า "เอาล่ะ GND ก็อยู่ในระดับต่ำเหมือนกัน" (เห็นได้ชัดว่าตัวต้านทานจะไม่เจ็บเลย...) เป็นเรื่องเกี่ยวกับความจริงที่ว่าหากมือไม่งอกจากที่นั่น อุปกรณ์อิเล็กทรอนิกส์ก็ไม่เคยหยุดที่จะสร้างความประหลาดใจ: เมื่อบัดกรีตัวเชื่อมต่อบนบอร์ด ฉันยังคงไม่สามารถบัดกรีหน้าสัมผัสได้อย่างถูกต้อง - วิดีโอแสดงให้เห็นว่าการบัดกรีกระจายโดยตรงอย่างไร ตลอดการเชื่อมต่อทั้งหมด เพียงแค่ใช้หัวแร้ง สำหรับฉัน เขา "ตบ" แบบสุ่ม บางทีบัดกรีอาจไม่เหมาะกับอุณหภูมิของหัวแร้ง อาจเป็นอย่างอื่น... โดยทั่วไปเมื่อฉันเห็นว่าฉันมีผู้ติดต่อหลายสิบรายแล้วฉันก็ยอมแพ้และเริ่มแก้ไขข้อบกพร่อง แล้วมันก็เริ่มขึ้น ลึกลับ: ฉันเชื่อมต่อ RX/TX จาก UART ฉันโหลดเฟิร์มแวร์ - มันบอกว่า

INIT
CMD0
ERROR

ทุกอย่างเป็นไปตามตรรกะ - ฉันไม่ได้เชื่อมต่อโมดูลการ์ด SD เราแก้ไขสถานการณ์ โหลดเฟิร์มแวร์... และเงียบ... ทำไมฉันไม่เปลี่ยนใจ แต่กล่องเล็กๆ เพิ่งเปิดออก: พินโมดูลอันใดอันหนึ่งต้องเชื่อมต่อกับ VCC ในกรณีของฉัน โมดูลรองรับไฟ 5V ดังนั้นฉันจึงเสียบสายไฟที่มาจากโมดูลไปที่ด้านตรงข้ามของบอร์ดโดยไม่ต้องคิดซ้ำ เป็นผลให้ขั้วต่อที่บัดกรีแบบคดเคี้ยวเอียงและ ผู้ติดต่อ UART หายไป facepalm.jpg โดยทั่วไปแล้ว “หัวที่ไม่ดีทำให้ขาไม่ได้พัก” และมือที่คดเคี้ยวทำให้ศีรษะไม่ได้พัก...

ส่งผลให้ฉันเห็นสิ่งที่รอคอยมานาน

INIT
CMD0
CMD8
ACMD41
CMD58
CMD16
CMD18
LOADING /

ยิ่งไปกว่านั้น มันจะเคลื่อนที่และตัวบ่งชี้การโหลดจะหมุน ฉันจำสมัยเรียนของฉันและการโหลด MinuetOS จากฟล็อปปี้ดิสก์ได้ทันที เว้นแต่ไดรฟ์จะไม่บด

ปัญหาคือหลังจากข้อความ BOOT ไม่มีอะไรเกิดขึ้น ซึ่งหมายความว่าถึงเวลาที่ต้องเชื่อมต่อผ่าน OpenOCD ไปยัง Raspberry, ไปยัง GDB บนโฮสต์ และดูว่าคืออะไร

ประการแรก การเชื่อมต่อโดยใช้ GDB แสดงให้เห็นทันที $pc (ตัวนับโปรแกรม ที่อยู่ของคำสั่งปัจจุบัน) บินไป 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;
 }

รหัสที่ยุ่งยากดังกล่าวถูกใช้ "เพื่อความน่าเชื่อถือ": ฉันได้ยินมาว่าการวนซ้ำไม่รู้จบเป็นพฤติกรรมที่ไม่ได้กำหนด แต่คอมไพเลอร์ไม่น่าจะเดาได้ (ฉันเตือนคุณว่าตาม 0x10000 ตั้งอยู่ BootROM)

ส่วนที่ 3: เกือบจะโหลด Linux จากการ์ด SD ไปยัง RocketChip

ดูเหมือนว่าจะคาดหวังอะไรอีก - การฝังที่รุนแรงมีซอร์สโค้ดประเภทใดบ้าง? แต่ใน บทความนั้น ผู้เขียนกำลังแก้ไขรหัส 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.

ส่วนที่ 3: เกือบจะโหลด Linux จากการ์ด SD ไปยัง RocketChip

คุณเพียงแค่ต้องดาวน์โหลดไม่ใช่ไฟล์ MIF หรือ bin แต่เป็นเวอร์ชันดั้งเดิมในรูปแบบ ELF

ตอนนี้คุณสามารถเดาได้ด้วยการพยายามครั้งที่ n ที่อยู่ที่การดำเนินการจะดำเนินต่อไป (นี่เป็นอีกเหตุผลว่าทำไมคอมไพเลอร์ไม่ควรเดาว่าการวนซ้ำนั้นไม่มีที่สิ้นสุด) ทีม

set variable $pc=0xADDR

ช่วยให้คุณสามารถเปลี่ยนค่ารีจิสเตอร์ได้ทันที (ในกรณีนี้คือที่อยู่ของคำสั่งปัจจุบัน) ด้วยความช่วยเหลือ คุณสามารถเปลี่ยนค่าที่เขียนลงในหน่วยความจำ (และรีจิสเตอร์ที่แมปหน่วยความจำ)

ในที่สุดฉันก็ได้ข้อสรุป (ไม่แน่ใจว่าอันไหนถูกต้อง) ว่าเรามี "อิมเมจการ์ด 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=11718750 บน DEMO_END=3078900 (อย่ามองหาความหมายในความหมายเฉพาะเจาะจง - ไม่มีเลย เพียงแต่ว่าตอนนี้รูปภาพถูกวางลงบนการ์ดแล้ว)

ระดับสอง ยู-บูท

ตอนนี้เรายังคง "ล้ม" อยู่ แต่เรามาถูกที่แล้ว 0x0000000080089a84- ฉันต้องยอมรับที่นี่: อันที่จริงการนำเสนอไม่ได้ไป "ด้วยการหยุดทั้งหมด" แต่เขียนบางส่วนว่า "หลัง" ดังนั้นที่นี่ฉันได้จัดการแทรกไฟล์ dtb ที่ถูกต้องจาก SoC ของเราแล้วแก้ไขในการตั้งค่า HiFive_U-Boot ตัวแปร 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

ตัวชี้สแต็กนั้นค่อนข้างดี พูดตามตรง: มันชี้ผ่าน RAM ไปเลย (เว้นแต่แน่นอนว่าเรายังไม่มีการแปลที่อยู่ แต่หวังว่าจะมีตัวเลือกง่ายๆ)

ลองแทนที่ตัวชี้ด้วย 0x881cf950- จึงทำให้เราได้ข้อสรุปว่า handle_trap เรียกแล้วเรียก และในเวลาเดียวกันเราก็เข้าไป _exit_trap ด้วยการโต้แย้ง 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

ตั้งค่าเบรกพอยต์เป็น 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- ถ้าฉันเข้าใจถูกต้องสิ่งที่เขียน ที่นี่ ในหน้า 37 ข้อยกเว้นนี้หมายถึง Load access fault- เหตุผลน่าจะเป็นที่นี่

ซุ้มประตู/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

สามารถดูรายละเอียดได้ใน ที่เก็บ.

ปรากฎว่าบนบอร์ด SiFive นี้ การลงทะเบียนของอุปกรณ์บางตัวมีที่อยู่ที่แตกต่างกัน ปรากฎว่า U-Boot ได้รับการกำหนดค่าโดยใช้กลไก Kconfig ซึ่งคุ้นเคยจากเคอร์เนล Linux แล้ว - ตัวอย่างเช่นคุณสามารถสั่ง make menuconfigและอินเทอร์เฟซข้อความที่สะดวกสบายจะปรากฏขึ้นตรงหน้าคุณโดยแสดงคำอธิบายของพารามิเตอร์ตาม ? ฯลฯ โดยทั่วไปแล้วการรวมคำอธิบายของอันที่สามจากคำอธิบายของสองบอร์ดเข้าด้วยกันโดยละทิ้งการกำหนดค่า PLL ที่อวดรู้ทุกประเภทออกไป (เห็นได้ชัดว่านี่เชื่อมโยงกับการควบคุมจากคอมพิวเตอร์โฮสต์ผ่าน PCIe แต่ก็ไม่แน่นอน) ฉันได้รับเฟิร์มแวร์ซึ่งในสภาพอากาศที่เหมาะสมบนดาวอังคารส่งข้อความถึงฉันผ่าน UART ว่าคอมมิตแฮชใดที่คอมไพล์มาจาก และเกี่ยวกับจำนวน DRAM ที่ฉันมี (แต่ฉันเองก็เขียนข้อมูลนี้ไว้ที่ส่วนหัว)

น่าเสียดายเพียงอย่างเดียวคือหลังจากนี้บอร์ดมักจะหยุดตอบสนองผ่านโปรเซสเซอร์ JTAG และการโหลดจากการ์ด SD ก็ไม่เร็วในการกำหนดค่าของฉัน ในทางกลับกัน บางครั้ง BootROM ก็ส่งข้อความมาว่า ERRORไม่สามารถบู๊ตได้ และ U-Boot ก็เด้งขึ้นมาทันที ตอนนั้นเองที่ฉันนึกถึง: เห็นได้ชัดว่าหลังจากรีบูตบิตสตรีมใน FPGA หน่วยความจำจะไม่ถูกลบไม่มีเวลา "เลิกฝึกอบรม" ฯลฯ ในระยะสั้นคุณสามารถทำได้เมื่อมีข้อความปรากฏขึ้น 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 อย่าไปสนใจ - ฉันพยายามทำความเข้าใจกับโปรเซสเซอร์แบบแขวนว่ามันทำงานอย่างถูกต้องกับสภาพแวดล้อมหรือไม่ คุณหมายถึงอะไร “มันค้างอยู่แบบนี้มาสิบนาทีแล้ว”? อย่างน้อยมันก็สามารถย้ายตำแหน่งและไปที่เมนูการบู๊ตได้! การพูดนอกเรื่องเล็กน้อย: แม้ว่า U-Boot จะถูกโหลดใน 2^24 ไบต์แรกจากการ์ด SD แต่เมื่อเริ่มต้น มันจะคัดลอกตัวเองไปยังที่อยู่ที่ห่างไกลออกไป ไม่ว่าจะเขียนในส่วนหัวของการกำหนดค่าหรือเพียงไปยังที่อยู่สูงกว่าของ RAM และดำเนินการย้ายตำแหน่งอักขระของ ELF และถ่ายโอนการควบคุมที่นั่น ดูเหมือนว่าเราจะผ่านระดับนี้และได้รับโบนัสที่โปรเซสเซอร์ไม่ได้แขวนแน่นหลังจากนั้น

แล้วทำไมตัวจับเวลาถึงไม่ทำงาน? ดูเหมือนว่านาฬิกาไม่ทำงานด้วยเหตุผลบางอย่าง...

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

จะเกิดอะไรขึ้นถ้าคุณหมุนลูกศรด้วยตนเอง?

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

แล้ว:

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

โอเค เรามาถึงระดับต่อไปแล้ว แต่ยังคงค้างอยู่ และบางครั้งก็มีข้อยกเว้นเกิดขึ้นด้วย คุณสามารถเห็นสาเหตุได้โดยการนอนรอรหัสตามที่อยู่ที่ระบุ $pc และหลังจากนั้น si อยู่บน trap_entry- ตัวจัดการ U-Boot เองสามารถส่งออกเฉพาะ mcause = 0..4 เท่านั้น ดังนั้นเตรียมตัวให้พร้อมที่จะติดค้างในการบู๊ตที่ไม่ถูกต้อง จากนั้นฉันก็เข้าสู่การกำหนดค่า เริ่มดูว่าฉันกำลังเปลี่ยนแปลงอะไร และจำได้ว่า: อยู่ในนั้น conf/rvboot-fit.txt เขียนไว้:

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

เรามานำไฟล์ทั้งหมดมาปฏิบัติตามกันดีกว่าแทนที่บรรทัดคำสั่งเคอร์เนลด้วยสิ่งนี้เนื่องจากมีข้อสงสัยว่า SIF0 - นี่คือเอาต์พุตที่ใดที่หนึ่งผ่าน PCIe:

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

และมาเปลี่ยนอัลกอริธึมการแฮชจาก SHA-256 เป็น MD5 กัน: ฉันไม่ต้องการความรัดกุมของการเข้ารหัส (โดยเฉพาะอย่างยิ่งเมื่อทำการดีบั๊ก) มันใช้เวลานานมาก และในการตรวจพบข้อผิดพลาดด้านความสมบูรณ์ระหว่างการโหลด MD5 นั้นง่ายเกินไป ผลลัพธ์สุดท้ายคืออะไร? เราเริ่มผ่านด่านก่อนหน้านี้เร็วขึ้นอย่างเห็นได้ชัด (เนื่องจากการแฮชที่ง่ายกว่า) และด่านถัดไปก็เปิดขึ้น:

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

แต่นาฬิกาไม่เดิน...

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

อ๊ะ ดูเหมือนว่าการแก้ไขนาฬิกากลับกลายเป็นยาหลอก แม้ว่าในเวลานั้นดูเหมือนว่ามันช่วยได้ก็ตาม ไม่ แน่นอนว่าจำเป็นต้องได้รับการแก้ไข แต่ก่อนอื่นเรามาหมุนลูกศรด้วยตนเองก่อนแล้วดูว่าจะเกิดอะไรขึ้น:

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.

ในขณะเดียวกัน…

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

ไม่ ฉันจะปรับนาฬิกาอัตโนมัติ ไม่เช่นนั้นเขาอาจจะตัดสินใจปรับเทียบนาฬิกาจับเวลาที่นั่น!

ในขณะเดียวกันที่อยู่ของจุดคำสั่งปัจจุบันอยู่ที่ไหนสักแห่งใน

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>

ภายใน Berkeley Boot Loader ที่โหลดอยู่ โดยส่วนตัวแล้วสิ่งที่ทำให้ฉันสับสนเกี่ยวกับเรื่องนี้คือการกล่าวถึง htif — อินเทอร์เฟซโฮสต์ที่ใช้สำหรับการเปิดตัวเคอร์เนลแบบเชื่อมต่ออินเทอร์เน็ต (นั่นคือในความร่วมมือกับโฮสต์ ARM) ฉันถือว่าเป็นแบบสแตนด์อโลน อย่างไรก็ตาม หากคุณพบฟังก์ชันนี้ในซอร์สโค้ด คุณจะเห็นว่าไม่ใช่ทุกอย่างที่แย่ขนาดนั้น:

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

ภารกิจ: เริ่มนาฬิกา

การค้นหาการลงทะเบียนใน CLINT นำเราไปสู่

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

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

ซึ่งเชื่อมต่อกับ RTC หรือ MockAON ลึกลับ ซึ่งตอนแรกฉันคิดว่า: “แล้วเรามีอะไรที่นี่? ไม่ชัดเจน? เรามาปิดมันกันเถอะ!" เนื่องจากฉันยังไม่เข้าใจว่าเวทมนตร์นาฬิกาชนิดใดเกิดขึ้นที่นั่น ดังนั้นฉันจะนำตรรกะนี้ไปใช้ใหม่ 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
  }

มุ่งหน้าสู่เคอร์เนล Linux

ที่นี่เรื่องราวได้ลากยาวไปแล้วและกลายเป็นเรื่องน่าเบื่อเล็กน้อย ดังนั้นฉันจะอธิบายจากบนลงล่าง:

BBL สันนิษฐานว่ามี FDT อยู่ที่ 0xF0000000แต่ฉันแก้ไขมันแล้ว! มาดูกันใหม่ครับ...เจอใน HiFive_U-Boot/arch/riscv/lib/boot.c, แทนที่ด้วย 0x81F00000ระบุไว้ในการกำหนดค่าการบูต U-Boot

แล้ว BBL ก็บ่นว่าไม่มีความทรงจำ เส้นทางของฉันอยู่ในฟังก์ชัน mem_prop, อะไรใน riscv-pk/เครื่องจักร/fdt.c: จากนั้นฉันได้เรียนรู้ว่าคุณต้องทำเครื่องหมาย fdt ram node เป็น device_type = "memory" - บางทีอาจจำเป็นต้องแก้ไขตัวสร้างโปรเซสเซอร์ แต่ตอนนี้ฉันจะเขียนมันด้วยตนเอง - อย่างไรก็ตามฉันถ่ายโอนไฟล์นี้ด้วยตนเอง

ตอนนี้ฉันได้รับข้อความ (มีให้ในรูปแบบที่จัดรูปแบบพร้อมการคืนแคร่):

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.

ดูเหมือนว่ามีการระบุตัวเลือกต่างๆ ตามความจำเป็น riscv,kernel-start и riscv,kernel-end ใน DTB แต่ศูนย์จะถูกแยกวิเคราะห์ การดีบัก query_chosen แสดงให้เห็นว่า BBL กำลังพยายามแยกวิเคราะห์ที่อยู่แบบ 32 บิต แต่กลับเจอที่อยู่คู่กัน <0x0 0xADDR>และค่าแรกดูเหมือนจะเป็นบิตที่มีนัยสำคัญน้อยที่สุด เพิ่มไปยังส่วน chosen

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

และแก้ไขการสร้างค่า: อย่าเพิ่ม 0x0 องค์ประกอบแรก

ขั้นตอนง่ายๆ 100500 ขั้นตอนเหล่านี้จะทำให้การดูนกเพนกวินตกเป็นเรื่องง่าย:

ข้อความที่ซ่อนอยู่

   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)

(โลโก้จะแสดงโดย BBL และโลโก้ที่มีการประทับเวลาจะแสดงโดยเคอร์เนล)

โชคดีที่ฉันไม่รู้ว่ามันเป็นยังไงทุกที่ แต่บน RocketChip เมื่อคุณเชื่อมต่อดีบักเกอร์ผ่าน JTAG คุณสามารถจับกับดักนอกกรอบได้ - ดีบักเกอร์จะหยุดตรงจุดนี้

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

เสรีภาพ-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); // < ВЫ НАХОДИТЕСЬ ЗДЕСЬ
}

ดังคำกล่าวเก่าๆที่ว่า ไม่พบ CPU กำลังเรียกใช้การจำลองซอฟต์แวร์- ดีหรือไม่ทำงาน สูญเสียไปในคอร์โปรเซสเซอร์ตัวเดียว

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

คอมเมนท์ดีๆ ครับ linux/arch/riscv/kernel/setup.c - การทาสีรั้วแบบหนึ่งโดยใช้วิธีของทอม ซอว์เยอร์ โดยทั่วไป ด้วยเหตุผลบางประการที่ไม่มีผู้ชนะในวันนี้ รางวัลจะถูกโอนไปยังงวดถัดไป...

ที่นี่ฉันขอเสนอให้จบบทความที่ยาวอยู่แล้ว

ยังมีต่อ. จะมีการต่อสู้กับแมลงเจ้าเล่ห์ที่สามารถซ่อนตัวได้หากคุณค่อยๆ คืบคลานขึ้นไปบนมันด้วยการก้าวเท้าเดียว

ดาวน์โหลดข้อความ screencast (ลิงค์ภายนอก):
ส่วนที่ 3: เกือบจะโหลด Linux จากการ์ด SD ไปยัง RocketChip

ที่มา: will.com

เพิ่มความคิดเห็น