Μέρος 3: Σχεδόν φόρτωση Linux από κάρτα SD στο RocketChip

Μέρος 3: Σχεδόν φόρτωση Linux από κάρτα SD στο RocketChip В προηγούμενο μέρος εφαρμόστηκε ένας περισσότερο ή λιγότερο ελεγκτής μνήμης εργασίας, ή μάλλον, ένα περιτύλιγμα πάνω από το 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 - και όλα θα είναι έτοιμα. Τότε αποφάσισα να δω πώς υλοποιήθηκε σε ένα «σοβαρό» σχέδιο. Λοιπόν, τι σοβαρό υπάρχει σε αυτό; Ο Άρτι, προφανώς, δεν ταιριάζει - το τέρας παραμένει 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, χάρη στο οποίο θα μπορεί να διαμορφώσει σωστά τους διαιρέτες συχνότητας για εξωτερικές διεπαφές.

Θα χρειαστεί επίσης να διορθώσετε ελαφρώς την "καλωδίωση":

Platform.scala:

class PlatformIO(implicit val p: Parameters) extends Bundle {

...

  // UART
  io.uart_tx := sys.uart(0).txd
  sys.uart(0).rxd := RegNext(RegNext(io.uart_rx))

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

Οι αλυσίδες μητρώου, για να είμαστε ειλικρινείς, προστέθηκαν απλώς κατ' αναλογία με κάποιες άλλες θέσεις στον αρχικό κώδικα. Πιθανότατα, θα πρέπει να προστατεύουν από μετασταθερότητα. Ίσως μέσα μερικά Τα μπλοκ έχουν ήδη τη δική τους προστασία, αλλά πρώτα θέλω να το λανσάρω τουλάχιστον «σε επίπεδο υψηλής ποιότητας». Μια πιο ενδιαφέρουσα ερώτηση για μένα είναι γιατί το MISO και το MOSI μένουν διαφορετικά dq? Δεν έχω βρει ακόμα την απάντηση, αλλά φαίνεται ότι ο υπόλοιπος κώδικας βασίζεται ακριβώς σε μια τέτοια σύνδεση.

Φυσικά, απλώς αντιστοιχούσα τις ακίδες σχεδίασης στις ελεύθερες επαφές στο μπλοκ και μετακίνησα τον βραχυκυκλωτήρα επιλογής τάσης στα 3.3 V.

Προσαρμογέας SD

Άποψη από ψηλά:

Μέρος 3: Σχεδόν φόρτωση Linux από κάρτα SD στο RocketChip

Κάτοψη:

Μέρος 3: Σχεδόν φόρτωση Linux από κάρτα SD στο RocketChip

Λογισμικό εντοπισμού σφαλμάτων: Εργαλεία

Αρχικά, ας μιλήσουμε για τα διαθέσιμα εργαλεία εντοπισμού σφαλμάτων και τους περιορισμούς τους.

Minicom

Αρχικά, θα χρειαστεί να διαβάσουμε με κάποιο τρόπο τι βγάζει ο 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 στο GDB. Ιδού τι ακολουθεί:

  • δεν μπορείτε να βάλετε μια τελεία μέσα στο BootROM επειδή η ROM
  • Μπορείτε να ορίσετε ένα σημείο διακοπής στον κωδικό που έχει φορτωθεί στη μνήμη RAM από την κάρτα SD, αλλά πρέπει να περιμένετε μέχρι να φορτωθεί. Διαφορετικά, δεν θα ξαναγράψουμε ένα κομμάτι κώδικα, αλλά ο φορτωτής θα ξαναγράψει το σημείο διακοπής μας

Είμαι βέβαιος ότι μπορείτε να ζητήσετε ρητά τη χρήση σημείων διακοπής υλικού, αλλά ούτως ή άλλως υπάρχει περιορισμένος αριθμός από αυτά.

Αντικατάσταση Quick BootROM

Στο αρχικό στάδιο του εντοπισμού σφαλμάτων, υπάρχει συχνά η επιθυμία να διορθώσετε το BootROM και να προσπαθήσετε ξανά. Αλλά υπάρχει ένα πρόβλημα: το BootROM είναι μέρος της σχεδίασης που έχει φορτωθεί στο FPGA και η σύνθεσή του είναι θέμα λίγων λεπτών (και αυτό συμβαίνει μετά από σχεδόν άμεση μεταγλώττιση της ίδιας της εικόνας BootROM από το C και το Assembler...). Ευτυχώς, στην πραγματικότητα τα πάντα πολύ πιο γρήγορα: η ακολουθία των ενεργειών είναι η εξής:

  • regenerate bootrom.mif (πέρασα σε MIF αντί για HEX, επειδή πάντα είχα κάποια προβλήματα με το HEX και το MIF είναι η εγγενής μορφή του Alter)
  • σε Quartus πω Processing -> Update Memory Initialization File
  • στο στοιχείο Assembler (στην αριστερή στήλη των Εργασιών) εντολή Έναρξη ξανά

Τα πάντα για τα πάντα - μερικές δεκάδες δευτερόλεπτα.

Προετοιμασία της κάρτας SD

Όλα εδώ είναι σχετικά απλά, αλλά πρέπει να είστε υπομονετικοί και να έχετε περίπου 14 Gb χώρο στο δίσκο:

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

Μετά από αυτό πρέπει να εισαγάγετε μια καθαρή, ή μάλλον, μια που δεν περιέχει τίποτα απαραίτητο, κάρτα SD και να εκτελέσετε

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

… Οπου sdX — συσκευή που έχει εκχωρηθεί στην κάρτα. ΠΡΟΣΟΧΗ: τα δεδομένα της κάρτας θα διαγραφούν, θα αντικατασταθούν και γενικά! Δεν αξίζει σχεδόν καθόλου να κάνετε ολόκληρη τη συναρμολόγηση από κάτω sudoγιατί τότε όλα τα κατασκευαστικά αντικείμενα θα ανήκουν root, και η συναρμολόγηση θα πρέπει να γίνει από κάτω sudo συνεχώς.

Το αποτέλεσμα είναι μια κάρτα σημειωμένη σε GPT με τέσσερα διαμερίσματα, ένα από τα οποία έχει FAT με uEnv.txt και μια εικόνα εκκίνησης σε μορφή FIT (περιέχει πολλές δευτερεύουσες εικόνες, η καθεμία με τη δική της διεύθυνση λήψης), το άλλο διαμέρισμα είναι κενό, υποτίθεται ότι έχει μορφοποιηθεί σε Ext4 για Linux. Δύο ακόμη ενότητες - μυστηριώδης: Το U-Boot ζει στη μία (η μετατόπισή της, απ' όσο καταλαβαίνω, είναι κωδικοποιημένη σε BootROM), στην άλλη, φαίνεται ότι οι μεταβλητές περιβάλλοντος ζουν, αλλά δεν τις χρησιμοποιώ ακόμα.

Επίπεδο ένα, BootROM

Η λαϊκή σοφία λέει: «Αν στον προγραμματισμό υπάρχει χορός με ντέφι, τότε στα ηλεκτρονικά υπάρχει και χορός με πυροσβεστήρα». Δεν πρόκειται καν για το γεγονός ότι κάποτε σχεδόν έκαψα τον πίνακα, αποφασίζοντας ότι «Λοιπόν, το GND είναι το ίδιο χαμηλό επίπεδο». (προφανώς, μια αντίσταση δεν θα έβλαπτε τελικά...) Αφορά περισσότερο το γεγονός ότι αν τα χέρια δεν μεγαλώσουν από εκεί, τότε τα ηλεκτρονικά δεν παύουν να φέρνουν εκπλήξεις: όταν κολλάω τον σύνδεσμο στην πλακέτα, ακόμα δεν μπορούσα να κολλήσω σωστά τις επαφές - το βίντεο δείχνει πώς απλώνεται απευθείας η συγκόλληση σε όλη τη σύνδεση, απλώς εφαρμόστε ένα κολλητήρι, Για μένα, "χαστούκισε" τυχαία. Λοιπόν, μπορεί η συγκόλληση να μην ήταν κατάλληλη για τη θερμοκρασία του κολλητηριού, ίσως κάτι άλλο... Γενικά, όταν είδα ότι είχα ήδη μια ντουζίνα επαφές, τα παράτησα και άρχισα το debugging. Και μετά άρχισε μυστηριώδης: Συνέδεσα RX/TX από UART, φορτώνω το firmware - λέει

INIT
CMD0
ERROR

Λοιπόν, όλα είναι λογικά - δεν συνέδεσα τη μονάδα κάρτας SD. Διορθώνουμε την κατάσταση, φορτώνουμε το υλικολογισμικό... Και σιωπή... Γιατί δεν άλλαξα γνώμη, αλλά το μικρό κουτί μόλις άνοιξε: μία από τις ακίδες της μονάδας έπρεπε να συνδεθεί στο VCC. Στην περίπτωσή μου, η μονάδα υποστήριζε 5 V για τροφοδοσία, οπότε χωρίς να το σκεφτώ δύο φορές, συνδέω το καλώδιο που έρχεται από τη μονάδα στην αντίθετη πλευρά της πλακέτας. Ως αποτέλεσμα, ο στραβά συγκολλημένος σύνδεσμος έγινε λοξός και Η επαφή 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 ή τον κάδο, αλλά την αρχική έκδοση σε μορφή ELF.

Τώρα μπορείτε να μαντέψετε με την nη προσπάθεια τη διεύθυνση όπου θα συνεχιστεί η εκτέλεση (αυτός είναι ένας άλλος λόγος για τον οποίο ο μεταγλωττιστής δεν έπρεπε να μαντέψει ότι ο βρόχος είναι άπειρος). Ομάδα

set variable $pc=0xADDR

σας επιτρέπει να αλλάζετε την τιμή του καταχωρητή εν κινήσει (σε ​​αυτήν την περίπτωση, τη διεύθυνση της τρέχουσας εντολής). Με τη βοήθειά του, μπορείτε να αλλάξετε τις τιμές που έχουν γραφτεί στη μνήμη (και στους καταχωρητές που έχουν αντιστοιχιστεί στη μνήμη).

Τελικά, κατέληξα στο συμπέρασμα (δεν είμαι σίγουρος ποιο είναι σωστό) ότι έχουμε "μια εικόνα κάρτας SD του λάθος συστήματος" και δεν πρέπει να πάμε στην αρχή των δεδομένων που έχουν ληφθεί, αλλά στο 0x89800 bytes περαιτέρω:

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 (μην ψάχνετε για νόημα με συγκεκριμένη έννοια - δεν υπάρχει, απλώς τώρα η εικόνα τοποθετείται στην κάρτα).

Επίπεδο δύο, U-Boot

Τώρα ακόμα «πέφτουμε», αλλά είμαστε ήδη στο σωστό μέρος 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. Ο λόγος φαίνεται να είναι αυτός εδώ

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. Κάποια στιγμή σκέφτηκα, ίσως, καλά, να προσθέσω ένα boot loader για τον επεξεργαστή - ίσως θα ήταν πιο εύκολο να φτιάξω λίγο τον επεξεργαστή; Αλλά μετά είδα ότι έμοιαζε περισσότερο με ένα τεχνούργημα που δεν είχε ολοκληρωθεί πλήρως#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 <[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

Λεπτομέρειες μπορείτε να βρείτε στο αποθετήρια.

Όπως αποδείχθηκε, σε αυτήν την πλακέτα SiFive οι καταχωρητές ορισμένων συσκευών έχουν διαφορετικές διευθύνσεις. Αποδείχθηκε επίσης ότι το U-Boot έχει ρυθμιστεί χρησιμοποιώντας τον μηχανισμό Kconfig, ήδη γνωστό από τον πυρήνα του Linux - για παράδειγμα, μπορείτε να κάνετε εντολή make menuconfig, και μια βολική διεπαφή κειμένου θα εμφανιστεί μπροστά σας που δείχνει περιγραφές των παραμέτρων κατά ? και τα λοιπά. Σε γενικές γραμμές, έχοντας συνδυάσει μια περιγραφή του τρίτου από τις περιγραφές δύο πλακών, ρίχνοντας από εκεί κάθε είδους επιτηδευμένες αναδιαμορφώσεις PLL (προφανώς, αυτό συνδέεται κατά κάποιο τρόπο με τον έλεγχο από τον κεντρικό υπολογιστή μέσω PCIe, αλλά αυτό δεν είναι σίγουρο) , έλαβα κάποιο υλικολογισμικό, το οποίο, με τον κατάλληλο καιρό στον Άρη, μου έδωσε ένα μήνυμα μέσω UART για το από ποιο commit hash έχει μεταγλωττιστεί και για το πόση DRAM έχω (αλλά εγώ ο ίδιος έγραψα αυτές τις πληροφορίες στην κεφαλίδα).

Το μόνο κρίμα είναι ότι μετά από αυτό η πλακέτα συνήθως σταματούσε να ανταποκρίνεται μέσω του επεξεργαστή JTAG και η φόρτωση από μια κάρτα SD, δυστυχώς, δεν είναι γρήγορη στη διαμόρφωση μου. Από την άλλη, μερικές φορές το BootROM έδινε ένα μήνυμα ότι ERROR, απέτυχε να εκκινήσει και το U-Boot εμφανίστηκε αμέσως. Ήταν τότε που με ξημέρωσε: προφανώς, μετά την επανεκκίνηση του bitstream στο 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 byte από την κάρτα 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). Αλλά διάολε, είμαι τακτοποιημένος και σχολαστικός: δεν ξέρω εκεί, εδώ είναι 25 MHz! Τελικά, τίποτα δεν λειτουργεί. Αφαίρεσα τις «βελτιώσεις» μου και...

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. Γιατί; Λοιπόν, η μέγιστη συχνότητα των 20 MHz ήταν γραμμένη στο config, και εξακολουθεί να γράφεται εκεί. Αλλά, απ' όσο καταλαβαίνω, οι διεπαφές, τουλάχιστον εδώ, λειτουργούν ως εξής: ο κώδικας διαιρεί τη συχνότητα της μονάδας υλικού (η δική μου είναι 25 MHz παντού) με τον στόχο και ορίζει την τιμή που προκύπτει ως διαιρέτη στο αντίστοιχο στοιχείο ελέγχου κανω ΕΓΓΡΑΦΗ. Το πρόβλημα είναι ότι αν για ένα UART 115200Hz υπάρχει περίπου αυτό που χρειάζεται, τότε αν διαιρέσετε το 25000000 με το 20000000 θα πάρετε 1, δηλ. θα λειτουργεί στα 25MHz. Ίσως αυτό είναι φυσιολογικό, αλλά αν τεθούν περιορισμοί, σημαίνει ότι κάποιος το χρειάζεται (αλλά αυτό δεν είναι σίγουρο)... Γενικά, είναι πιο εύκολο να το βάλεις κάτω και να προχωρήσεις - μακριά και, δυστυχώς, για πολύ καιρό. Τα 25 MHz δεν είναι 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

Εντάξει, φτάσαμε στο επόμενο επίπεδο, αλλά είναι ακόμα παγωμένο. Και μερικές φορές ραντίζει και εξαιρέσεις. Μπορείτε να δείτε το mcause περιμένοντας τον κωδικό στην καθορισμένη διεύθυνση $pc και μετά si να είναι σε trap_entry. Ο ίδιος ο χειριστής U-Boot μπορεί να δώσει έξοδο μόνο για mcause = 0..4, οπότε ετοιμαστείτε να κολλήσετε σε λανθασμένη εκκίνηση. Μετά μπήκα στο config, άρχισα να κοιτάζω τι άλλαζα και θυμήθηκα: εκεί μέσα 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"); }
  }
}

Quest: ξεκινήστε το ρολόι

Η αναζήτηση μητρώων στο 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/machine/fdt.c: από εκεί έμαθα ότι πρέπει να επισημάνετε τον κόμβο fdt ram ως 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-bit, αλλά συναντά ένα ζεύγος <0x0 0xADDR>, και η πρώτη τιμή φαίνεται να είναι τα λιγότερο σημαντικά bits. Προστέθηκε στην ενότητα 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

free-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 - ένα είδος ζωγραφικής ενός φράχτη με τη μέθοδο Tom Sawyer. Σε γενικές γραμμές, για κάποιο λόγο δεν υπήρξαν νικητές σήμερα, το έπαθλο μεταφέρεται στην επόμενη κλήρωση...

Εδώ προτείνω να τελειώσει το ήδη εκτενές άρθρο.

Συνεχίζεται. Θα υπάρξει μια μάχη με ένα πονηρό ζωύφιο που καταφέρνει να κρυφτεί αν σιγά-σιγά το ξεπεράσετε με ένα βήμα.

Έκδοση οθόνης λήψης κειμένου (εξωτερικός σύνδεσμος):
Μέρος 3: Σχεδόν φόρτωση Linux από κάρτα SD στο RocketChip

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο