في منشوراتي السابقة لـ EJTAG: نقطة جذب للمتسللين و Black Swift: باستخدام EJTAG ، تم النظر في أسهل طريقة لاستخدام EJTAG - التحميل في ذاكرة الوصول العشوائي وبدء برنامج المستخدم. ومع ذلك ، لا تقتصر قدرات EJTAG على ذلك. يصف المنشور كيفية تنظيم تصحيح الأخطاء البرمجية البسيطة باستخدام EJTAG ، باستخدام أدوات مجانية openocd و GDB.تمت مطالبتي بكتابة هذا المنشور برسالة من قارئ طلب مني أن أبقى مجهولاً وطلب مني المساعدة - لا يتم تشغيل جهاز يستند إلى AR9344 (يتم تعليقه في مرحلة تهيئة U-boot) - كيف يمكنني معرفة المشكلة مع EJTAG؟نظرًا لعدم وجود جهاز يعتمد على AR9344 في متناول اليد ، ولكن تبين أن لوحة Black Swift Pro القائمة على شريحة AR9331 ذات الصلة هي السرد مع التركيز عليه. أعتقد أن التغييرات التي يجب إجراؤها على AR9344 ليست كبيرة.دعناننتقل إلى بيان المشكلة: هناك لوحة Black Swift Pro ، التي اتصلنا بها باستخدام openocd عبر EJTAG ووضع المعالج في وضع الإيقاف.يلزم تنفيذ عدة عشرات من تعليمات المعالج بالتسلسل ، والتوقف بعد تنفيذ كل تعليمة ، وإذا لزم الأمر ، فحص محتويات ذاكرة الوصول العشوائي أو ذاكرة القراءة فقط التمهيد أو سجلات وحدة التحكم الطرفية أو سجلات المعالج.لحل المشكلة ، أرى طريقتين على الأقل:- بسيطة - فقط باستخدام openocd - الوظيفة الأساسية لتنفيذ الإجراءات المطلوبة موجودة بالفعل في openocd. من الضروري فقط أن تكون قادرًا على استخدامه ؛
- معقد - باستخدام حزمة openocd + GDB - سيتحكم المستخدم في عملية تنفيذ تعليمات المعالج عبر GDB ، وسيقوم openocd بتحويل طلبات GDB إلى أوامر EJTAG.
الآن فكر في كلا الحلين بمزيد من التفصيل., Black Swift: EJTAG.
1: openocd
يجب على أولئك الذين قرأوا مطبوعاتي السابقة حول EJTAG أن يتذكروا أن openocd يظهر فيها كمنفذ برنامج نصي غبي (ملفات التكوين) ، والذي يبدو أنه يعمل في وضع الدفعة ولا يوفر تفاعل المستخدم. ومع ذلك ، ليس كذلك. في الواقع ، أثناء تشغيل برنامج openocd ، من الممكن "سؤاله" لتنفيذ أمر باستخدام واجهة سطر الأوامر. للوصول إلى واجهة سطر الأوامر ، يقوم openocd بتشغيل خادم telnet.بشكل افتراضي ، سيتم استخدام منفذ TCP 4444 لخادم telnet. إذا لزم الأمر ، يمكن تغيير رقم منفذ TCP باستخدام الخيار telnet_port
(انظر المثال أدناه).دعونا نحاول تتبع برنامج bootloader للوحة Black Swift باستخدام openocd.مثال ملف التكوينblack-swift-trace.cfg
لـ openocd ، الذي يفرض openocd على خادم telnet استخدام المنفذ 4455: المصدر [find interface / ftdi / tumpa.cfg]
المحول 6000 كيلو
المصدر [find black-swift.cfg]
التلنت 4455
فيه
وقف
تشغيل openocd 0.9.0 كجذر يبدو كالتالي: # openocd -f black-swift-trace.cfg
المصحح المفتوح على الشريحة 0.9.0 (2015-05-28-17: 08)
مرخص بموجب GNU GPL v2
لتقارير الأخطاء ، اقرأ
http://openocd.org/doc/doxygen/bugs.html
لا شيء منفصل
سرعة المحول: 6000 كيلو هرتز
معلومات: التحديد التلقائي لأول جلسة نقل متاحة "jtag". لإلغاء استخدام "النقل حدد <transport>".
خطأ: لم يتم العثور على جهاز
خطأ: غير قادر على فتح جهاز ftdi مع vid 0403 و pid 8a98 والوصف "*" والمسلسل "*"
معلومات: سرعة الساعة 6000 كيلو هرتز
معلومات: JTAG tap: ar9331.cpu tap / الجهاز الموجود: 0x00000001 (mfg: 0x000 ، الجزء: 0x0000 ، الإصدار: 0x0)
الدولة المستهدفة: أوقفت
تم إيقاف الهدف في وضع MIPS32 بسبب طلب التصحيح ، الكمبيوتر الشخصي: 0xbfc00000
الدولة المستهدفة: أوقفت
تم إيقاف الهدف في وضع MIPS32 بسبب جهاز كمبيوتر أحادي الخطوة: 0xbfc00404
الآن يمكننا فتح نافذة طرفية أخرى والاتصال بخادم teloc openocd باستخدام البرنامج telnet
: $ التلنت المستضيف 4455
جرب :: 1 ...
جارٍ محاولة 127.0.0.1 ...
متصل بالمضيف المحلي.
حرف الهروب هو "^]".
فتح المصحح على الشريحة
>
من السهل الحصول على قائمة بجميع أوامر openocd باستخدام الأمر help
.من أجل تنفيذ تعليمات المعالج خطوة بخطوة ، فإن الأمر step
التالي مفيد لنا : الخطوة [العنوان]
تنفيذ تعليمات واحدة على العنوان المحدد في السجل
عداد الأوامر (PC). إذا تم تحديد معلمة العنوان ، ثم
سيتم تنفيذ التعليمات بدءًا من عنوان العنوان.
يبدو تنفيذ تعليمات المعالج خطوة بخطوة في وحدة التحكم كما يلي: > الخطوة 0xbfc00400
الدولة المستهدفة: أوقفت
تم إيقاف الهدف في وضع MIPS32 بسبب جهاز كمبيوتر أحادي الخطوة: 0xbfc00404
> خطوة
الدولة المستهدفة: أوقفت
تم إيقاف الهدف في وضع MIPS32 بسبب جهاز كمبيوتر أحادي الخطوة: 0xbfc00408
> خطوة
الدولة المستهدفة: أوقفت
تم إيقاف الهدف في وضع MIPS32 بسبب جهاز كمبيوتر أحادي الخطوة: 0xbfc0040c
قد تكون أوامر openocd التالية مفيدة أيضًا: reg [(register_number | register_name) [(القيمة | 'force')]]]
قراءة أو كتابة قيمة تسجيل المعالج.
استدعاء reg بدون معلمات ينتج عنه إخراج كافة التسجيلات.
إذا تم استخدام المعامل 'force' ، أجبر
طرح التسجيل من المعالج (بدلاً من إصدار ذاكرة التخزين المؤقت
القيم).
mwb ['phys'] address value [count]
address , value.
phys, address — ,
— .
count address
count,
value.
mwh ['phys'] address value [count]
mwb, 16- .
mww ['phys'] address value [count]
mwb, 32- .
mdb ['phys'] address [count]
address.
phys, address — ,
— .
count
صفيف في عنوان بايت طول طول العنوان.
mdh ['phys'] العنوان [count]
الأمر مشابه لـ mdb ، ولكن تتم قراءة كلمة 16 بت بدلاً من بايت.
mdw ['phys'] العنوان [count]
الأمر مشابه لـ mdb ، ولكن تتم قراءة كلمة 32 بت بدلاً من بايت.
كما ترى ، لسوء الحظ ، فإن أحدث إصدار (في وقت كتابة هذه السطور) من openocd 0.9.0 غير قادر على تفكيك تعليمات المعالج باستخدام بنية MIPS ، على الرغم من وجود مثل هذا المفكك للمعالجات ذات بنية ARM .عدم وجود أداة تفكيك يجعل تنفيذ تعليمات المعالج خطوة بخطوة مباشرة مع openocd غير مريح للغاية. يمكنك زيادة مستوى الراحة إذا كنت تستخدم GDB.الحل 2: استخدم حزمة openocd + GDB
في حزمة openocd + GDB ، يتم توزيع الأدوار على النحو التالي: يتواصل المستخدم مع GDB ، الذي يوفر واجهة ملائمة لتصحيح الأخطاء ، والاستخلاص من الآلية التي يتم من خلالها تنفيذ التعليمات ، ويتولى openocd مهمة التحكم المباشر في المعالج وفقًا لتعليمات GDB.استخدام GDB للتحكم في تنفيذ التعليمات على معالج MIPS من خلال EJTAG له مزايا عديدة على openocd:- كما هو مذكور أعلاه ، تم دمج أداة تفكيك بنية MIPS في GDB ؛
- من الممكن استخدام معلومات التصحيح من المصدر ؛ على سبيل المثال ، إذا كنت تقوم بتصحيح برنامج C الخاص بك ، فسيكون GDB قادرًا على إظهار سطر C-code الذي يتم تنفيذه حاليًا وتفصيل حالة متغيرات البرنامج بدقة ، وليس خلايا الذاكرة ذات العناوين الغامضة ؛
- openocd GDB GDB Remote Serial Protocol; qemu , , — GDB;
- , GDB TAB.
يجب أن يوضع في الاعتبار أن GDB تعمل بمفاهيم عالية المستوى ، وأن openocd مجبر على العمل مع المعدات التي لا يمكن دائمًا تنفيذ قائمة أمنيات GDB باستخدام EJTAG.على سبيل المثال ، يرشد المستخدم GDB لتعيين نقطة توقف على العنوان المحدد ، يذهب هذا التعليمات إلى openocd ، لكن معالجات MIPS openocd لديها طريقتان على الأقل لتعيين نقطة توقف:- sdbbp, , openocd sdbbp , sdbbp , . software breakpoint. , .
- . . , hardware breakpoint, .
على الرغم من وجود اختلاف بين نقطة توقف الأجهزة ونقطة توقف البرنامج داخل بروتوكول التسلسل عن بُعد لـ GDB (انظر الحزم z و z0 في وصف البروتوكول ) ، ويوفر GDB خيارات مناسبة لاختيار نوع نقاط التوقف ، فقد يتبين أن هناك قيودًا على استخدام النقاط على معالج معين انهيار نوع أو آخر. وفقًا لذلك ، فإن openocd لديه خيار gdb_breakpoint_override
يسمح لك بفرض إحدى الطريقتين الموصوفتين لتنظيم نقاط التوقف.لتوصيل مصحح أخطاء GDB ، يقوم openocd بتطبيق خادم GDB ، والذي يستخدم بشكل افتراضي منفذ TCP 3333. إذا لزم الأمر ، يمكن تغيير رقم منفذ TCP باستخدام الخيار gdb_port
.للاتصال بخادم openocd GDB الذي يتحكم في معالج MIPS ، نحتاج إلى إصدار خاص من GDB مع دعم MIPS. أفترض أن القارئ يستخدم أجهزة الكمبيوتر المستندة إلى x86 / amd64 التي تشغل Debian Linux لتشغيل openocd و GDB ، باستخدام mips-linux-gnu-gdb من حزمة Sourcery CodeBench. حول كيفية تثبيت هذه الحزمة مكتوبة هنا ، ما عليك سوى إدخال التعديل ، أنه في وقت كتابة هذه السطور ، كان الأخير هو إصدار Sourcery CodeBench mips-2015.05-18 ، الذي تم إصداره في مايو 2015 ، يمكنك تنزيل الأرشيف هنا باستخدام هذا الرابط .دعونا نحاول استخدام حزمة openocd + GDB عمليًا. تشغيل openocd: # openocd -f run-u-boot_mod-trace.cfg \
> -c "gdb_breakpoint_override hard" -c "step 0xbfc00400"
سيوفر الأمران الأخيران لـ openocd ما يلي:- سيتم استخدام نقاط توقف الأجهزة ، بغض النظر عن ما يتخيله GDB هناك (العناوين 0xbfc0xxxx تتوافق مع ROM ، لذلك لن تعمل نقاط التوقف في البرنامج) ؛
- سيتم تنفيذ تعليمة واحدة من العنوان 0xbfc00400 ، وبعد ذلك سيتوقف المعالج مرة أخرى.
افتح نافذة طرفية أخرى وقم بتشغيل GDB: $ /opt/mips-2015.05/bin/mips-linux-gnu-gdb
GNU gdb (Sourcery CodeBench Lite 2015.05-18) 7.7.50.20140217-cvs
حقوق النشر (C) 2014 Free Software Foundation، Inc.
ترخيص GPLv3 +: GNU GPL الإصدار 3 أو الأحدث <http://gnu.org/licenses/gpl.html>
هذا برنامج مجاني: أنت حر في تغييره وإعادة توزيعه.
لا يوجد ضمان ، إلى الحد الذي يسمح به القانون. اكتب "إظهار النسخ"
و "إظهار الضمان" للحصول على التفاصيل.
تم تكوين GDB هذا على أنه "--host = i686-pc-linux-gnu --target = mips-linux-gnu".
اكتب "إظهار التكوين" للحصول على تفاصيل التكوين.
للحصول على تعليمات الإبلاغ عن الخطأ ، يرجى الاطلاع على:
<https://sourcery.mentor.com/GNUToolchain/>.
ابحث عن دليل GDB وموارد التوثيق الأخرى عبر الإنترنت على:
<http://www.gnu.org/software/gdb/documentation/>.
للمساعدة ، اكتب "مساعدة".
اكتب "apropos word" للبحث عن الأوامر المتعلقة بـ "word".
(gdb)
الآن نشرح GDB نوع المعالج الذي سنعمل معه ، ونطلب تفكيك التعليمات التنفيذية التالية للمعالج ، وأخيرًا الاتصال بخادم GDB openocd: (gdb) تعيين mips العمارة: isa32r2
يفترض أن العمارة الهدف هي mips: isa32r2
(gdb) حدد endian big
من المفترض أن يكون الهدف كبيرًا
(gdb) قم بتشغيل تفكيك السطر التالي
(gdb) الهدف البعيد: 3333
التصحيح عن بعد باستخدام: 3333
0xbfc00404 في ؟؟ ()
=> 0xbfc00404: 40 80 08 00 mtc0 صفر ، c0_random
لتنفيذ تعليمات معالج واحد في GDB ، يتم استخدام الأمر stepi
. دعونا نحاول اتباع بعض الإرشادات من المعالج ولا تدع GDB يحذرك من نقص معلومات التصحيح (لا يمكن العثور على بداية الوظيفة) ، خذ هذه المعلومات من أي مكان. (gdb) stepi
تحذير: لا يمكن لـ GDB العثور على بداية الوظيفة عند 0xbfc00408.
يتعذر على GDB العثور على بداية الوظيفة عند 0xbfc00408
وبالتالي لا يمكن تحديد حجم إطار مكدس تلك الوظيفة.
وهذا يعني أن GDB قد يتعذر الوصول إلى إطار المكدس هذا ، أو
الإطارات تحته.
هذه المشكلة على الأرجح بسبب عداد برنامج غير صالح أو
مؤشر المكدس.
ومع ذلك ، إذا كنت تعتقد أن GDB يجب أن يبحث ببساطة إلى أبعد من ذلك
من 0xbfc00408 للرمز الذي يشبه بداية a
وظيفة ، يمكنك زيادة نطاق البحث باستخدام `مجموعة
الأمر التوجيهي السياج بعد.
0xbfc00408 في ؟؟ ()
=> 0xbfc00408: 40 80 10 00 mtc0 صفر ، c0_entrylo0
(gdb) stepi
تحذير: لا يمكن لـ GDB العثور على بداية الوظيفة عند 0xbfc0040c.
0xbfc0040c في ؟؟ ()
=> 0xbfc0040c: 40 80 18 00 mtc0 صفر ، c0_entrylo1
(gdb) stepi
تحذير: لا يمكن لـ GDB العثور على بداية الوظيفة عند 0xbfc00410.
0xbfc00410 في ؟؟ ()
=> 0xbfc00410: 40 80 20 00 mtc0 صفر ، c0_context
(gdb)
اقرأ الآن كلمة 32 بت على 0xbfc00408: (gdb) p / x * 0xbfc00408
1 دولار = 0x40801000
لطباعة حالة تسجيلات المعالج ، استخدم الأمر info registers
: (gdb) يسجل معلومات
صفر عند v0 v1 a0 a1 a2 a3
R0 00000000 37c688e2 22b15a00 28252198 0c12d319 4193c014 84e49102 06193640
t0 t1 t2 t3 t4 t5 t6 t7
R8 00000002 9f003bc0 92061301 1201c163 31d004a0 92944911 ac031248 b806001c
s0 s1 s2 s3 s4 s5 s6 s7
R16 8bc81985 402da011 c94d2454 88d5a554 81808e0d cc445151 4401a826 50020402
t8 t9 k0 k1 gp sp s8 ra
R24 01c06b30 01000000 10000004 fffffffe 9f003bc0 54854eab 329d626b bfc004b4
حالة لو مرحبا badvaddr يسبب جهاز الكمبيوتر
00400004 00244309 b9ca872c ed6a1f00 60808350 bfc00410
fcsr التنوب
00000000 00000000
GDB هي أداة متقدمة مع عدد كبير من الأوامر والخيارات. لمزيد من التعارف مع GDB ، أحيل القارئ إلى وثائق GDB الرسمية .جاري تهيئة وحدة التحكم في ذاكرة الوصول العشوائي AR9331
دعونا نرى كيف يمكن لـ GDB حل مشكلة معينة: من خلال تتبع تنفيذ U-boot على لوحة Black Swift ، سنحدد سلسلة من الإدخالات في سجلات وحدة تحكم RAM ، مما يؤدي إلى تهيئتها. إن اكتشاف مثل هذا التسلسل مفيد للغاية إذا أردنا تشغيل البرامج على Black Swift باستخدام openocd لتجاوز U-boot. كما أن تسلسل التهيئة هذا مفيد عند إنشاء محمل إقلاع بديل لـ Black Swift.قم بتشغيل openocd (تمامًا كما في القسم السابق): # openocd -f run-u-boot_mod-trace.cfg \
> -c "gdb_breakpoint_override hard" -c "step 0xbfc00400"
لتشغيل GDB ، أنشئ برنامجًا نصيًا bs-u-boot-trace-gdb.conf
: تعيين mips العمارة: isa32r2
تعيين endian كبيرة
تعيين تفكيك السطر التالي على
الهدف البعيد: 3333
إيقاف ترقيم الصفحات
تعيين ملف التسجيل bs_gdb.log
تعيين تسجيل الدخول
بينما $ pc! = (void (*) ()) 0x9f002ab0
ربيب
يسجل المعلومات
النهاية
فصل
استقال
مقارنة بالمثال في القسم السابق ، يتسبب هذا النص البرمجي في قيام GDB بتكرار الإخراج إلى ملف bs_gdb.log
، وتعطيل ترقيم الصفحات (تأكيد تحول الصفحة بواسطة المستخدم) ، ثم يبدأ في تنفيذ تعليمات المعالج دوريًا وعرض حالة تسجيلات المعالج بعد كل تعليمة. عندما يصل تسجيل الكمبيوتر (سجل العنوان الخاص بالتعليمات التالية) إلى القيمة 0x9f002ab0 ، ينقطع GDB من openocd ويتوقف عن العمل. وهكذا ، في نهاية GDB ، سيتم إنشاء ملف bs_gdb.log
مع تتبع كامل لتعليمات المعالج.سيكون إطلاق GDB على النحو التالي: $ /opt/mips-2015.05/bin/mips-linux-gnu-gdb -x bs-u-boot-trace-gdb.conf
ملاحظة: من bs-u-boot-trace-gdb.conf
المرجح أن البرنامج النصي لن يعمل على الفور بعد تطبيق الطاقة على اللوحة ، نظرًا لأن u-boot_mod سيعيد أيضًا ضبط أخطاء الغموض AR9331 ، مما سيتسبب في توقف البرنامج النصي عن التنفيذ. في هذه الحالة ، أوقف openocd و GDB ، ثم قم بتشغيل openocd و GDB مرة أخرى.
الآن كل شيء صغير - تحتاج إلى تحديد bs_gdb.log
جميع تعليمات الكتابة من الملف sw
(كلمة مخزن ، أي كتابة قيم 32 بت). تكون سجلات وحدة التحكم في الذاكرة AR9331 32 بت ، بحيث يمكن تجاهل الإرشادات الأخرى من عائلة المتجر بأمان.نظرًا لأن المفكك ينتج فقط أسماء سجلات الحجة للتعليماتsw
=> 0xbfc004ec: الإعلان f9 00 00 sw t9،0 (t7)
ولكن ليس قيمهم ، لا يكفي bs_gdb.log
تحديد جميع الأسطر التي تحتوي على العبارة sw من الملف . لتحديد القيمة التي تمت كتابتها إلى أي عنوان باستخدام sw ، يجب أن يخضع الملف bs_gdb.log
لمعالجة إضافية. يمكن أن تتم المعالجة باستخدام البرنامج النصي parse_gdb_output.pl:
my %r;
foreach $i (qw(zero at v0 v1 a0 a1 a2 a3 t0 t1 t2 t3 t4 t5 t6 t7
s0 s1 s2 s3 s4 s5 s6 s7 t8 t9 k0 k1 gp sp s8 ra)) {
$r{$i} = "none";
}
sub parse_reg($)
{
$_ = $_[0];
if (/^ R/) {
my @fields = split m'\s+';
my $f = 2;
my @rgs;
@rgs = qw(zero at v0 v1 a0 a1 a2 a3) if (/^ R0/);
@rgs = qw(t0 t1 t2 t3 t4 t5 t6 t7) if (/^ R8/);
@rgs = qw(s0 s1 s2 s3 s4 s5 s6 s7) if (/^ R1/);
@rgs = qw(t8 t9 k0 k1 gp sp s8 ra) if (/^ R2/);
foreach $i (@rgs) {
$r{$i} = $fields[$f];
$f = $f + 1;
}
}
}
while (<>) {
if (/^=>([^s]*)\tsw\t([^,]*),(\d+)\(([^)]*)\)/) {
my $rs = $2;
my $offset = $3;
my $rd = $4;
parse_reg(<>);
parse_reg(<>);
parse_reg(<>);
parse_reg(<>);
print("$1 sw $rs={0x$r{$rs}}, $offset($rd={0x$r{$rd}})\n");
}
}
الإطلاق parse_gdb_output.pl
على النحو التالي:$ grep "^ = \ | ^ R" bs_gdb.log | ./parse_gdb_output.pl
إليك مقتطف من الإخراج parse_gdb_output.pl
(تم إدخال علامتي <<< PLL
"و" <<< DDR
يدويًا لاحقًا): ...
0x9f002700: ad cf 00 00 sw t7 = {0x00dbd860}، 0 (t6 = {0xb8116248})
0x9f00271c: الإعلان f9 00 00 sw t9 = {0x000fffff} ، 0 (t7 = {0xb800009c})
0x9f0027a0: الإعلان f9 00 00 sw t9 = {0x00018004}، 0 (t7 = {0xb8050008}) <<< PLL
0x9f0027dc: ad f9 00 00 sw t9={0x00000352}, 0(t7={0xb8050004}) <<<
0x9f002840: ad f9 00 00 sw t9={0x40818000}, 0(t7={0xb8050000}) <<<
0x9f002898: ad f9 00 00 sw t9={0x001003e8}, 0(t7={0xb8050010}) <<<
0x9f0028f4: ad f9 00 00 sw t9={0x00818000}, 0(t7={0xb8050000}) <<<
0x9f002970: ad cf 00 00 sw t7={0x00800000}, 0(t6={0xb8116248})
...
0x9f002994: ad cf 00 00 sw t7={0x40800700}, 0(t6={0xb8116248})
0x9f002a54: ad f9 00 00 sw t9={0x00008000}, 0(t7={0xb8050008})
0x9f00309c: af 38 00 00 sw t8={0x7fbc8cd0}, 0(t9={0xb8000000}) <<< DDR
0x9f0030b0: af 38 00 00 sw t8={0x9dd0e6a8}, 0(t9={0xb8000004}) <<<
0x9f0030dc: af 38 00 00 sw t8={0x00000a59}, 0(t9={0xb800008c}) <<<
0x9f0030ec: af 38 00 00 sw t8 = {0x00000008}، 0 (t9 = {0xb8000010}) <<<
...
نظرًا لأن عناوين سجلات مولد إشارة الساعة (PLL) وعناوين سجلات وحدة تحكم الذاكرة من نوع DDR معروفة ، فمن السهل معرفة الأرقام التي يجب كتابتها إلى العناوين من أجل تهيئة وحدة تحكم RAM بشكل صحيح.ملخص
كما ترى ، فإن التصحيح من خلال JTAG باستخدام openocd و GDB ليس صعبًا على الإطلاق ، وأساليب العمل الموصوفة مناسبة ليس فقط لـ AR9331 ، ولكن بعد بعض التكيف ، وحتى مع المعالجات ذات بنية مختلفة ، والتي يوجد لها دعم في openocd و GDB.