Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
am335xbootrom — Reverse engineering the TI AM3358 boot ROM | Kitploit
أدوات/GitHubGitHub/sjgallagher2/am335xbootrom
Embedded Systems SecurityReverse EngineeringDebuggersHardware HackingBinary AnalysisLearning & EducationFirmware Analysis
GitHubsjgallagher2/am335xbootrom

am335xbootrom

Reverse engineering the TI AM3358 boot ROM

عرض المستودع
615منذ سنة واحدةتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

هندسة عكسية لـ AM335x Boot ROM

ربما مرّت ثمانية عشر شهرًا منذ أن حصلت لأول مرة على بضع لوحات Beaglebone Black، كانت قد أُنقذت من حاوية النفايات. لسوء الحظ، لم تعمل اللوحات مباشرة. الآن، كانت هذه أول تجربة لي مع واحدة من هذه اللوحات، أو أي حاسوب أحادي اللوحة في هذا الشأن، لذلك لم أكن متأكدًا مما إذا كانت المشكلة شيئًا أفعله أنا، أم شيئًا خاطئًا في اللوحات نفسها (ربما لهذا كانت في طريقها إلى حاوية النفايات في المقام الأول). استغرقني وقت طويل جدًا، وجهد كبير، لأجعل هذه اللوحات تبدأ الإقلاع فعلًا، لكن بحق الجحيم، فعلتها. وهذا ما تعلمته على طول الطريق.

ملاحظة: كيفية استخدام ملف Ghidra XML

لقد أدرجت بعض الأدوات المساعدة في هذا المستودع، بالإضافة إلى ملف xml مُصدَّر من Ghidra يحتوي على جميع الرموز التي حصلت عليها حتى الآن من الهندسة العكسية. استخدمت هذا المنشور للتصدير دون البرنامج الثابت الفعلي، لتجنب أي مشاكل حقوق نشر، فقط في حالة. إذا كنت تريد تصحيح أخطاء boot ROM بنفسك، فسيكون لديك بالفعل JTAG موصولًا، لذا يمكنك تفريغ boot ROM (من 0x20000 إلى 0x2BFFF) بنفسك.

لتحميل الرموز:

  1. أنشئ مشروع Ghidra جديدًا. استورد الملف الثنائي (وليس XML) إلى Ghidra: استخدم ARMv7 Little Endian، وتأكد من ضبط عنوان القاعدة على 0x20000 تحت Options، ويمكنك ضبط اسم الكتلة إلى bootrom.
  2. افتح هذا الملف الثنائي في CodeBrowser. لا تقم بالتحليل.
  3. انتقل إلى File > Add program، وحدد ملف XML. الإعدادات الافتراضية يجب أن تكون جيدة. يمكنك الآن المرور عبر معالج الإعادة الضبط، أو القفز إلى main()، أو معالج إقلاع بطاقة MMC/SD.

المشكلة

في البداية، كنت أعرف أن هذه إصدارات مخصصة من beaglebone black القياسي، لذلك قررت مبكرًا أنه قد يكون هناك شيء مفقود على اللوحة نفسها، مثل معرّف اللوحة. ما رأيته عند الإقلاع من بطاقة SD قياسية منسّقة باستخدام balenaEtcher، كان لا شيء. توقعت أن تبدأ مصابيح LED على اللوحة في الوميض، وتوقعت أن توصيل كابل UART إلى USB سيتيح لي رؤية عملية U-Boot. ومع ذلك، كان UART صامتًا. إذا أزلت بطاقة SD، فإنه يُخرج الحرف C مرارًا وتكرارًا، وهو سلوك متوقع لإقلاع UART/تسلسلي. كان بالتأكيد يحاول الإقلاع، وكانت بطاقة SD تغيّر هذا السلوك، لكن لم يكن لدي أي رؤية إضافية. معظم استكشاف الأخطاء على الويب يعتمد على مخرجات U-Boot كنقطة بداية لتشخيص المشكلات. أظن أنني لن أحظى بذلك الرفاهية.

رأيت في هذه المرحلة أنه سيكون من المفيد توصيل مسبار تصحيح أخطاء. لسوء الحظ، لم يكن لدي موصل رأس مطابق للبصمة الموجودة، لذا صنعت واحدًا بنفسي.

لوحة beaglebone تحتوي على موصل رأس بتسمية P2 والذي يوصل توصيلات JTAG. وصلت بعض الأسلاك من هذا إلى موصل رأس أنثوي حتى أتمكن من التواصل معه عبر J-Link الخاص بي.

عند التشغيل في Ozone (مصحح Segger) قمت بتهيئة J-Link وبدأت بمجرد محاولة العثور على نقطة الدخول. ظننت أن reset-halt سيضعني حيث أحتاج، وهكذا توصلت إلى الافتراض (الخاطئ) بأن نقطة الدخول هي 0x2148a، على الرغم من أنني لاحظت بالتأكيد أن هذا لم يكن ثابتًا. لاحقًا، أدركت أن لوحات AM335x لا تتوافق جيدًا مع reset-halt في J-Link، لذلك كان هناك في الواقع تأخير ربما بضع مئات من دورات الساعة، مما أوصلني إلى مكان ما داخل معالج إقلاع، بشكل غير حتمي. (تجاوزت هذا في النهاية بكتابة ملف GEL لـ TI's Code Composer Studio الذي يدعم تصحيح أخطاء J-Link - عند إعادة الضبط، يتم تعيين سجل PC إلى معالج إعادة الضبط، وتُمسح السجلات، ويُجبر وضع التعليمات على ARM.)

من موضوع في منتديات TI (AM335x: TI employees, where can I get the ROM Bootloader source code/symbols?) حصلت على رمزي تصحيح أخطاء: SPI Initialize عند 0x231e0، وSPI ReadSectors عند 0x23230، و0x24bfa هو روتين يقوم بقراءة UART. هذا قدر لا بأس به من المساعدة على ما أظن. لاحظت أن الإقلاع فشل بالوصول إلى حلقة لا نهائية عند 0x402f0440، وهي حلقة ميتة. همم، بعيدة جدًا عن بقية boot ROM، يجب أن تكون في RAM أو شيء من هذا القبيل. ربما حان الوقت للانتقال إلى دليل المرجع التقني (TRM)!

يحتوي الفصل 26 من TRM على كمية هائلة من المعلومات حول الإقلاع. نحصل على العرض التالي لـ boot ROM:

الوصف:

تُظهر البنية المعمارية لـ Public ROM Code في الشكل 26-1. وهي مقسمة إلى ثلاث طبقات رئيسية بنهج من الأعلى إلى الأسفل: المستوى العالي، وبرامج التشغيل، وطبقة تجريد العتاد (HAL). تتواصل إحدى الطبقات مع طبقة مستوى أدنى من خلال واجهة موحدة. الطبقة عالية المستوى مسؤولة عن المهام الرئيسية لـ Public ROM Code: تهيئة الـ watchdog والساعات وروتين الإقلاع الرئيسي. تنفذ طبقة برامج التشغيل البروتوكولات المنطقية والاتصالية لأي جهاز إقلاع وفقًا لمواصفات الواجهة. أخيرًا، تنفذ HAL الكود منخفض المستوى للتفاعل مع بنى IP الخاصة ببنية العتاد. الأجهزة الطرفية للإقلاع النهائية موصولة بمنصات إدخال/إخراج الجهاز.

يوضح الشكل 26-2 التدفق عالي المستوى لإجراء إقلاع Public ROM Code. على هذا الجهاز، يبدأ Public ROM Code عند اكتمال بدء التشغيل الآمن (المنفذ بواسطة Secure ROM Code). ثم يقوم ROM Code بتهيئة النظام الأساسي وإعداده كجزء من إجراء البدء العام. يتم إنشاء قائمة أجهزة الإقلاع بناءً على دبابيس SYSBOOT. يمكن أن يكون جهاز الإقلاع جهاز إقلاع ذاكرة (ذاكرة فلاش ملحومة أو جهاز إقلاع مؤقت مثل بطاقة الذاكرة) أو واجهة طرفية متصلة بمضيف. تمر الحلقة الرئيسية لإجراء الإقلاع عبر قائمة أجهزة الإقلاع وتحاول البحث عن صورة من جهاز الإقلاع المحدد حاليًا. يتم الخروج من هذه الحلقة إذا تم العثور على صورة إقلاع صالحة وتنفيذها بنجاح أو عند انتهاء مهلة الـ watchdog. يتم تنفيذ إجراء مصادقة الصورة قبل تنفيذ الصورة على جهاز HS. يؤدي الفشل في إجراء المصادقة إلى التفرع إلى "حلقة ميتة" في Secure ROM (بانتظار إعادة ضبط الـ watchdog).

خريطة الذاكرة! متجهات الاستثناءات! مخططات التدفق! الكثير من المعلومات في هذا القسم. أصبحت مهمتي أسهل كثيرًا.

في هذه المرحلة استخدمت مسبار JTAG لتنزيل البرنامج الثابت إلى ملفين مختلفين، وبدأت في تحميل الأشياء إلى Ghidra. لم تكن هناك أي ملفات SVD أو تعيينات سجلات أخرى متاحة بتنسيق مناسب، وهو أمر مؤسف حقًا، لأن ذلك يعني أنني بحاجة إلى تعريف مناطق الذاكرة والسجلات وكل شيء يدويًا. كانت هذه عملية مضنية، لكن بعد فترة أصبح لدي سكربت بايثون يمكنني استخدامه لتحميل الرموز إلى Ghidra لـ AM3358. أمر أقل للقلق بشأنه!

الهندسة العكسية

يبدو أن الملفات التي لدي يمكن تعيينها على النحو التالي:

من المثير للاهتمام أن الحلقة اللانهائية عند 0x402f_0440 تقع في أعلى "الصورة التي تم تنزيلها" في SRAM الداخلية، بينما تُخزَّن متجهات الاستثناءات في مكان آخر. ربما سيكون هذا تلميحًا مهمًا لاحقًا...

عند إعادة الضبط، يتولى boot ROM الخاص التعامل مع أمور الأمان، ويتفرع إلى 0x2 0000 الذي يحتوي على متجهات إعادة الضبط. أول تعليمة هي فرع إلى 0x2 08d0 والتي يجب أن تكون نقطة الدخول. إنها ليست تعليمة BX لذا، على الأرجح، ما زلنا في وضع ARM عند تلك النقطة.

معالج إعادة الضبط

هذا هو أول كود يتم تشغيله، مما يعني أنه ليس تمامًا "دالة" بمعاملات، بل هو أشبه بسكربت تشغيل مُولَّد من المترجم. أول كتلة أساسية:```arm ldr r4,[->Peripherals::CM_PER] mov r0,#0x2c ldr r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL mov r6,#0x2 str r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL mov r0,#0x2c poll: ldr r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL cmp r6,#0x2 bne poll

root@kitploit:~
تضبط هذه الكتلة ساعة OCMC RAM لتصبح ممكّنة:
1. اضبط `CM_PER_OCMCRAM_CLKCTRL=0x2`
2. تحقق مما إذا تم ضبط السجل؛ إذا لم يتم، واصل الاستقصاء
يستخدم سجل `CM_PER_OCMCRAM_CLKCTRL` البتّين 0 و1 لحقل `MODULEMODE`، وضبط هذا `=0x2` يمكّن الساعة الموجهة إلى OCMC RAM. 

الكتلة الأساسية التالية:```arm
	    ldr        r0,[PTR_control_status] 
	    ldr        r0,[r0,#0x0]=>control_status 
	    and        r0,r0,#0x700
	    mov        r0,r0, lsr #0x8
	    cmp        r0,#0x3
	    bne        skip
		ldr        r0,[PTR_control_status] 
	    ldr        r0,[r0,#0x0]=>control_status
	    cpy        r6,r0
	    and        r0,r0,#0x1f
	    cmp        r0,#0x1f
	    bleq       GPMIC_init 
skip:   ...

تقوم هذه الكتلة بما يلي:

  1. تحقق مما إذا كانت (control_status & 0x700) >> 8 == 0x3، وتخطَّ إذا لم تكن كذلك
  2. تحقق مما إذا كانت control_status & 0x1f == 0x1f، وعندها استدعِ الدالة GPMC_init بعد تحميل control_status في r6

تقوم الكتلة التالية بإعداد المعالج المساعد:```arm msr cpsr_c,#0xd3 ldr r4,[->Exceptions::ROM_RESET_VECTOR] mcr p15,0x0,r4,cr12,cr0,0x0 bl LAB_00020934 bl LAB_00020938 bl LAB_0002093c bl LAB_00020940 bl LAB_00020944 bl LAB_00020948 bl LAB_0002094c bl LAB_00020950 mrc p15,0x0,r0,cr1,cr0,0x0 orr r0,r0,#0x800 mcr p15,0x0,r0,cr1,cr0,0x0 b LAB_000207f0

root@kitploit:~
العمليات في هذا المقطع:
1. انقل `11010011b` إلى حقل التحكم في CPSR (`I=1`,`F=1`,`T=0`,`MODE=10011`)
	1. `I` هو تعطيل المقاطعات، و`F` هو تعطيل المقاطعة السريعة (لذا `I=F=1` يعني أن المقاطعات معطلة)
	2. `T` هو وضع Thumb، مضبوط على `0`
	3. `MODE=10011` يضبط وضع المعالج على وضع المشرف ([مرجع](https://developer.arm.com/documentation/ddi0406/b/System-Level-Architecture/The-System-Level-Programmers--Model/ARM-processor-modes-and-core-registers/ARM-processor-modes?lang=en#CIHGHDGI))
	4. انظر [هنا](https://developer.arm.com/documentation/ddi0406/b/System-Level-Architecture/The-System-Level-Programmers--Model/ARM-processor-modes-and-core-registers/Program-Status-Registers--PSRs-) لمزيد من المعلومات
2. حمّل العنوان إلى متجه إعادة تعيين ROM
3. قم بالوصول إلى [المعالج المساعد 15](https://developer.arm.com/documentation/den0013/d/ARM-Processor-Modes-and-Registers/Registers/Coprocessor-15) (المعالج المساعد للتحكم بالنظام) سجل ملحقات الأمان `c12` وحمّل متجه إعادة تعيين ROM في `VBAR` (سجل العنوان الأساسي للمتجه)
![](https://assets.kitploit.com/production/public/readmes/48883/6c4ef51fde89883fcdb3a9497004830d928d6f6c716668acd86dce3b9a68c93b.png)
4. هل تبدو وكأنها قفزات `nop`؟ لماذا `bl` بدلاً من `b`؟
5. فعّل التنبؤ بالتفرع (عيّن البت 11 في سجل التحكم بالنظام `SCTLR`)
![](https://assets.kitploit.com/production/public/readmes/48883/7a99ff8bd5285136c05b9d91bdf5057a6512c614cacc2726ce740254e4c240fa.png)
*سجلات CP15 `c1` (سجلات التحكم بالنظام) في تطبيق VMSA*

وصف سجل `SCTLR`:
> يوفر سجل SCTLR التحكم على المستوى الأعلى للنظام، بما في ذلك نظام الذاكرة الخاص به.
> هذا السجل جزء من المجموعة الوظيفية لسجلات التحكم في الذاكرة الافتراضية.

انظر صفحة B4-1687 من TRM. البت 11 هو بت *تمكين التنبؤ بالتفرع*، وضبطه على التمكين يعني أن [التنبؤ بالتفرع](https://developer.arm.com/documentation/ddi0406/b/System-Level-Architecture/Common-Memory-System-Architecture-Features/Caches/Branch-predictors) مفعّل.

6. استدعِ دالة (عبر فرع إلى تعليمة استدعاء)

### الدالة عند `0x20894` (`__main`)

يتم التفرع إلى هذه الدالة من روتين مبكر آخر. أعتقد أنها تهيئ المكدس، وربما المؤقتات أو المؤقت الحارس، قبل استدعاء `FUN_0002889c` (الذي تبيّن لاحقاً أنه `main()`!).```arm
		 ldr    sp,[->RESERVED_EXCEPTION_BRANCH]   ; 0x4030ce00
		 blx    load_stack_1
		 ldr    r12,[DWORD_1]
		 add    r12,r12,pc
		 tst    r12,#0x1
		 adrne  lr,0x208bd
		 cpyeq  lr,pc
		 bx     r12 ;=>init_timers_maybe
		 adr    r12,0x208bd
		 bx     r12 ;=>LAB_000208bc
000208bc bl     FUN_0002889c
000208c0 ddw    0x109
000208c4 addr   RESERVED_EXCEPTION_BRANCH
...
RESERVED_EXCEPTION_BRANCH:
         ldr    pc=>LAB_00020090,[PTR_LAB_4030ce20]
         ; 20090 is a dead loop

العمليات هنا هي:

  1. تحميل بداية جدول استثناءات RAM إلى مؤشر المكدس (stack pointer)
  2. التفرع إلى دالة تدفع r0,r1,r2,r3,r4,lr إلى المكدس (المكدس العام في خريطة الذاكرة)
    1. تتفرع تلك الدالة load_stack_1 إلى دالة فارغة bx lr، ثم تسحب r0,r1,r2,r3,r4,pc من المكدس (تضع هذه البيانات مرة أخرى في السجلات وتضع ما يحمله lr في pc للعودة)
  3. التحقق مما إذا كان pc + 0x109 فرديًا؛ إذا كان زوجيًا فحمّل 0x208bd في lr، وإلا انسخ pc إلى lr
  4. استدعاء الدالة main
  5. استدعاء FUN_0002889c

يجب أن تكون هذه هي الدالة __main() المشار إليها في مخطط تدفق الإقلاع:

هذا يجعل الدالة التالية هي الدالة main.

كما هو موضح في أعلى الشكل 26-8، تقفز وحدة المعالجة المركزية (CPU) إلى متجه إعادة تعيين كود ROM العام بمجرد اكتمال تهيئة الإقلاع الآمن. وبمجرد الدخول في الوضع العام، عند بدء تشغيل النظام، تنفّذ وحدة المعالجة المركزية التهيئة الخاصة بالجانب العام وإعداد المكدس (تهيئة C- المولّدة تلقائيًا من المترجم أو "التحميل المبعثر"). ثم تقوم بضبط مؤقت المراقبة 1 (مضبوطًا على ثلاث دقائق)، وتنفيذ إعداد ساعات النظام. وأخيرًا تقفز إلى روتين الإقلاع.

الدالة الرئيسية (0x209b0)

عند استدعاء main، يشير سجل SP إلى 0x4030ce00. هنا يبدأ المكدس، وينمو نزولًا نحو 0x4030 b800؛ وبما أننا نشير إلى العنوان 0x4030 cdf0 بعد دفع 4 سجلات (فرق قدره 16 بايت أو 4 كلمات)، فإننا نستخدم مكدسًا تنازليًا كاملًا، كما في AAPCS. أي أن SP يشير إلى الكلمة الأحدث على المكدس وينمو نزولًا.

فيما يلي دالة main() المفككة:```c int main() { uint local_10; uint local_c;

local_c = 0; local_10 = 0; check_stack_prm(&local_10); update_coldreset_tracing_vector(local_10); update_current_tracing_vector(1); main_clock_init(6,0); watchdog_softreset(); watchdog_write_disable_seq_data2(); set_watchdog(300000); if ((local_10 & 1) != 0) { update_current_tracing_vector(2); local_c = local_c & 0xffff | 1; } timer_func_1(); clock_init_func_4(&local_c); run_booting_loop(&local_c,local_10 & 0xff); return 0; }

root@kitploit:~
Most interesting for my purposes is the `run_booting_loop` function at `0x20a10`. 

### ملاحظات حول X-Loader

بعد المرور عبر مرحلة الإقلاع والوصول إلى هذا الجزء الذي يتضمن سلاسل نصية مثل "ISSW" و"CHSETTINGS" و"X-LOADER"، بدأت أبحث عن أماكن أخرى قد تظهر فيها هذه السلاسل في سياقات مرتبطة بـ U-Boot. عثرت على [هذا الموضوع](https://forum.xda-developers.com/t/discussion-on-the-boot-loader-cracked.1378886/) لأشخاص يقومون بهندسة عكسية أو كسر البرنامج الثابت لجهاز Nook، ويحتوي [مصدر x-loader](https://github.com/joelagnel/x-loader/blob/f3c74bc9b01dac58e553393d6ec1041353f2f1f7/scripts/signGP.c) على مراجع لأشياء مثل `CHSETTINGS`. ومن خلال البحث، يبدو أن "ISSW" [تشير](https://github.com/u-boot/u-boot/blob/master/doc/README.ti-secure) إلى الإقلاع من أجهزة غير ذاكرة. 

لنتذكر من توثيق التهيئة الكود عالي المستوى:
![](https://assets.kitploit.com/production/public/readmes/48883/3b7ecd05acbf1f0aa5497009b4df65d17a7b0e133b9b711bebaf9f5bebf546cf.png)

مما يثير الاهتمام:
- RNDIS
- FAR
- XMODEM
- BOOTP
- TFTP
- DFT

ربما حان الوقت لتجربة التصحيح المباشر مرة أخرى. إن محاولة عكس كل تلك البنى ستكون مؤلمة على الأرجح بالنظر إلى الكمية الكبيرة من البيانات التي لا أستطيع فهمها...

مرحى! التصحيح المباشر يعمل عند ضبط PC وSP يدويًا باستخدام المتجهات التي وجدتها:
![](https://assets.kitploit.com/production/public/readmes/48883/3886242947cd1cef1093f3cb02dbbcc02faf27f1298708dd849e69eae7b75f97.png)

من المصدر ومتجهات التتبع، تمكنت من رسم خريطة لخيارات الإقلاع المختلفة وأرقام الأجهزة المخصصة لها. أصبح هذا مفيدًا جدًا لاحقًا، إذ كان عليّ التمييز بين MMC0 (8) وMMCSD1 (9) عند تعيين نقاط التوقف في معالج إقلاع SD/MMC. 

| النوع | الجهاز | معرّف الجهاز |
| ---------- | ----------------- | ----------- |
| ذاكرة | XIP (MUX2) | 1 |
| ذاكرة | XIP w/WAIT (MUX2) | 2 |
| ذاكرة | XIP (MUX1) | 3 |
| ذاكرة | XIP w/WAIT (MUX1) | 4 |
| ذاكرة | NAND | 5 |
| ذاكرة | MMCSD1 | 7, 9 (eMMC) |
| ذاكرة | NAND_I2C | 10 |
| ذاكرة | MMC0 | 8, 12 (SD) |
| طرفي | UART0 | 16 |
| طرفي | USB | 20 |
| طرفي | GPGMAC0 | 22 |

للحصول على معلومات حول كيفية إقلاع المعالج، راجع TRM و[هذه الإجابة على Stack Exchange](https://stackoverflow.com/a/31252989/8565545). للتلخيص: 
1. قامت ذاكرة الإقلاع ROM بتحديد ملف MLO (Mmc LOader) على بطاقة SD ونسخته إلى SRAM.
2. هذا هو محمّل البرامج الثانوي، وهو محمّل إقلاع أصغر يقوم بتهيئة ذاكرة RAM الكاملة ونسخ ملف U-Boot الكامل إليها للتنفيذ.
3. بعد تشغيل ملف U-Boot الثنائي، نقوم (أو بالأحرى U-Boot) أخيرًا بإقلاع النواة.

### `run_booting_loop()`

هذه هي حلقة الإقلاع الرئيسية. تعمل إلى ما لا نهاية، أو حتى يتفرع التنفيذ إلى محمّل إقلاع آخر سيتم تحميله في RAM. 

بداية الإجراء، دون تحديثات متجهات التتبع:
- ابحث عن نوع الجهاز
	- إذا كان نوع الجهاز هو 5 (جهاز آمن) فقم ببعض التهيئة الأخرى
- شغّل `build_boot_list(int,buffer[],data[],int)`  
	- تتم تهيئة `buffer[]` إلى `0xff` وتحتوي `data[]` على نوع الجهاز (على الأرجح)```c
void run_booting_loop(uint32_t *r0_config,undefined4 param_2,undefined4 param_3,
                     undefined4 default_list)

{
  int iVar1;
  uint j;
  uint i;
  int device_type;
  byte alt_list [12];
  undefined4 boot_status;
  byte boot_list [8];
  uint8_t local_buffer [8];
  
  update_current_tracing_vector(3);
                    /* Device type is 3 */
  lookup_device_type(&device_type);
  if ((device_type == AM335X_HIGH_SECURITY) && (iVar1 = return_zero_4(), iVar1 != 0)) {
    init_something_1_small(&STATIC_DATA_1);
  }
                    /* param1 = 1
                       param2 = 4030 ebc4
                       param3 = 4030 ebb4 */
  build_boot_list(*(ushort *)r0_config,boot_list,alt_list,default_list);
  do {
    i = 0;
    local_buffer[0] = 0xff;
    local_buffer[1] = 0xff;
    local_buffer[2] = 0xff;
    local_buffer[3] = 0xff;
    do {
      if (boot_list[i] - 1 < 12) {
        update_current_tracing_vector(4);
                    /* No return unless there is an error */
        boot_device_1(r0_config,boot_list[i],local_buffer);
      }
      else if (boot_list[i] - 65 < 8) {
        update_current_tracing_vector(5);
        watchdog_write_disable_seq_data2();
        boot_status = 0xffffffff;
        boot_device_2((uint32_t)r0_config,boot_list[i],&boot_status,local_buffer);
        watchdog_write_enable_seq_data2();
        if (boot_status != 0xffffffff) {
          local_buffer[0] = (undefined)boot_status;
          local_buffer[1] = boot_status._1_1_;
          local_buffer[2] = boot_status._2_1_;
          local_buffer[3] = boot_status._3_1_;
          if ((boot_status & 0xffff00ff) == 0xf0030006) {
            update_current_tracing_vector(9);
            boot_list[i + 1] = (byte)(boot_status >> 8);
          }
          else if (boot_status != 0xf0030002) {
            update_current_tracing_vector(8);
            j = 0;
            do {
              if (63 < boot_list[j]) {
                boot_list[j] = 0;
              }
              j = j + 1 & 0xff;
            } while (j < 8);
          }
        }
      }
      i = i + 1 & 0xff;
    } while (i < 8);
    update_current_tracing_vector(6);
  } while( true );
}

الإقلاع إلى SRAM

اكتشفت وجود دالة عند 0x23d7a أسميتها boot_into_SRAM()، وهي آخر دالة تُستدعى قبل الانتقال إلى SRAM، ومن ثم الدخول إلى معالج الاستثناءات. في السابق، كنت أحتفظ بحالة مختلفة من RAM محفوظة، لكن أثناء التصحيح المباشر مرة أخرى (الآن بعد مرور أكثر من عام، يوليو 2024)، أدركت ما الذي يجب أن يحدث. كانت اللوحة تقرأ البيانات من بطاقة SD بنجاح، وتشغّل كودًا حمّلته من البطاقة! للتحقق من ذلك، كان عليّ العثور على بايتات في SRAM تطابق ما هو موجود على بطاقة SD. اتضح أنه يوجد داخل am335x-evm-linux-sdk-bin-.../board-support/prebuilt-images/ ملف ثنائي باسم u-boot-spl.bin-am335x-evm، وأن الكود الموجود في هذا الملف الثنائي يطابق ما يظهر في SRAM. لقد وصلنا إلى uboot SPL!

طرفية UART

لقد أقلعنا بنجاح إلى SRAM، والآن أتساءل عن طرفية UART، التي يجب أن تعرض معلومات عن uboot. التوصيل المادي موضح أدناه.

عند التوصيل بالجهاز باستخدام CuteCom، 115200 @ 8-N-1، وبدون إدخال بطاقة SD، فإنه يخرج الحرف C بشكل متكرر فقط.

لكن عند إدخال بطاقة SD، لا يُخرج UART أي شيء. لا رسائل ولا أحرف. لا بد أن الخطأ يحدث في وقت مبكر جدًا من عملية الإقلاع؟ لكن الآن بما أنني أعرف أيضًا الكود الذي ينفذه (ولدي مصدره)، يجب أن أتمكن من بناء رموز تصحيح له وبدء جلسة تصحيح مناسبة. قد لا يكون هذا أمرًا بسيطًا، فعليّ التأكد من أنني أُصرّف الكود بنفس الطريقة، وقد يستغرق الأمر بعض الوقت لمعرفة ما حمّله SDK بالضبط على بطاقة SD الخاصة بي وكيفية بنائه.

بما أن U-Boot كان يجب أن يُخرج نصًا إلى UART وأنا لا أرى شيئًا، أعتقد أننا نلتقط استثناءً في مكان ما داخل SPL.

عند هذه النقطة، قضيت ساعاتٍ طويلة جدًا في إجراء الهندسة العكسية وتنظيف الكود المصدري المُفكك لـ boot ROM في Ghidra، والاطلاع على البُنى (structs) وكيفية استخدام كل عضو بيانات عبر الدوال، مع تداخلات كانت تسبب لي كل أنواع الفوضى. وبينما كان ذلك مؤجلًا، رأيت أنه حان الوقت أيضًا لبدء تصحيح وتجميع الكود الخاص بي. نحن في RAM في النهاية، فلماذا لا نحمل رموز SPL ونرى ما الذي يحدث؟

تطوير SDK

التصحيح

يمكنك التصحيح باستخدام Ozone، مصحح أخطاء Segger لـ J-Link. ويمكنك أيضًا استخدام Code Composer Studio (CCS) الخاص بـ TI، أو ربما نسخته "الخفيفة" بنمط VSCode وهي CCS Theia. تمكنت من بناء كل شيء في SDK باتباع هذا الفيديو: سلسلة نقل لوحات Sitara Linux: الوحدة 6. هناك ثلاثة مكونات يجب بناؤها:

  • إعداد المعالج
  • ملف U-Boot الثنائي
  • مُحمّل البرامج الثانوي لـ U-Boot (SPL)

اتبعت فيديو الوحدة 7 من السلسلة أعلاه وتمكنت من جعل الأمور تعمل، مع ملاحظتين:

  1. لم تعد s_init() موجودة
  2. يجب ضبط نقاط التوقف العتادية مع J-Link من خلال لوحة تحكم J-Link (انظر أيقونة العلبة). لست متأكدًا من كيفية تحميل الكود بهذه الطريقة. ربما عبر Ozone.

أصبح لدينا رموز؟ رائع. الآن يمكنني رؤية ما يحدث أثناء التنفيذ، بدءًا من معالج إعادة الضبط reset()، ويمكنني رؤية أين ينتهي بنا المطاف عند الاستثناء. لتتبعه، وضعت نقطة توقف عند معالج الاستثناءات عند 0x402f 0440، وفحصت سجل الارتباط، الذي ما زال يخزن عنوان أحدث دالة. اتضح أنه العنوان 0x402f 76ce، على الرغم من أنه لا يبدو ثابتًا. ما الذي يسبب الخطأ؟

ملاحظة: للتصحيح، اتبع فيديو الوحدة 7، وشغّل حتى 0x402f 0400، ثم قم بجزء Load Memory(). يجب القيام بذلك عند كل إعادة تشغيل.

ندخل إلى الدالة device_probe() (0x402f 74c4)، ثم دالتين أخريين؟ ثم من do_setup_dpll() التي تتفرع عند 0x402f 07fc، لا نخرج، لذا دعنا نتعمق أكثر فيها. وعند التنفيذ خطوة بخطوة، نعود إلى _main() في crt0.S، الموجودة عند 0x402f 14e0. يبدو أننا ربما نخرج من board_init_f()، ونتقدم إلى spl_relocate_stack_gd(). هذا الاستدعاء لا يخرج. نصل إلى dm_fixup_for_gd_move(). تحتوي هذه الدالة على تعليمة تفشل عند 0x402f 76c2. أعتقد أن هذا هو السبب: إنها تحاول الوصول إلى 0x81ff ff20. يبدو أن ذلك غير ناجح. لدي حدس أن هناك مشكلة في إعداد SDRAM. تتبعت جميع المواضيع في منتديات TI المتعلقة بمشكلات مشابهة، ووجدت نحو ستة منها تحتوي على تلميحات ساعدتني. وخلصت إلى أنها ربما كانت مرتبطة إما بـ (أ) ضبط EMIF، أو (ب) التسوية البرمجية (software leveling).

إعداد DDR3 RAM

لوحتي هي المعروضة أدناه.

الذاكرة من Micron، بينما المخطط الكهربائي للوحة BeagleBone Black التي أملكها (المراجعة C3) يستخدم ذاكرة DDR3 من Kingston، وتحديدًا D2516EC4BXGGB. ذاكرة DDR3 هي U12، ويمكننا استخدام صفحة فك ترميز علامات Micron للعثور على القطعة:

  • MT41K256M16TW-107 XIT:P
    • 256 ميجا × 16
    • 96 كرة 8مم × 14مم FBGA، المراجعة P
    • $t_{CK}$=1.07ns، CL = 13

دعونا نتأكد من أن القطعة تعمل، بدأت ببساطة بفحص وجود الطاقة. تحدد ورقة البيانات أنه يجب أن يكون الجهد 1.5V +/- 0.075V. قمت بقياس 1.506V عبر R6 على الجانب السفلي من اللوحة. لدينا نقطتا اختبار، TP1 وTP2.

في حال كان ذلك مفيدًا يومًا ما، إليك بعض نقاط الاختبار.

دارة الذاكرة موصوفة بالتفصيل في صفحة تصميم العتاد.

  • جهاز الذاكرة

دعونا نفحص خط تمكين الساعة. يمكننا فحص جانبي R96، يجب أن يكون أحد الجانبين مؤرضًا، والجانب الآخر مرفوعًا إلى المستوى العالي. تم التأكيد، 1.5V على CKE.

الخطوة التالية هي فحص إشارة الساعة. فعلت ما بوسعي هنا، باستخدام tinySA مع توصيل الهوائي وتوجيهه تقريبًا في اتجاه شريحة RAM. من خلال هذا النوع من "الاستشعار"، أشعر بثقة تامة أن الساعة موجودة، على الأقل بما يكفي في الوقت الحالي.

بالانتقال إلى الأمام، حان الوقت للنظر في واجهة الذاكرة الخارجية. من الأمور التي تظهر كثيرًا مفهوم ملف GEL. وهي لغة مفسّرة طورتها Texas Instruments لـ Code Composer Studio، ويشير الاختصار إلى General Extension Language.

  • إنشاء ملفات GEL لتهيئة الأجهزة

يتم تضمين ملف GEL في أداة إعداد ذاكرة DDR.

حسنًا! اتبعت إجراء الضبط (بقدر ما استطعت) وتمكنت من العثور على القيم المثلى لملف GEL.```


root@kitploit:~
The Slave Ratio Search Program Values are... 

PARAMETER MAX | MIN | OPTIMUM | RANGE


DATA_PHY_RD_DQS_SLAVE_RATIO 0x071 | 0x005 | 0x03b | 0x06c DATA_PHY_FIFO_WE_SLAVE_RATIO 0x1b3 | 0x046 | 0x0fc | 0x16d DATA_PHY_WR_DQS_SLAVE_RATIO 0x0f7 | 0x01a | 0x088 | 0x0dd DATA_PHY_WR_DATA_SLAVE_RATIO 0x137 | 0x05a | 0x0c8 | 0x0dd


root@kitploit:~
محاولة ضبط أمور الـ RAM في متصفّح الذاكرة... رائع! إنه يعمل!

إذن يبدو أن الذاكرة تعمل فعلًا، لكن الـ SPL ما زال يفشل، لذا ربما هناك شيء ما يتعلق بالطريقة التي يحاول بها الـ SPL تهيئة الـ SDRAM؟ آه، صحيح، هناك المزيد من إجراء الضبط، بالطبع! عليك في الواقع تحديث الـ SPL...

نقترب الآن أكثر. الملف `board.c` يقوم بتهيئة الـ DDR عن طريق التحقق من نوع اللوحة التي لدينا. لكن بالنسبة لهذه اللوحة، جميع الدوال (`board_is_evm_sk()`، `board_is_icev2()`، `board_is_bone_lt()`، إلخ) تُرجع false، لذلك يقع الاختيار افتراضيًا على `config_ddr(266, ...)` حيث 266 هو تردد الساعة بالميغاهرتز، و*يجب* أن يكون 400 ميغاهرتز. هذا بالتأكيد سيكون مشكلة.

`board_is_bone_lt` يجب تجاوزها بحيث تُرجع true دائمًا. فعلت هذا، وتقدّمت قليلًا، لكن هناك شيء يقلقني. تحميل الملف الجديد MLO على بطاقة الـ SD لا يعمل، رغم أن تحميل البرنامج مباشرة يعمل بشكل جيد. ما الذي يحدث هنا؟ أستطيع أن ألاحظ أن الكود الذي يتم تحميله إلى الـ SRAM ليس نفس الكود الذي قمت بترجمته. في الواقع، حتى بعد تهيئة بطاقة الـ SD، يبدو أن SPL افتراضي يتم تحميله إلى الـ SRAM على أي حال! تحققت من أن عملية الإقلاع لا تحاول الاستمرار عند استخدام بطاقة SD أخرى. لذلك، فإن مُحمل الإقلاع يبحث بالتأكيد عن قسم الإقلاع على بطاقة الـ SD، ثم ينقل التنفيذ إلى الـ SRAM، لكنه لم ينسخ البيانات بعد؟ آه. من أين يأتي؟؟

هذه المشكلة سبّبت لي قدرًا لا بأس به من المتاعب. حذفت جميع الأقسام، وصفّرت الـ MBR، وصفّرت قسم الإقلاع، وجرّبت بطاقات SD مختلفة، ومع ذلك كانت بطاقتي وحدها ما زالت قادرة على الإقلاع، لذا لا بد أن *بعض* البيانات القابلة للإقلاع ما زالت عليها، مخزّنة في مكان ما. أخيرًا، تمكنت من وضع حد لهذا الجنون بصفّر البطاقة *بالكامل*.

تعلمت أيضًا في هذه المرحلة عن **متجهات التتبع** التي يمكنك الوصول إليها عند استكشاف أخطاء الـ boot ROM وإصلاحها. سيكون هذا مفيدًا للغاية أيضًا للهندسة العكسية، لأنني عرفت من أين تأتي جميع استدعاءات التتبع، وبالتالي يمكنني تعيين أسماء الدوال إلخ بناءً على تلك الاستدعاءات. أنشأت جدول بيانات لتفسير هذه المتجهات، واستخدمته لفهم كيفية تغيّر إجراء الإقلاع بسرعة عندما غيّرت معاملات البطاقة. وبالفعل، كان يدّعي العثور على CHSETTINGS مرارًا وتكرارًا بينما كنت أحاول تهيئة وإعادة تهيئة بطاقة الـ SD، قبل أن أقوم بيأس بصفّر البطاقة بأكملها.

لمحاولة قراءة البطاقة بالطريقة التي يعالجها بها معالج TI، يمكنك استخدام `dd`. استخدم حجم كتلة = 512، وحدد القطاع الأول (جرّب استخدام GParted لمعرفة أي قطاع) بتخطي أول `n`. مثال: القطاع الأول هو 2048، الجهاز هو `sda`، سنقرأ القطاع الأول فقط:```
sudo dd if=/dev/sda1 of=/home/sam/sector2048 bs=512 skip=2048 count=1

لقد استخدمت هذا لتنزيل صور من MBR وبداية قسم الإقلاع مباشرة من بطاقة SD، وكلاهما سيكون مفيدًا لاحقًا.

التعمق في بطاقة SD

جهودي في الهندسة العكسية في Ghidra قادتني إلى دوال معالج إقلاع بطاقة SD، وأصبحت أستطيع رؤية دوال ترسل أوامر SD إلى البطاقة، وأستطيع التنقل خطوة بخطوة ورؤية ما كانت البطاقة تستجيب به. ظننت أنني أنظر في المكان الصحيح، وأن البطاقة كانت تعيد كل الأصفار. لاحقًا علمت أنني ربما كنت أتنقل عبر معالج eMMC (نفس المعالج لكن بمعرّف جهاز مختلف)، أو أن شيئًا آخر كان خاطئًا، لأنه لم يكن هناك أي خطأ في بطاقة SD. ومع ذلك، رأيت أنه حان الوقت لتعلم كيفية عمل هذه البطاقات.

كنت فضوليًا لمعرفة سبب استمرار البطاقة في إرجاع كل الأصفار أثناء كل طلب كتلة. من الواضح أن وظائف البطاقة والبرنامج يمكنها القراءة منها لأنها فعلت ذلك في الماضي. ولكن مع ذلك، كان الوقت قد حان لتوصيل الأسلاك وإلقاء نظرة باستخدام محلل المنطق. القليل من اللحام الدقيق، وتثبيت أسلاك 30awg باستخدام إيبوكسي معالج بالأشعة فوق البنفسجية، وتوصيلها بجهاز Saleae الخاص بي، ولدينا شيء يعمل.

استخدمت هذا المحلل لتحليل البيانات. أولاً جربت بدون إدخال بطاقة.

بالنسبة للأوامر القليلة الأولى، يكون معدل الساعة 120 كيلو هرتز. من الواضح أن البطاقة لا ترد (لأنها غير موجودة).``` CMD0, arg=\0 GO_IDLE_STATE CMD8, arg=\x01\xAA SEND_EXT_CSD CMD55, arg=\0 APP_CMD CMD1, arg=\0 SEND_OP_COND ... CMD0, arg=\0 GO_IDLE_STATE CMD8, arg=\x01\xAA SEND_EXT_CSD CMD55, arg=\0 APP_CMD CMD1, arg=\0 SEND_OP_COND

root@kitploit:~
عند إدخال البطاقة فعليًا، يقفز التردد إلى حوالي 6 MHz بعد الإعداد.

![](https://assets.kitploit.com/production/public/readmes/48883/80a4aca50543bd6766805e8491ba504f07a660b61804ec7a673f738dac0b03cc.png)

بعد تجاوز بعض الأعطال الناتجة عن استخدام وضع SD (نصيحة: حتى مع بطاقة SD هذه يجب استخدام وضع MMC) وتمكنت من التحقق من أن البطاقة كانت توفر بيانات معقولة. تركت هذا الأمر بعد ذلك لأنني ضاعفت جهودي لفهم مُعالج إقلاع بطاقة SD وأدركت أن البيانات الصحيحة *كانت* تُقرأ، وأنها كانت نفس البيانات التي حصلت عليها من تنفيذ `dd` يدويًا على البطاقة! حسنًا، كانت رحلة جانبية ممتعة وساعدتني على الشعور بالثقة بأن البطاقة تعمل.

### اكتشاف المشكلة

تأتي البيانات من العنوان `0x4030c928` (متغير مكدس، مصفوفة من 512 بايت) بعد تشغيل الفرع عند `0x25c2e` للعنوان `0x0000` والجهاز `8` (انظر البيانات الثابتة عند `0x4030d00c`). عند القفز إلى الطريقة التي أسميتها `MBR_detection`، يتحقق البرنامج من البايتات السحرية `0xaa55`. أولاً يقوم بتحميل البايتين الثانيين `0xaa`، ثم الأول `0xaa`.```
r0 = data[0x1ff]
r1 = data[0x1fe]
orr r0,r1,r0,lsl #8
sub r1,r0,#0xaa00
subs r1,#0x55
bne <return FAIL>

هذا ينجح. غير أن الفحص التالي يفشل:``` r0 = data[0xc] => 0 r1 = data[0xb] => 0 orr r0,r1,r0, lsl #8 cmp r0,#0x200 bne

root@kitploit:~
الكود الزائف للمفكك:```c
if (
	 data[0x1fe] != 0x55aa || 
	 data[0xb]   != 0x200  ||
	(data[0xd] != 1 &&     // bit 0
	 data[0xd] != 2 &&     // bit 1
	 data[0xd] != 4 &&     // bit 2
	 data[0xd] != 8 &&     // bit 3
	 data[0xd] != 0x10 &&  // bit 4
	 data[0xd] != 0x20 &&  // bit 5
	 data[0xd] != 0x40 &&  // bit 6
	 data[0xd] != 0x80)    // bit 7
	) 
{
	return 1;
}

هذا يتحقق مما إذا كانت البايتة عند 0xb = 11 تساوي 0x200، ويتحقق مما إذا كانت البايتة عند 0xd تساوي قيمة ذات بِت واحد. يجب استيفاء كلا الشرطين، وإلا فإنها تُرجع فشلًا.

بعد أن تُعيد دالة الكشف القيمة 1، يحاول معالج الإقلاع بعد ذلك قراءتها كـ MBR ويقوم بتحميل إزاحة القسم الأول لمعرفة ما إذا كان ذلك قسمًا قابلًا للتمهيد. إليك الإجراء:```C // Call block read function // mmc_block_read_something(boot_device *dev,blk_read_struct blk) ret = ((code *)blockread_struct->block_read_func)(blockread_struct->device_ptr,&block_read_info); if (ret != 0) { return 1; } // Check if device doesn't use MBR ret = MBR_check_bootable_partition((partition_struct *)block_data,blockread_struct); if (ret != 0) { // It uses MBR, try each partition in the partition entries for a bootable // partition ret = MBR_check_entries(block_data,blockread_struct); if (ret != 0) { return 1; } ret = MBR_parse_entries(block_data,&blockread_struct->part_entry); if (ret != 0) { return 1; } // Get bootable partition offset block_read_info = (blk_read_struct *)(blockread_struct->part_entry).first_sect_pos; uStack_220 = 1; pbStack_21c = block_data; // Call block read function // mmc_block_read_something(boot_device *dev,blk_read_struct blk) ret = ((code *)blockread_struct->block_read_func) (blockread_struct->device_ptr,&block_read_info); if (ret != 0) { return 1; } // Try and verify bootable partition again ret = MBR_check_bootable_partition((partition_struct *)block_data,blockread_struct); if (ret != 0) { return 1; } }

root@kitploit:~
الآن أنتقل إلى النقطة التي يقرأ فيها البيانات عند `0x800` وأحصل على التفريغ الصحيح. لكن طريقة كشف MBR لا تزال تُرجع 1، حتى مع البيانات الصحيحة (وهناك عدة طبقات من الالتفاف كان عليّ تتبعها، grrr) لذلك لا بد أن المشكلة تكمن هناك.

نحن في الأمتار الأخيرة الآن. أول قراءة ذاكرة من بطاقة SD هي MBR، والتي تحتوي على ما يصل إلى أربعة إدخالات لجدول الأقسام، انظر جداول TRM 26-20، 26-21. يقول إدخال جدول الأقسام لقسم الإقلاع إن القسم يحتوي على `0x40000` قطاعًا. لكن نظام ملفات القسم (انظر جدول TRM 26-23) يقول إنه لا يوجد سوى `0x3fff8`، لسبب ما، ويكتشف boot ROM ذلك ويفشل.

كتجربة، قفزت فوق القفزة التي كانت تسبب المشاكل (وقد ينتهي الأمر بشكل سيئ... نأمل خيرًا...) وبالتأكيد استمر البرنامج، على الرغم من أنني لست متأكدًا أين وصلت، يبدو الأمر وكأنه بيانات عشوائية. لكن بغض النظر عن ذلك، الجهاز يقوم بالإقلاع فعلًا! تعيين نقطة توقف عند `0x402f0400` (بداية الصورة المحمّلة) وكل شيء يسير بشكل صحيح. ربما حان الوقت لتوصيل UART؟ UART رائع!

أما بالنسبة لمشكلة بطاقة SD؟ لقد طرحت [سؤالًا على Unix SE](https://unix.stackexchange.com/questions/781715/why-do-the-mbr-partition-entry-and-partition-filesystem-disagree-on-the-number-o/781755)، لكن لم أحصل على مساعدة كبيرة في المشكلة المطروحة (رغم أنني حصلت على بعض المعلومات الجيدة على أي حال). من هناك: لقد أصلحته أخيرًا! أثناء مراجعة أوامر `mkfs` لبناء نظام ملفات FAT16، لاحظت أن [مرجعًا آخر](https://blog.billvanleeuwen.ca/porting-u-boot-onto-the-beaglebone) يستخدم العلم `-a` الذي يعطّل المحاذاة. كان هذا هو المفتاح. بإضافة هذا العلم وإعادة البناء، تتطابق أعداد القطاعات (`0x40000`) ويُقلع النظام. أعتقد أن boot ROM لا يدعم هذا النوع من المحاذاة.

أحصل الآن على الرسائل أدناه في حلقة إقلاع، مع توصيل البطاقة. يا للروعة! كل ما تبقى هو معرفة سبب عدم بدء تشغيل النواة، وبعدها نكون في القمة! كل هذا العمل قد يؤتي ثماره أخيرًا مع عدد قليل من لوحات beaglebone القابلة للاستخدام. هل يستحق الأمر؟ من الذي يحكم؟```
U-Boot SPL 2021.01-00001-gc59bf25a382-dirty (Jul 24 2024 - 20:38:49 -0400)
Trying to boot from MMC1


U-Boot 2021.01-00001-gc59bf25a382-dirty (Jul 28 2024 - 20:36:46 -0400)

CPU  : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM:  512 MiB
WDT:   Started with servicing (60s timeout)
NAND:  0 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... *** Warning - bad CRC, using default environment

<ethaddr> not set. Validating first E-fuse MAC
Net:   eth2: ethernet@4a100000, eth3: usb_ether
Hit any key to stop autoboot:  2 <0x08><0x08><0x08> 1 <0x08><0x08><0x08> 0 
WARNING: Could not determine device tree to use
switch to partitions #0, OK
mmc0 is current device
SD/MMC found on device 0
Failed to load 'boot.scr'
Failed to load 'uEnv.txt'
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
libfdt fdt_check_header(): FDT_ERR_BADMAGIC
<0x1b>7<0x1b>[r<0x1b>[999;999H<0x1b>[6n<0x1b>8Scanning disk [email protected]...
Scanning disk [email protected]...
** Unrecognized filesystem type **
Found 4 disks
No EFI system partition
BootOrder not defined
EFI boot manager: Cannot load any image
switch to partitions #0, OK
mmc0 is current device
SD/MMC found on device 0
4997632 bytes read in 353 ms (13.5 MiB/s)
Failed to load '/boot/undefined'

Starting kernel ...

إذن هناك مشكلة مع شجرة الأجهزة ("WARNING: Could not determine device tree to use"). هذه أول تجربة لي مع النواة وإقلاع النواة، لذا لا فكرة لدي عمّا يعنيه ذلك.

"إقلاع النواة" يتضمّن ما يلي، على حد فهمي:

  1. يُحمّل U-Boot ويبحث عن صورة النواة (zImage)؛ صورة النواة هي ملف ثنائي مضغوط للنواة، وzImage يفُكّ ضغط نفسه ذاتيًا
  2. تُحمَّل صورة النواة في الذاكرة، ثم تُفكّ ضغطها، إمّا ذاتيًا (zImage) أو بواسطة U-Boot (uImage)
  3. تنفّذ النواة مهامها المعتادة منخفضة المستوى، ثم تشغّل برامج/خفيّات init

جانب مهم في عملية إقلاع النواة هو شجرة الأجهزة. تُخزَّن هذه في ملف .dtb (ثنائي شجرة الأجهزة؛ قارن مع ملفات مصدر شجرة الأجهزة .dts) الخاصة باللوحة.

مشكلتي الآن هي أن U-Boot لا يُحمّل شجرة أجهزة اللوحة، إذ لا توجد رسالة reading /am335x-boneblack.dtb في السجلّ. وبدلًا من ذلك نحصل على WARNING: Could not determine device tree to use. إذن هذا دليل جيد! أظنّ أنّ السبب هو غياب معرّف اللوحة في EEPROM.

بعض التفاصيل حول كيف يتعرّف على اللوحة موجودة في هذا الموضوع على منتديات TI.

إذن، كيف يعرف U-Boot كيف يهيّئ نفسه ويقلع بشكل صحيح؟ داخل مصدر U-Boot الذي بنيناه، يوجد مجلد باسم configs/ يخزّن ملفات defconfig لمختلف اللوحات. تُعرّف هذه الملفات معاملات الإعداد المختلفة لـ U-Boot، بما في ذلك أمر الإقلاع، الذي قد يبدو بهذا الشكل:``` if test ${boot_fit} -eq 1; then run update_to_fit; fi; run findfdt; run init_console; run envboot; run finduuid; run distro_bootcmd

root@kitploit:~
نقوم بتحديد الإعداد الذي نستخدمه عند تشغيل هدف `make <boardname>_config`. تُستخدم الدالة `findfdt` لتحديد اللوحة التي نعمل عليها، وتقوم بتهيئة شجرة الأجهزة بشكل صحيح. تبدو كالتالي (معرّفة في `am335x_evm.h`):```
"findfdt="\
		"if test $board_name = A335BONE; then " \
			"setenv fdtfile am335x-bone.dtb; fi; " \
		"if test $board_name = A335BNLT; then " \
			"setenv fdtfile am335x-boneblack.dtb; fi; " \
		"if test $board_name = A335PBGL; then " \
			"setenv fdtfile am335x-pocketbeagle.dtb; fi; " \
		"if test $board_name = BBBW; then " \
			"setenv fdtfile am335x-boneblack-wireless.dtb; fi; " \
		"if test $board_name = BBG1; then " \
			"setenv fdtfile am335x-bonegreen.dtb; fi; " \
		"if test $board_name = BBGW; then " \
			"setenv fdtfile am335x-bonegreen-wireless.dtb; fi; " \
		"if test $board_name = BBBL; then " \
			"setenv fdtfile am335x-boneblue.dtb; fi; " \
		"if test $board_name = BBEN; then " \
			"setenv fdtfile am335x-sancloud-bbe.dtb; fi; " \
		"if test $board_name = A33515BB; then " \
			"setenv fdtfile am335x-evm.dtb; fi; " \
		"if test $board_name = A335X_SK; then " \
			"setenv fdtfile am335x-evmsk.dtb; fi; " \
		"if test $board_name = A335_ICE && test $ice_mii = rmii; then " \
			"setenv fdtfile am335x-icev2.dtb; fi; " \
		"if test $board_name = A335_ICE && test $ice_mii = mii; then " \
			"setenv fdtfile am335x-icev2-prueth.dtb; fi; " \
		"if test $fdtfile = undefined; then " \
			"echo WARNING: Could not determine device tree to use; fi; \0" \

إذا أردنا السلوك الافتراضي، هل يمكننا ببساطة تغيير المتغير board_name؟ حسنًا، ربما لا، أو على الأقل لا أعرف أفضل مكان لتغييره. لكن على الرغم من أن تعيين board_name لم يعمل بحد ذاته، إلا أنني في الواقع حدّثت أيضًا ملف .dtb الافتراضي وقد نجح ذلك!``` _____                    _____           _         _    
|  _  |___ ___ ___ ___   |  _  |___ ___  ||__ | |  
|     |  _| .'| . | . |  |   |  | . | | | -|  _|  _|
|
|
|| |__,|  ||  ||  || ||| ||||   
             |
|                    |___|             

Arago Project http://arago-project.org am335x-evm ttyS0

Arago 2021.09 am335x-evm ttyS0

am335x-evm login: root
root@am335x-evm:~#

root@kitploit:~
أخيرًا، وصلنا إلى طرفية. لوحاتي الخردة تعمل!

### إصلاح معرّف EEPROM المفقود

كخطوة أخيرة، سأكتب معرّف اللوحة الصحيح إلى EEPROM، وهو أمر سهل من داخل مساحة مستخدم Linux. من مصدر SPL، معرّفات اللوحات المختلفة هي:
- `A335BONE` - لوحة Beaglebone
- `A335BNLT` - لوحة Beaglebone Black
- `A335PBGL`
- `A335X_SK`
- `A33515BB`
- `A335_ICE`

وهناك أيضًا مراجعة اختيارية للوحة. استنادًا إلى المخطط، فإن EEPROM موجود على I2C0، والرقاقة نفسها (24LC32A في المخطط الخاص بي، على الرغم من أنها مُصنفة على أنها '256Kx8) توفر عنوان جهاز I2C بقيمة `0x50` (ثنائي `b1010` متبوعًا بعنوان الرقاقة `000`، نظرًا لأن الحزمة ذات 5 سنون لا تحتوي على دبابيس عنوان إضافية). نقطة أخيرة تجدر ملاحظتها: دبوس WP مربوط بمستوى HIGH بمقاومة سحب علوية 10k، لذا فإن الحماية ضد الكتابة مفعّلة افتراضيًا؛ يجب ربطه بمستوى LOW قبل أي كتابة، وإلا فسيُصدر إقرارًا لكنه ببساطة لن يكتب أي شيء.

يمكن الوصول إلى EEPROM من خلال النواة في `/sys/bus/i2c/devices/0-0050`، ويوجد بداخله ملف باسم `eeprom`. لذلك، مع سحب دبوس WP إلى LOW (اربط TP4 في الأعلى بالقرب من مقبس التيار المستمر بالأرض) يكفي بعض استدعاءات `echo`. يحتوي مرجع نظام Beaglebone Black على الصيغة. لقد اقتبست هذا [من هنا](https://groups.google.com/g/beagleboard/c/di5O5JCl4yw).```sh
root@am335x-evm:~# cat fix_eeprom.sh 
#!/bin/bash
# Fix board ID EEPROM

EEPROM_FILE=/tmp/eeprom.tmp
EEPROM=/sys/bus/i2c/devices/0-0050/eeprom

# header bytes
echo -ne "\xaa\x55\x33\xee" > ${EEPROM_FILE}
# Board ID
echo -n "A335BNLT" >> ${EEPROM_FILE}
# serial number (I left this basically as the template)
echo -n "000C24wwBBoxxxx" >> ${EEPROM_FILE}

dd if=${EEPROM_FILE} of=${EEPROM}

باستخدام less للتأكيد، ولا ينبغي أن نواجه أي مشكلة في تشغيل بطاقة SD افتراضية من الآن فصاعدًا. يمكن التراجع عن التغييرات التي تطرأ على إعدادات SPL وU-Boot بمجرد كتابة EEPROMs في جميع اللوحات.

تنزيل الأداة
RegionStart AddressLength
Boot ROM (Public)0x4002_00000xBFFF
Boot ROM (Public, alias)0x0002_00000xBFFF
SRAM Internal0x402F_04000xFC00
L3 OCM00x4030_00000x10000
نقطة الاختبارالتوصيلورقة المخططجانب اللوحة
TP1DGND2 (D1)العلوي
TP2VDD_MPUON (VDD_MPU_MON)5 (C4)العلوي
TP3TESTOUT5 (B2)العلوي
TP4Board ID WP11 (B1)العلوي