ربما يحتاج عدد قليل من موظفي تكنولوجيا المعلومات إلى شرح ماهية Memtest86 + - ربما أصبح بالفعل أكثر أو أقل من المعيار في اختبار ذاكرة الوصول العشوائي على جهاز كمبيوتر. عندما صادفت في أحد الأجزاء السابقة شريط ذاكرة معطلاً مرفقًا باللوحة ، بدا (مع نتبووك مزود بتقنية DDR2) حلاً واضحًا. سؤال آخر هو أنه ، من حيث المبدأ ، كانت العملية غير المستقرة للنظام مرئية للعين المجردة. في الحالات الأكثر صعوبة ، سمعت أنه بالإضافة إلى "التنصت" غير العادي لخلايا الذاكرة إلى ما لا نهاية ، تستخدم هذه الأداة بعض أنماط البيانات الخاصة التي من المحتمل أن يتم اكتشاف الأخطاء في تشغيل DDR عليها. بشكل عام ، شيء رائع ، إنه لأمر مؤسف أنه حتى في الاسم يقول: 86 - "فقط للأنظمة المتوافقة مع x86." أم لا؟
تحت القص ، سترى محاولاتي لتوصيل MemTest86 + v5.1 إلى RISC-V والإجمالي الفرعي. المفسد: يتحرك!
إخلاء المسئولية: تم اختبار المشروع الناتج إلى الحد الأدنى على وجه التحديد من قبلي على مجموعة RocketChip محددة على لوحة محددة. الدقة والسلامة (خاصة في الأنظمة الأخرى) غير مضمونة. استخدام على مسؤوليتك الخاصة. على وجه الخصوص ، لا تتم معالجة مناطق الذاكرة المحجوزة حاليًا بأي طريقة إذا كانت تقع في نطاق ذاكرة الوصول العشوائي.
كما قلت بالفعل ، منذ وقت ليس ببعيد اشتريت اللوحة الأم مع Cyclone IV على AliExpress ، لكن الذاكرة فيه كانت عربات التي تجرها الدواب. لحسن الحظ ، كانت إحدى الميزات المهمة في هذا المنتدى هي استخدام وحدات DDR2 SO-DIMM التقليدية - كما هو الحال في نتبووك القديم الخاص بي. ومع ذلك ، سيكون من المثير للاهتمام الحصول ، إذا جاز التعبير ، على حل مستضاف ذاتيًا لاختبار وحدات الذاكرة (وفي الواقع ، أيضًا وحدة التحكم). كان احتمال تصحيح أخطائي في ظروف الذاكرة السيئة بطريقة ما غير مرضي على الإطلاق. خاصةً لا آمل في حل سريع والاستعداد الذهني لتأجيل إعادة كتابة كاملة في مجمع آخر لفترة طويلة إلى الأبد ، فتحت مقالة ويكيبيديا على Memtest86 + ورأيت فجأة "مكتوب في: C والتجمع" في البطاقة. هم ، وهذا هو ، على الرغم من أن "... 86" ، ولكن لم يكتب بالكامل في المجمع؟ هذا مشجع. يبقى فقط لفهم العلاقة.
لذا ، انتقل إلى memtest.org وقم بتنزيل الإصدار 5.01 ضمن GPL2. لسهولة التطوير ، قمت بإعادة تحميله على جيثب. لحسن الحظ ، مباشرة في أرشيف المصدر ، استقبلنا ملف README.background ، المعنون
علم التشريح وعلم وظائف الأعضاء من Memtest86-SMP
ويوضح في بعض التفاصيل (وحتى مع الصور في شكل ASCII الفن) عملية رفيعة المستوى من التعليمات البرمجية. في بداية المستند ، نرى تخطيطًا ثنائيًا يتكون من bootsect.o
و setup.o
و head.o
وبعض memtest_shared
. من السهل معرفة أنه يتم الحصول على ملفات الكائنات الثلاثة هذه من مصادر المجمّع المناظرة. للوهلة الأولى ، كل شيء آخر مكتوب في C! ليس سيئا ، ليس سيئا ...
نتيجة لذلك ، قمت بنسخ Makefile
إلى Makefile.arch
وبدأت في إعادة كتابة كل شيء ، وحاول التخلص مما لا يتوافق. أولاً وقبل كل شيء ، بالطبع ، كنت بحاجة إلى سلسلة أدوات من أجل RISC-V ، والتي ، لحسن الحظ ، كانت لا تزال معي منذ التجارب السابقة. في البداية ، فكرت في إنشاء منفذ لهندسة 32 بت ، لكنني تذكرت بعد ذلك أنه تم تحميل معالج 64 بت على اللوحة ، وكان لديّ سلسلة riscv64-
بها بادئة riscv64-
.
الانحدار الغنائي: بالطبع ، كان أول شيء هو دراسة مسألة توافق الكود 32 و 64 بت. نتيجة لذلك ، فإن المواصفات الخاصة بالجزء غير المميز من ISA (بنية مجموعة التعليمات) موجودة في الفقرة 1.3 RISC-V ISA Overview
بيان 1.3 RISC-V ISA Overview
:
الميزة الرئيسية لفصل ISAs الأساسي بشكل صريح هي أنه يمكن تحسين كل ISA ISA لاحتياجاته دون الحاجة إلى دعم جميع العمليات اللازمة لمعايير ISA الأساسية الأخرى. على سبيل المثال ، يمكن أن يقوم RV64I بحذف التعليمات و CSRs المطلوبة فقط للتعامل مع السجلات الأضيق في RV32I. يمكن أن تستخدم خيارات RV32I مساحة الترميز المحجوزة على خلاف ذلك للتعليمات المطلوبة فقط من خلال المتغيرات مساحة العنوان الأوسع.
أريد أيضًا أن أشير إلى أن سلسلة الأدوات مع بادئة riscv64-
المحتمل أن تجمع بسهولة رمز 32 بت إذا تم تحديد البنية الهدف بشكل صحيح - المزيد حول ذلك لاحقًا.
في عملية النقل ، من المنطقي الحفاظ على هذه المستندات في متناول اليد:
بناء الإعداد
لنبدأ بالموافقة: أريد الحصول على منفذ مناسب لمزيد من النقل إلى البنيات الأخرى غير x86 و RISC-V. أقترح أيضًا إلقاء أقراص مرنة التمهيد وتفاصيل x86 الأخرى خارج بنية النظام الأساسي المشترك.
ما لدينا في النهاية: هناك ثلاثة ملفات مجمّع: bootsect.S
و setup.S
و head.S
هناك حاجة إلى الأولين فقط عند بدء التشغيل ، والثالث مطلوب لاحقًا عند الانتقال إلى منطقة ذاكرة أخرى. والحقيقة هي أنه من أجل اختبار الذاكرة "تحت نفسه" ، يجب أن ينتقل رمز الاختبار أولاً إلى مكان جديد. يتم جمع ملفات Sich في ELF ، ومن ثم يتم أخذ أقسام الكود والبيانات وما إلى ذلك. علاوة على ذلك ، يتم جمعها في شكل PIC (رمز الوظيفة المستقلة) - في البداية كنت مندهشًا: على الرغم من أن الكود قائم بذاته (أي ، بدون نواة ، libc ، إلخ) ، فإنه يستخدم هذه الميزات المتقدمة.
علاوة على ذلك ، تظهر المعلمات التي تحدد البنية بشكل دوري في Makefile: -march=i486
و -m32
وما شابه. أحتاج إلى كتابة شيء من هذا القبيل ، ومن ثم مثل مصاصة . الوضع مع بنية RISC-V يشبه هذا: هناك rv64
و rv64
(على rv64
، لا يزال هناك أكثر مضمن و rv128 محجوزين للمستقبل ، لكننا لسنا مهتمين بها) ، ويتم تكوين اسم ISA عن طريق تعيين أحرف لهذه البادئة الامتدادات: i
- مجموعة الأعداد الصحيحة الصحيحة ، m
- عدد الأعداد الصحيحة والقسمة ، ... بالطبع ، أود أن أفعل rv64i
، لكن بالكاد سيتم نقل Memtest86 بسهولة إلى الهيكل دون مضاعفة. صحيح ، يبدو أن المترجم سيقوم ببساطة بإنشاء استدعاءات الدوال بدلاً من إرشادات "مشكلة" ، ولكن هناك مخاطرة في البقاء مع أداء منخفض إلى حد كبير (ناهيك عن حقيقة أن هذه الدالات ستحتاج إلى كتابة أو أخذها في مكان ما).
سوف تحتاج أيضا إلى خط ABI. من حيث المبدأ ، تم توضيح أساسيات اصطلاح النداء بالفعل في Volume I
المحدد في "دليل RISC-V التجميع مبرمج" ، لذلك سأفعل فقط شيء مثل
$ riscv64-linux-gnu-gcc-9 -mabi=help riscv64-linux-gnu-gcc-9: error: unrecognized argument in option '-mabi=help' riscv64-linux-gnu-gcc-9: note: valid arguments to '-mabi=' are: ilp32 ilp32d ilp32e ilp32f lp64 lp64d lp64f riscv64-linux-gnu-gcc-9: fatal error: no input files compilation terminated.
وبدون تفكير lp64
، سآخذ lp64
. بالنظر إلى المستقبل ، سأقول أنه مع ABI ، لم تنجح ملفات الرأس من المكتبة القياسية ، لذا أخذت lp64f
، وتمت ترقية ARCH إلى rv64imf
. بدون ذعر ، لا أخطط حقًا لاستخدام النقطة العائمة في منفذي.
نظرًا لأنني لا أريد بطريقة ما أن أعمق في كتابة البرامج النصية head.S
عبر الأنظمة الأساسية - وبالتالي لم أتمكن من العثور على مفاتيح ld على الفور ، فقد قررت الحصول على ملف head.S
المجمّع. تمسّك ببقية الوظائف باستخدام memtest_shared.arch.lds
. لقد طرحت مؤشراً على تنسيق الإخراج والبنية منه (بعد كل شيء ، من الأسهل تغييره من متغير في Makefile) ، كما علقت مؤقتًا على DISCARD
في النهاية ، ولم أتمكن من معرفة الأقسام المحددة لمعلومات تصحيح الأخطاء التي أحتاج إليها. (التطلع إلى المستقبل: معلومات تصحيح أخطاء جيدة ، ولكن يجب إضافة .rela
) بشكل عام ، أكدت نسخة x86 على الحاجة لتناسب 64 .rela
- آمل أن يكون هذا مرتبطًا بطريقة ما بميزات الوضع الحقيقي ولا يهتم بنا على RISC-V . نتيجة لذلك ، سيتم جمع الكائن المشترك مع PIC ، كما هو الحال في النص الأصلي ، سيتم فك الشفرة والبيانات التي سيتم تحميلها في الذاكرة.
نقوم بجمع ... ويقع reloc.c
ملف reloc.c
الأول - يبدو أنه مأخوذ من بعض ld-linux.so
وهو مسؤول عن دعم جدول الأوفست العالمي ، إلخ. وفقا لاتفاقيات الدعوة إلى x86. اتضح أنه يتطلب العمل مباشرة مع السجلات باستخدام إدراج المجمع. لكننا في RISC-V - تم إعداده أصلاً لدعم PIC الأصلي ، لذلك لا تتردد في رمي reloc.c
. علاوة على ذلك ، لا يزال هناك إدراج ، في بعض الأحيان طويلة جدا. لحسن الحظ ، كانوا إما في رمز الاختبار فورًا بعد كود C الموضح ، والذي قاموا بتحسينه (من بينهم قمت مرة أخرى بعمل قطع كاملة من التعليمات البرمجية تم تبديلها بواسطة توجيه المعالج المسبق) أو أي شيء يعتمد على النظام الأساسي ، وبدون ذلك ، في الحالات القصوى ، يمكنني (على الأرجح) تفعل (مثل تشغيل / إيقاف ذاكرة التخزين المؤقت ، وطرح CPUID ، وما إلى ذلك). أخيرًا ، كانت هناك بعض الأشياء مثل استدعاء rdtsc
، وأنا rdtsc
، دون أي مشاكل كبيرة وضعت في رأس معتمد على النظام الأساسي rdtsc
وفقًا للوثائق الموجودة على RISC-V.
نتيجة لذلك ، حصلنا على دليل arch/i386
، حيث تم نقل قدر كبير من رمز دعم PCI ، وقراءة المعلومات من الشرائح ، وتعريفات خاصة بالنظام الأساسي للعناوين المعينة بالذاكرة ، إلخ. أيضًا ، test_start
بداية دالة test_start
، وهي نقطة الدخول من setup.S
إلى الكود C. كم من الوقت ، قصير ، ولكن مع التعليق على كل ما هو ممكن وتحقيق كل شيء لا يمكن التعليق عليه ضمن RISC-V (مثل setup.S
ورمز العمل مع المنفذ التسلسلي في تنفيذ SiFive) ، حصلت على دليل arch/riscv
، والذي تم تجميع كل شيء به بشكل أو بآخر.
أنا مضطر هنا لتوضيح أن التجارب نفسها قد أجريت جزئيًا قبل كتابة المقال ، لذلك قد يحتوي تسلسل محدد من الإجراءات على قدر معين من "الخيال الفني". ومع ذلك ، أحاول على الأقل إجراء العرض التقديمي بطريقة تمثل على أي حال أحد المسارات الممكنة (أنا مبرمج ، أتذكر ذلك) . لذلك دعونا نرى كيفية بدء كل شيء.
يعمل على الحديد
منذ التجارب السابقة ، لا يزال لدي "حامل" مترب من Raspberry Pi ، تم توصيله بلوحة التصحيح. توفر الأسلاك UART و JTAG ومحولًا مع بطاقة SD. يتم مخيط معالج RV64 معين مع وحدة تحكم DDR2 في ذاكرة التكوين. كما في المرات السابقة ، أقوم بتشغيل "التوت" ، وفتح جلستين SSH قبله ، واحدة منها تعيد توجيه منفذ TCP 3333 لتوصيل gdb إلى OpenOCD. في إحدى الجلسات ، أبدأ minicom لمشاهدة UART ، في جلسة أخرى - openocd لتصحيح الأخطاء من المضيف عبر JTAG. أقوم بتشغيل لوحة - والرسائل في وحدة التحكم حول كيفية تحميل البيانات من SD المدى.
الآن يمكنك تشغيل الأمر:
riscv64-unknown-elf-gdb \ -ex 'target remote 127.0.0.1:3333' \ -ex 'restore /path/to/memtest_shared.bin binary 0x80010000' \ -ex 'add-symbol-file /path/to/memtest_shared 0x80010000' -ex 'set $pc=0x80010000'
-ex
خيارات -ex
بالتظاهر بأن المستخدم قد أدخل هذه الأوامر من وحدة التحكم:
- الأول تأسيس اتصال مع OpenOCD
- الثاني بنسخ محتويات ملف المضيف المحدد إلى العنوان المحدد
- يوضح الجزء الثالث لـ gdb أن المعلومات حول الكود المصدري يجب أن تؤخذ من هذا الملف ، مع الأخذ في الاعتبار حقيقة أنه تم تنزيله على هذا العنوان (وليس ما هو مبين في نفسه)
- ملاحظة: نأخذ الأحرف من ملف ELF ، ونحمّل الثنائي "الخام"
- أخيرًا ، يقوم الرابع بترجمة مؤشر الأمر الحالي إلى التعليمات البرمجية الخاصة بنا
لسوء الحظ ، لا يسير كل شيء بسلاسة تمامًا ، وعلى الرغم من عرض أسطر التعليمات البرمجية في المصحح بشكل صحيح ، ولكن في جميع المتغيرات العامة - الأصفار. في الواقع ، إذا قمنا بتنفيذ أمر من النموذج p &global_var
في p &global_var
، فإننا ، للأسف ، نرى العنوان وفقًا لعنوان التنزيل الأولي (لدي 0x0
) ، والذي لم يتم تحديده باستخدام add-symbol-file
. كعاد ، ولكن حل بسيط للغاية ، لقد أضفت 0x80010000
ببساطة إلى العنوان المحدد يدويا ونظرت إلى محتويات الذاكرة من خلال x/x 0xADDR
. في الواقع ، سيكون من الممكن الإشارة مؤقتًا إلى عنوان البدء الصحيح في البرنامج النصي للرابط ، والذي سيتزامن في الوقت الحالي مع عنوان التنزيل في تكوين الاختبار هذا .
ميزات النقل على المباني الحديثة
حسنًا ، كيفية تنزيل الكود بطريقة أو بأخرى اكتشفناه - بدأنا تشغيله. لا يعمل يُظهر تصحيح الأخطاء خطوة بخطوة أننا نقع أثناء تشغيل وظيفة switch_to_main_stack
- يبدو أنه لا يزال يحاول استخدام القيمة غير المفصلة لعنوان الرمز المطابق لمكدس العمل.
ومع ذلك ، فإن المجلد الأول من الوثائق يخبرنا بالتعليمات الزائفة المختلفة وعملهم مع PIC داخل وخارج:

كما ترون ، المبدأ العام هو أن العناوين في الذاكرة تحسب من التعليمات الحالية ، مع إضافة الأول أعلى الإزاحة ، add
التالية تلميع البتات منخفضة الترتيب. بالكاد يساعد على إعلان متغير عالمي مثل
struct vars * const v = &variables;
لذلك ، نحن نأخذ وثائق RISC-V ELF psABI مع وصف لأنواع عمليات الترحيل reloc.c
الجزء الخاص reloc.c
الأساسي من أجل reloc.c
. هنا تجدر الإشارة إلى أن الملف الأصلي ، على ما يبدو ، قد أخذ من كود النظام الأساسي المشترك. هناك ، حتى بدلاً من تحديد عمق بت معين ، يتم استخدام وحدات ماكرو من نوع ElfW(Addr)
، Elf32_Addr
في Elf32_Addr
أو Elf64_Addr
. ومع ذلك ، ليس في كل مكان ، وهذا هو السبب في أننا arch/riscv/reloc.inc.c
حيث أنهم ليسوا في الكود العام (وكذلك في arch/riscv/reloc.inc.c
- بعد كل شيء ، بالنسبة إلى RISC-V ، لا يوجد أي معنى خاص arch/riscv/reloc.inc.c
بعمق بت محدد ، حيث لا يوجد مطلوب).
نتيجة لذلك ، بدأ switch_to_main_stack
بالمرور (وليس بدون إرشادات المجمّع التي تعتمد على النظام الأساسي ، بالطبع). يظهر المصحح المتغيرات العمومية لا يزال crookedly. حسنا ، حسنا :(
تعريف الأجهزة
بالطبع ، بالنسبة للاختبارات ، سيكون من الممكن استخدام الثوابت الثابتة الترميز بدلاً من كود تعريف المعدات الذي تم طرحه ، لكن بالنسبة لكل مجموعة محددة من المعالجات ، فإن إعادة بناء memtest مكلفة للغاية وفقًا لمعايير طلبي. لذلك ، سوف نتصرف "كبالغين جادين". لحسن الحظ ، على RISC-V (وربما في معظم البنيات الحديثة) ، من المعتاد أن يقوم محمل الإقلاع بتمرير رمز إلى Device Tree Blob ، وهو إصدار مترجم من وصف DTS مثل هذا:
zeowaa-1gb.dts /dts-v1/; / { #address-cells = ^_^lt gt^_^; #size-cells = ^_^lt gt^_^; compatible = "freechips,rocketchip-unknown-dev"; model = "freechips,rocketchip-unknown"; chosen { bootargs = "console=ttySIF0,125200 debug loglevel=7"; }; firmware { sifive,uboot = "YYYY-MM-DD"; }; L16: aliases { serial0 = &L8; }; L15: cpus { #address-cells = ^_^lt gt^_^; #size-cells = ^_^lt�gt^_^; timebase-frequency = ^_^ltgt^_^; L5: cpu@0 { device_type = "cpu"; clock-frequency = ^_^lt�gt^_^; compatible = "sifive,rocket0", "riscv"; d-cache-block-size = ^_^lt gt^_^; d-cache-sets = ^_^lt@gt^_^; d-cache-size = ^_^ltကgt^_^; d-tlb-sets = ^_^lt gt^_^; d-tlb-size = ^_^lt gt^_^; i-cache-block-size = ^_^lt gt^_^; i-cache-sets = ^_^lt@gt^_^; i-cache-size = ^_^ltကgt^_^; i-tlb-sets = ^_^lt gt^_^; i-tlb-size = ^_^lt gt^_^; mmu-type = "riscv,sv39"; next-level-cache = <&L10>; reg = <0x0>; riscv,isa = "rv64imafdc"; status = "okay"; timebase-frequency = ^_^ltgt^_^; tlb-split; L3: interrupt-controller { #interrupt-cells = ^_^lt gt^_^; compatible = "riscv,cpu-intc"; interrupt-controller; }; }; }; L10: ram@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x40000000>; reg-names = "mem"; }; L14: soc { #address-cells = ^_^lt gt^_^; #size-cells = ^_^lt gt^_^; compatible = "freechips,rocketchip-unknown-soc", "simple-bus"; ranges; L1: clint@2000000 { compatible = "riscv,clint0"; interrupts-extended = <&L3 3 &L3 7>; reg = <0x2000000 0x10000>; reg-names = "control"; }; L2: debug-controller@0 { compatible = "sifive,debug-013", "riscv,debug-013"; interrupts-extended = <&L3 65535>; reg = <0x0 0x1000>; reg-names = "control"; }; L9: gpio@64002000 { #gpio-cells = ^_^lt gt^_^; #interrupt-cells = ^_^lt gt^_^; compatible = "sifive,gpio0"; gpio-controller; interrupt-controller; interrupt-parent = <&L0>; interrupts = <3 4 5 6 7 8>; reg = <0x64002000 0x1000>; reg-names = "control"; }; L0: interrupt-controller@c000000 { #interrupt-cells = ^_^lt gt^_^; compatible = "riscv,plic0"; interrupt-controller; interrupts-extended = <&L3 11 &L3 9>; reg = <0xc000000 0x4000000>; reg-names = "control"; riscv,max-priority = ^_^lt gt^_^; riscv,ndev = ^_^lt gt^_^; }; L6: rom@10000 { compatible = "sifive,maskrom0"; reg = <0x10000 0x2000>; reg-names = "mem"; }; L8: serial@64000000 { compatible = "sifive,uart0"; interrupt-parent = <&L0>; clocks = <&tlclk>; interrupts = ^_^lt gt^_^; reg = <0x64000000 0x1000>; reg-names = "control"; }; L7: spi@64001000 { #address-cells = ^_^lt gt^_^; #size-cells = ^_^lt�gt^_^; compatible = "sifive,spi0"; interrupt-parent = <&L0>; interrupts = ^_^lt gt^_^; reg = <0x64001000 0x1000>; clocks = <&tlclk>; reg-names = "control"; L12: mmc@0 { compatible = "mmc-spi-slot"; disable-wp; reg = <0x0>; spi-max-frequency = ^_^lt gt^_^; voltage-ranges = <3300 3300>; }; }; tlclk: tlclk { #clock-cells = ^_^lt�gt^_^; clock-frequency = ^_^lt gt^_^; clock-output-names = "tlclk"; compatible = "fixed-clock"; }; }; };
اعتدت على تحليل ملفات ELF ، لكنني الآن مقتنع مرة أخرى بشجرة الجهاز المسطحة (FDT): تتم كتابة هذه المواصفات الكريمة بواسطة أشخاص مهتمين جيدًا (لا يزال ، هم أنفسهم ثم تحليل ذلك!) وتحليل هذه الملفات (على الأقل حتى تحتاج إلى معالجة مدخلات غير موثوق بها) لا يمثل أي مشاكل خاصة. هنا: في بداية الملف ، توجد بنية رأس بسيطة تحتوي على رقم سحري 0xd00dfeed
وعدد قليل من الحقول. نحن مهتمون بإزاحة "الشجرة المسطحة" off_dt_struct
وجدول الصفوف off_dt_strings
. في الواقع ، تحتاج أيضًا إلى معالجة off_mem_rsvmap
، والذي يعدد مناطق الذاكرة التي يجب تجنبها. ما زلت أتجاهلهم (ليسوا على لوحتي) ، لكن لا تكرر هذا في المنزل .
من حيث المبدأ ، المعالجة ليست صعبة بشكل خاص: تحتاج فقط إلى المشي على شجرة مسطحة وفقًا للرموز. هناك ثلاثة رموز رئيسية :
حسنًا ، بشكل عام ، هذا كل شيء: نذهب إلى هذا القسم ، دون أن ننسى ملاحظة المحاذاة بمقدار 4 بايت. أوه نعم ، ذبابة في المرهم: الأرقام في FDT تكون بتنسيق endian الكبير ، لذلك نقوم بعمل بسيط
static inline uint32_t be32(uint32_t x) { return (x << 24) | (x >> 24) | ((x & 0xff0000) >> 8) | ((x & 0xff00) << 8); }
نتيجة لذلك ، في riscv_entry
أول شيء يجب فعله هو تحليل FDT ، head.S
أن جزء head.S
المسؤول عن نقل التحكم إلى riscv_entry
يشبه هذا
.globl startup_32 # -- ... startup_32: lla sp, boot_stack_top mv s0, a0 # s0, s1 -- callee-saved mv s1, a1 # ... .bss # jal _dl_start # mv a0, s0 mv a1, s1 j riscv_entry
في السجل a0
حصلنا على معرّف hart (hart يشبه دفق الأجهزة في مصطلحات RISC-V) - أنا لا أستخدمه بعد ، وسيتعين علي معرفة ذلك في حالة مفردة الترابط. في a1
يضع أداة تحميل التشغيل مؤشرًا إلى FDT. void riscv_entry(ulong hartid, uint8_t *fdt_address)
إلى الدالة void riscv_entry(ulong hartid, uint8_t *fdt_address)
.
الآن ، مع ظهور FDT parsilka في الكود ، أصبح تسلسل تحميل اللوحة كما يلي:
ومن جانب gdb ، تتم إضافة الأمر
كما اتضح ، بالإضافة إلى إدراج المجمّع ، هناك أيضًا عناوين ذاكرة معروفة. على سبيل المثال SCREEN_ADR
(تمامًا مثل ذلك ، مع D
واحد) ، والذي يشير إلى المنطقة المقابلة لما يتم عرضه على الشاشة. عندما صادفت هذا ، وضعت ببساطة مع لفتة واسعة كل ما يشير إليها تحت #if HAS_SCREEN
، ثم لفترة طويلة تصحيح عمياء. لقد فكرت بالفعل يدويًا مرة واحدة في بعض الوقت في تفريغ هذا الكل إلى وحدة التحكم ، لكن بعد ذلك لاحظت أن نفس الرمز المؤلم يخرج العديد من تسلسل الهروب إلى المنفذ التسلسلي. اتضح أن كل شيء قد كتب بالفعل أمامنا ، ما عليك سوى وضع التعريفات بدقة أكبر - وهنا ، الواجهة المألوفة (وإن كانت بالأبيض والأسود) في نافذة minicom! (في الوقت الحالي ، لا يتم استخدام HAS_SCREEN على الإطلاق - لقد بدأت للتو مجموعة dummy_con
لتغيير الرمز الأصلي كحد أدنى.)
تصحيح الأخطاء على QEMU
لذلك قمت بتصحيح كل شيء على لوحة حقيقية ، ومنذ بعض الوقت الآن - ولا حتى عمياء. ولكن كل شيء يبطئ على JTAG - الرعب! حسنًا ، في النهاية ، يجب أن يعمل كل شيء على أجهزة حقيقية ، ولكن سيكون من الجيد تصحيح الأخطاء على QEMU. بعد عدد معين من التجارب ، تبين أن شيئًا ما غريبًا ، ولكنه مشابه جدًا للعمل مع لوحة:
$ qemu-system-riscv64 -M help Supported machines are: none empty machine sifive_e RISC-V Board compatible with SiFive E SDK sifive_u RISC-V Board compatible with SiFive U SDK spike_v1.10 RISC-V Spike Board (Privileged ISA v1.10) (default) spike_v1.9.1 RISC-V Spike Board (Privileged ISA v1.9.1) virt RISC-V VirtIO Board (Privileged ISA v1.10)
نحن ننظر إلى أي مجالس QEMU مستعدة لمحاكاة. أنا مهتم sifive_u
متوافقة مع sifive_u
.
$ qemu-system-riscv64 -M sifive_u,dumpdtb -m 1g # - QEMU on -- strace $ ls -l on -rw-rw-r-- 1 trosinenko trosinenko 1923 19 20:14 on $ dtc -I dtb < on > on.dts # $ vim on.dts # bootargs $ dtc < on.dts > on.dtb <stdout>: Warning (clocks_property): /soc/ethernet@100900fc:clocks: cell 0 is not a phandle reference <stdout>: Warning (clocks_property): /soc/ethernet@100900fc:clocks: cell 1 is not a phandle reference <stdout>: Warning (clocks_property): /soc/ethernet@100900fc:clocks: cell 2 is not a phandle reference <stdout>: Warning (interrupts_extended_property): /soc/interrupt-controller@c000000:interrupts-extended: cell 0 is not a phandle reference <stdout>: Warning (interrupts_extended_property): /soc/interrupt-controller@c000000:interrupts-extended: cell 2 is not a phandle reference <stdout>: Warning (interrupts_extended_property): /soc/clint@2000000:interrupts-extended: cell 0 is not a phandle reference <stdout>: Warning (interrupts_extended_property): /soc/clint@2000000:interrupts-extended: cell 2 is not a phandle reference
الآن لدينا blob شجرة الجهاز "ثابت". بدون تغيير تكوين VM (عكازات!) ، قم بتشغيل:
qemu-system-riscv64 \ -M sifive_u -m 1g \ -serial stdio \ -s -S
-serial stdio
يعيد توجيه المنفذ التسلسلي إلى وحدة التحكم ، لأنه سيتم استخدام تسلسل الهروب بنشاط. تزيد خيارات -s -S
gdbserver وإنشاء VM للتوقف ، على التوالي. يمكنك تنزيل الكود باستخدام loader
، ولكن عليك إعادة تشغيل QEMU في كل مرة.
يمكنك الاتصال باستخدام
riscv64-unknown-elf-gdb \ -ex 'target remote 127.0.0.1:1234' \ -ex 'restore /path/to/on.dtb binary 0x80100000' \ -ex 'restore /path/to/memtest_shared.bin binary 0x80020000' \ -ex 'add-symbol-file memtest_shared 0x80100000' \ -ex 'set $a1=0x80020000' \ -ex 'set $pc=0x80100000'
نتيجة لذلك ، كل شيء يعمل أكثر من بذكاء!
المبدأ العام للعمل
, , , Memtest86+ btrace
, , ( , QEMU):

, , memtest . , (, trap): , , QEMU - ! «» Illegal instruction
, . mcause
(?), — mepc
(?), — mtval
( ?), .

, :
head.S:
# # = 0 --- , # , , ... lla t1, _trap_entry csrw mtvec, t1 # ... _trap_entry: csrr a0, mcause csrr a1, mepc csrr a2, mtval jal riscv_trap_entry
, calling convention, . memtest, HiFive_U-Boot, Volume II
:
arch.c:
static const char *errors[] = { "Instruction address misaligned", "Instruction access fault", "Illegal instruction", "Breakpoint", "Load address misaligned", "Load access fault", "Store/AMO address misaligned", "Store/AMO access fault", ^_^quot quot^_^, ^_^quot quot^_^, ^_^quot quot^_^, ^_^quot quot^_^, "Instruction page fault", "Load page fault", ^_^quot quot^_^, "Store/AMO page fault", }; void riscv_trap_entry(ulong cause, ulong epc, ulong tval) { char buf[32]; cprint(12, 0, "EXCP: "); if (cause < sizeof(errors) / sizeof(errors[0])) { cprint(12, 8, errors[cause]); } else { itoa(buf, cause); cprint(12, 8, buf); } cprint(13, 0, "PC: "); hprint3(13, 8, epc, 8); cprint(14, 0, "Addr: "); hprint3(14, 8, tval, 8); HALT(); }
— « » . , «» , , , .
: . , memtest : : « , , . ». : do_test
main.c
2, ( ), — «» , memtest. , run_at
, memtest _start
_end
( «» ), - spinlock' goto *addr;
. , , «» , «».
, bss
— _dl_start
, riscv_entry
, trap entry. , : L1I-, . , fence.i
.
, Memtest86+ — , barrier_s
. , . , , .
, : . : . : , - (Own Address, ) . , , . . - . , x86 , , uint64_t
0x80000002
. , : , load/store x86 , — . , QEMU , « , ».
, , — unaligned access ..
, , RocketChip, — QEMU, , , RocketChip — unaligned access trap, QEMU « ».
«misaligned» ,
Changed description of misaligned load and store behavior. The specification now allows visible misaligned address traps in execution environment interfaces, rather than just mandating invisible handling of misaligned loads and stores in user mode. Also, now allows access exceptions to be reported for misaligned accesses (including atomics) that should not be emulated.
, , — , user-mode code , . . , , . , — - machine mode . , rdtsc
(x86) rdtime
(rv64), trap, . , , memory-mapped .
: , low_test_addr
( ), , fdt . , , low_test_addr
, , 2 high_test_adr
… , — : head.S
initial_load_addr
, riscv_entry
move_to_correct_addr
:
static void move_to_correct_addr(void) { uintptr_t cur_start = (uintptr_t)&_start; uintptr_t cur_end = (uintptr_t)&_end; if (cur_start == low_test_addr || cur_start == high_test_adr) {
, — , memtest , RAM - . RISC-V , v->plim_lower
.
, «» , -, — test.c
ulong
( unsigneg long
), 32- x86 uint32_t
, « 64 » uint64_t
. «!!! Good: ffffffff Real: ffffffff Bad bits: 00000000». ? - -1, 32 1. , , 0… , : , ulong
( uint32_t
), ( uintptr_t
). , . , uint64_t
4. RISC-V , C, , — UB. memtest UBSan. , , UBSan trap-on-error JTAG.
, memtest - , , U-Boot.
: mkimage
U-Boot Linux :
mkimage -A riscv -O linux -T kernel -C none \ -a 0x80000000 -e 0x80000000 \ -n memtest -d memtest.bin memtest.uboot
SD-
run mmcsetup; run fdtsetup; fdt set /chosen bootargs "console=ttyS0"; fatload mmc 0:1 82000000 memtest.uboot; bootm fdt; bootm 82000000 - ${fdtaddr}
( , run
— ).
: FDT: 0xbffb7c80
. , : ffffffff
, . , ( ), : HiFive_U-Boot :
theKernel(machid, (unsigned long)images->ft_addr);
,
void (*theKernel)(int arch, uint params);
, , , , 32 , head.S
:
li t0, 0xffffffffL and a1, a1, t0
, , - , , , :
- x86. — review
- SMP RISC-V
arch/
-test.c
RISC-V ( -O0
!)