Моя предыдущая публикация EJTAG: аттракцион для хакеров хотя и была тепло встречена общественностью, имела некоторые недочёты: к примеру, была продемонстрирована уж слишком низкая производительность передачи по EJTAG (аж целых 2 КБ/с!).
К сожалению, я умудрился привести интерфейс JTAG платы MR3020 в полную негодность (был оторван провод TDI вместе с кусочком SMD-резистора R16). Так как устранить поломку не удалось, то недочёты остались неисправленными.
Несколько дней назад я получил от руководителя проекта Black Swift Дмитрия Жеребкова плату Black Swift Pro с адаптером. Плата Black Swift Pro во многом аналогична MR3020, а значит у меня появилась возможность написать публикацию про EJTAG на Black Swift и устранить прошлые недочёты!
Предупреждение: ниже излагается методика работы с EJTAG для платы Black Swift при помощи свободного ПО; предполагается, что читатель не впадает в ступор от интерфейса командной строки, знает кто такие AR9331, MR3020 и EJTAG, в курсе где брать кросс-компилятор для MIPS и догадывается зачем используют minicom. При возникновении какого-либо вопросов по использованию EJTAG на плате Black Swift автор рекомендует сначала поискать ответ в публикации EJTAG: аттракцион для хакеров.
Black Swift: отличие от TP-Link MR3020
Black Swift в отличии от TP-Link MR3020 не является законченным изделием, предназначенным для решения фиксированной задачи.
Как раз наоборот: плата Black Swift предназначена для того, чтобы пользователи могли легко создавать на её базе собственные изделия. По этой причине плата Black Swift имеет гораздо меньше ограничений (как программных, так и аппаратных) для проведения экспериментов.
Размеры Black Swift весьма невелики, на плате используются разъёмы, по своей природе аналогичные разъёмам на Arduino-подобных платах, но имеющие в два раза меньшее расстояние между соседними соединителями — 1,27 мм. По причине такой миниатюрности для упрощения макетирования с такой платой предлагается использовать специальный адаптер, который позволяет использовать привычные «большие» провода от Arduino:
SD-карта на фотографии приведена исключительно для масштаба.
В ПЗУ Black Swift прошит штатный для этой платы загрузчик на базе u-boot_mod.
При старте загрузчик выдаёт следующее сообщение в UART (настройки UART: 115200 8n1):
********************************************* * U-Boot 1.1.4 (Apr 1 2015) * ********************************************* U-Boot for Black Swift board (AR9331) DRAM: 64 MB DDR2 16-bit FLASH: Winbond W25Q128 (16 MB) CLOCKS: 400/400/200/33 MHz (CPU/RAM/AHB/SPI)
Если в течении 1 секунды пользователь не отправляет в UART никакого символа, то производится загрузка ОС на базе OpenWrt. Если же пользователь успеет отправить загрузчику какой-нибудь символ, то становится доступной командная строка загрузчика. Из этой строки возможно дать команду загрузить данные в ОЗУ (через UART по Y-Modem или через Ethernet по tftp) и передать управление на любой адрес; даже есть команды для перепрограммирования ПЗУ.
На первый взгляд Black Swift для разработчика встроенного программного обеспечения имеет массу преимуществ перед MR3020:
- подключаться к практически любым интерфейсам AR9331 (в том числе и JTAG) стало гораздо проще;
- загрузчик довольно дружелюбен (в MR3020 возможности по загрузке кодов пользователя в ОЗУ были сильно ограничены);
- если ничего не трогать, вход GPIO11/JS AR9331 на Black Swift оказывается подключённым к VDD25, что соответствует разрешению использовать EJTAG (напомню, что на MR3020 для этого надо было специально жать кнопку SW1);
- в версии Black Swift Pro появилась возможность подключить загрузочное ПЗУ к программатору (см. ниже об особенностях AR9331).
Но, как говорится, поверьте, у нас тут не всё так однозначно...
Ничто не предвещало беды
Оказалось, что штатная прошивка на базе u-boot_mod, хотя и позволяет загружать в ОЗУ код и исполнять его, но, как и штатная прошивка MR3020, враждебно настроена по отношению к любителям EJTAG и норовит программно отключить EJTAG вскоре после старта.
Использовать опыт MR3020, связанный с отключением загрузочного ПЗУ SPI Flash за счёт разрыва цепи CS, довольно затруднительно
несмотря на то, что на Black Swift Pro есть микропереключатели, обеспечивающие отключение загрузочного ПЗУ от AR9331 (что по задумке авторов позволяет использовать внешний программатор). Доступ к этим микропереключателям на плате, установленной на стандартный адаптер, мягко говоря затруднён. Для того, чтобы аккуратно перевести переключатели в нужное положение плату придётся снимать с адаптера. На плате Black Swift (не Pro!) и вовсе нет никаких микропереключателей. Теоретически возможно припаять пару проводов к микропереключателю или микросхеме ПЗУ для замыкания/размыкания цепи CS снаружи, но такая доработка требует умения хорошо паять (думаю фото ниже даёт представление о сложности доработки).
Зачем вообще понадобились микропереключатели на Black Swift Pro?
Дело в том, что попытки перепрограммировать (или даже просто прочитать) микросхему загрузочного ПЗУ SPI Flash без отсоединения от AR9331 обречены на неудачу − AR9331 упорно драйверит линии SPI даже при активной линии RESET. На Black Swift Pro четыре микропереключателя позволяют решить эту проблему, путём полного отключения загрузочного ПЗУ от AR9331, что и позволяет успешно использовать внешний программатор.
Таким образом, использовать методику оживления JTAG, успешно работающую на MR3020, будет затруднительно.
Мы пойдём другим путём.
«Грязный хак позволит поюзать JTAG»
Итак проблема состоит в том, что загрузчик u-boot_mod отключает JTAG.
Давайте посмотрим, что делает u-boot_mod при старте (напомню, что после снятия сигнала сброс любой процессор с архитектурой MIPS начинает исполнять инструкции с адреса
0xbfc00000
):bfc00000: 100000ff b 0xbfc00400 bfc00004: 00000000 nop
Если заменить команду ветвления на адрес
0xbfc00400
на команду ветвления на адрес 0xbfc00000
, вот так:bfc00000: 1000ffff b 0xbfc00000 bfc00004: 00000000 nop
то u-boot_mod не сможет сделать своё чёрное дело по отключению JTAG.
Конечно предложенное решение уж очень топорно...
и гораздо лучше было бы исправить штатную прошивку так, чтобы доступ к JTAG оставался всегда (или хотя бы при определённом условии), но, к сожаление, на момент подготовки данной публикации исходные тексты штатной прошивки недоступны (см. форум black-swift.ru), а вносить нетривиальные исправления в бинарную прошивку совершенно не хочется.
Замену можно сделать при помощи внешнего программатора, но чтобы не делать лишних движений лучше использовать barebox.
Предупреждение! Описанные ниже действия могут привести к полному окирпичиванию вашей платы Black Swift.
Сборка barebox
Для модификации уже прошитого в загрузочное ПЗУ загрузчика u-boot_mod мы используем barebox из официального git-репозитория barebox.org. Скачиваем исходные тексты из ветки next:
$ git clone -b next git://git.pengutronix.de/git/barebox.git
Почему надо скачивать именно ветку next из git-репозитория?
Ветку next мы тащим ради единственного тривиального изменения, которое научит barebox работать с микросхемой ПЗУ, установленной в Black Swift:
На момент публикации данное изменение ещё не попало в стабильную версию barebox.
commit bd3e5011346e3d4d03ac076ada5768c2cf197dc4 Author: Antony Pavlov <antonynpavlov@gmail.com> Date: Mon Apr 13 23:56:41 2015 +0300 mtd: m25p80: import ID for Winbond W25Q128FVSSI from linux Winbond W25Q128FVSSI chip is used in Black Swift board, (see http://www.black-swift.com for details).
На момент публикации данное изменение ещё не попало в стабильную версию barebox.
Переходим в каталог с исходными текстами, производим конфигурацию и сборку:
$ cd barebox barebox$ export ARCH=mips barebox$ export CROSS_COMPILE=/opt/mips-2014.11/bin/mips-linux-gnu- barebox$ make tplink-mr3020_defconfig barebox$ sed "s/# CONFIG_PARTITION is not set/CONFIG_PARTITION=y/" -i .config barebox$ sed "s/# CONFIG_CMD_PARTITION is not set/CONFIG_CMD_PARTITION=y/" -i .config barebox$ make
Если сборка прошла успешно, то получим файл
barebox.bin
.Остаётся загрузить этот файл в ОЗУ Black Swift и стартовать.
Подключение макетки FT2232H к Black Swift
Для подключения к интерфейсам UART и JTAG микросхемы AR9331 будем использовать макетку FT2232H (см. также FT2232 breakout board).
Хотя плата Black Swift Pro имеет собственный преобразователь USB-UART, использовать для подключения UART макетку FT2232H по ряду причин удобнее:
- в этом случае для подключения используется только один кабель USB;
- запитывая Black Swift от макетки (хотя её нагрузочная способность ограничена) и желая на некоторое время отключить питание от Black Swift мы отключаем линию питания от макетки; так мы избавлены от проблемы с перенумерацией последовательных портов USB (ttyUSBxx) в linux, которая может возникнуть, если мы временно отключим Black Swift Pro;
- уменьшаяется вероятность повредить Black Swift вставляя/вынимая разъём microUSB;
- используя макетку не надо задумываться, с каким вариантом Black Swift работаешь (Pro или не Pro) (всё равно макетка нужна для подключения по JTAG).
Вот схема подключения:
Внешний вид Black Swift с подключённой макеткой FT2232H:
Модификация u-boot_mod
После того, как макетка FT2232H подключена к Black Swift можно приступить к модификации u-boot_mod. Для этого запускаем minicom.
Далее перезапускаем Black Swift (сделать это можно либо временно оторвав питание от платы, либо замкнув вход RESET на «землю» (0V)).
Как только в minicom заметим сообщение
Hit any key to stop autobooting
нужно прервать загрузку, нажав в minicom любую клавишу, после чего должно появиться приглашение загрузчика
BSB>
.Теперь загружаем в ОЗУ
barebox.bin
по протоколу Y-Modem (для использования Y-Modem в Debian помимо minicom должен быть установлен пакет lrzsz).Для этого в командной строке загрузчика запускаем приём данных по Y-Modem:
BSB> loady 0xa0200000
В minicom'е запускаем отправку. Если minicom настроен так, как в публикации EJTAG: аттракцион для хакеров то для отправки файла понадобиться нажать Ctrl-B S, в меню выбрать ymodem, а затем либо выбрать файл при помощи меню, либо нажать Enter и ввести путь к файлу
barebox.bin
вручную.После успешного окончания загрузки остаётся только передать управление на адрес загрузки
barebox.bin
:BSB> go 0xa0200000 ## Starting application at 0xA0200000... barebox 2015.04.0-00103-g8397d68 #1 Fri Apr 17 08:59:12 MSK 2015 Board: TP-LINK MR3020 m25p80 m25p80@00: w25q128 (16384 Kbytes) malloc space: 0xa0400000 -> 0xa07fffff (size 4 MiB) environment load /dev/env0: No such file or directory Maybe you have to create the partition. running /env/bin/init... /env/bin/init not found barebox:/
Строка
m25p80 m25p80@00: w25q128 (16384 Kbytes)
из выдачи выше и говорит нам, что barebox нашёл загрузочное ПЗУ w25q128.
Для доступа к загрузочному ПЗУ barebox создаёт устройство /dev/spiflash. Для того, чтобы работать с ПЗУ было удобнее, а также чтобы уменьшить вероятность возникновения ошибки, разделим ПЗУ на разделы. Для этого обратимся к описанию структуры флэш-памяти
Используем команду addpart для выделения разделов:
barebox:/ addpart /dev/spiflash 128K@0(u-boot) barebox:/ addpart /dev/spiflash 64K@128K(u-boot-env) barebox:/ addpart /dev/spiflash 16128K@192K(open-wrt) barebox:/ addpart /dev/spiflash 64K@16320K(art)
Хотя нас интересует только раздел с u-boot, из чистого любопытства проверим, что находится в первых 64 байтах каждого раздела:
barebox:/ md -s /dev/spiflash.u-boot 0+64 00000000: 100000ff 00000000 100000fd 00000000 ................ 00000010: 1000018e 00000000 1000018c 00000000 ................ 00000020: 1000018a 00000000 10000188 00000000 ................ 00000030: 10000186 00000000 10000184 00000000 ................ barebox:/ md -s /dev/spiflash.u-boot-env 0+64 00000000: 071043c4 626f6f74 636d643d 626f6f74 ..C.bootcmd=boot 00000010: 6d203078 39463033 30303030 00626f6f m 0x9F030000.boo 00000020: 7464656c 61793d31 00626175 64726174 tdelay=1.baudrat 00000030: 653d3131 35323030 00697061 6464723d e=115200.ipaddr= barebox:/ md -s /dev/spiflash.open-wrt 0+64 00000000: 27051956 acb82611 551bcb1f 001503ef '..V....U....... 00000010: 80060000 80060000 bc6b30a9 05050203 .........k0..... 00000020: 4d495053 204f7065 6e577274 204c696e MIPS OpenWrt Lin 00000030: 75782d33 2e31302e 34390000 00000000 ux-3.10.49...... barebox:/ md -s /dev/spiflash.art 0+64 00000000: 807b8591 2010ffff ffffffff ffffffff .{.. ........... 00000010: ffffffff ffffffff ffffffff ffffffff ................ 00000020: ffffffff ffffffff ffffffff ffffffff ................ 00000030: ffffffff ffffffff ffffffff ffffffff ................
Эта маленькая проверка показывает, что ошибки в определении смещений разделов нет − в spiflash.u-boot сидит начало u-boot_mod, в spiflash.u-boot-env действительно находятся переменные u-boot_mod, а в разделе spiflash.open-wrt заголовок образа Linux OpenWrt.
В разделе art хранится базовый MAC-адрес платы, который используется для назначения MAC-адресов всем трём сетевым интерфейсам (одному WiFi и двум Ethernet).
Вот, к примеру, что сообщает linux про MAC-адреса этой же платы
root@BlackSwift:/dev# ifconfig -a | grep HWaddr br-lan Link encap:Ethernet HWaddr 80:7B:85:91:20:12 eth0 Link encap:Ethernet HWaddr 80:7B:85:91:20:11 eth1 Link encap:Ethernet HWaddr 80:7B:85:91:20:12 wlan0 Link encap:Ethernet HWaddr 80:7B:85:91:20:10
Теперь зачитаем образ u-boot_mod из ПЗУ в ОЗУ:
barebox:/ memcpy -s /dev/spiflash.u-boot 0 0xa0100000
В barebox эта операция чтения bit-bang'ом занимает 49 секунд.
А как с чтением из ПЗУ в linux?
в linux основной прошивки с доступом к флешу всё гораздо лучше (производительность чтения более 2 МБ/с):
root@BlackSwift:/dev# time dd if=mtdblock2 of=/tmp/test bs=1M 15+1 records in 15+1 records out real 0m 7.07s user 0m 0.00s sys 0m 0.48s
Модифицируем образ u-boot_mod в ОЗУ и запишем его назад в ПЗУ.
barebox:/ mw 0xa0100000 0x1000ffff barebox:/ erase /dev/spiflash.u-boot barebox:/ memcpy -d /dev/spiflash.u-boot 0xa0100000 0 0x20000
Обновлённое содержимое ПЗУ:
barebox:/ md -s /dev/spiflash.u-boot 0+64 00000000: 1000ffff 00000000 100000fd 00000000 ................ 00000010: 1000018e 00000000 1000018c 00000000 ................ 00000020: 1000018a 00000000 10000188 00000000 ................ 00000030: 10000186 00000000 10000184 00000000 ................
Внимание, проверьте внимательно содержимое
/dev/spiflash.u-boot
ещё раз пока не поздно!Теперь Black Swift можно перезапустить. Из barebox перезапуск можно осуществить при помощи команды reset:
barebox:/ reset
Теперь плата не может работать самостоятельно, зато появляется возможность использовать EJTAG. При необходимости, использую EJTAG, плату легко привести в исходное состояние.
Использование EJTAG
К сожалению, тупо воспользоваться скриптами openocd для MR3020 для Black Swift не получится: MR3020 использует микросхему оперативной памяти DDR1 в то время как Black Swift использует микросхему DDR2, а значит используется другая процедура инициализации контроллера ОЗУ.
Отмечу проблемы публикации EJTAG: аттракцион для хакеров в части openocd:
- по мнению Павла Ферцера (а уж он-то знает толк в openocd!), публикация пропагандировала порочную практику работы с openocd;
- была продемонстрирована уж слишком низкая производительность передачи по EJTAG (аж целых 2 КБ/с!).
В этот раз я постараюсь избежать старых ошибок. В составе openocd уже имеется конфигурационный файл для TIAO USB Multi-Protocol Adapter (TUMPA), который можно использовать для FT2232 Board, поэтому вместо полного описания просто импортируем конфигурационный файл для TUMPA (
interface/ftdi/tumpa.cfg
).Для описания подключения JTAG к микросхеме AR9331 Алексеем Ремпелем уже создан конфигурационный файл
atheros_ar9331.cfg
, так что его также можно импортировать. К сожалению, этот файл добавлен в репозиторий openocd совсем недавно (см. вот этот commit) и не входит в состав пакета openocd для Debian.Придётся скачать его отдельно:
$ wget -O atheros_ar9331.cfg "http://openocd.zylin.com/gitweb?p=openocd.git;a=blob_plain;f=tcl/target/atheros_ar9331.cfg;hb=7e66b02ba4f1453ab1c45eaebbeee6eaa0cfb436"
В соответствии с традицией openocd в сотдельном файле с именем
black-swift.cfg
сохраним описание платы. Это описание использует atheros_ar9331.cfg
и содержит процедуру инициализации DDR2 (файл создан по мотивам tcl/board/tp-link_tl-mr3020.cfg):source [find atheros_ar9331.cfg] # ar9331_25mhz_pll_init is imported from tcl/board/tp-link_tl-mr3020.cfg proc ar9331_25mhz_pll_init {} { mww 0xb8050008 0x00018004 ;# bypass PLL; AHB_POST_DIV - ratio 4 mww 0xb8050004 0x00000352 ;# 34000(ns)/40ns(25MHz) = 0x352 (850) mww 0xb8050000 0x40818000 ;# Power down control for CPU PLL ;# OUTDIV | REFDIV | DIV_INT mww 0xb8050010 0x001003e8 ;# CPU PLL Dither FRAC Register ;# (disabled?) mww 0xb8050000 0x00818000 ;# Power on | OUTDIV | REFDIV | DIV_INT mww 0xb8050008 0x00008000 ;# remove bypass; ;# AHB_POST_DIV - ratio 2 } proc ar9331_ddr2_init {} { mww 0xb8000000 0x7fbc8cd0 ;# DDR_CONFIG - lots of DRAM confs mww 0xb8000004 0x9dd0e6a8 ;# DDR_CONFIG2 - more DRAM confs mww 0xb800008c 0x00000a59 mww 0xb8000010 0x00000008 mww 0xb8000090 0x00000000 mww 0xb8000010 0x00000010 mww 0xb8000094 0x00000000 mww 0xb8000010 0x00000020 mww 0xb800000c 0x00000000 mww 0xb8000010 0x00000002 mww 0xb8000008 0x00000100 mww 0xb8000010 0x00000001 mww 0xb8000010 0x00000008 mww 0xb8000010 0x00000004 mww 0xb8000010 0x00000004 mww 0xb8000008 0x00000a33 mww 0xb8000010 0x00000001 mww 0xb800000c 0x00000382 mww 0xb8000010 0x00000002 mww 0xb800000c 0x00000402 mww 0xb8000010 0x00000002 mww 0xb8000014 0x00004186 mww 0xb800001c 0x00000008 mww 0xb8000020 0x00000009 mww 0xb8000018 0x000000ff } $TARGETNAME configure -event reset-init { ar9331_25mhz_pll_init sleep 1 ar9331_ddr2_init } set ram_boot_address 0xa0000000 $TARGETNAME configure -work-area-phys 0xa1ffe000 -work-area-size 0x1000
У читателя может возникнуть вопрос ...
как была получена последовательность записей в регистры, обеспечивающая инициализацию контроллера памяти DDR2? Такая последовательность получается при помощи EJTAG! Достаточно протрассировать исполнение u-boot_mod: инициализация контроллера памяти производится на самом раннем этапе, выбрав из трассы инструкции записи (store), получим искомую последовательность.
Конфигурационный файл
run-barebox.cfg
, который собирает всё воедино и обеспечивает загрузку и запуск barebox.bin
выглядит так:source [find interface/ftdi/tumpa.cfg] adapter_khz 6000 source [find black-swift.cfg] init halt reset init load_image barebox/barebox.bin 0xa0100000 bin # General Purpose I/O Function (GPIO_FUNCTION_1) # # SPI_EN (18) enables SPI SPA Interface signals # in GPIO_2, GPIO_3, GPIO_4 and GPIO_5. # RES (15) reserved. This bit must be written with 1. # UART_EN (2) enables UART I/O on GPIO_9 (SIN) and GPIO_10 (SOUT). # mww 0xb8040028 0x48002 resume 0xa0100000 shutdown
Итак, после того, как у нас в рабочем каталоге появились файлы
atheros_ar9331.cfg
, black-swift.cfg
, run-barebox.cfg
мы можем загрузить и запустить barebox:$ sudo openocd -f run-barebox.cfg Open On-Chip Debugger 0.8.0 (2014-10-20-21:48) Licensed under GNU GPL v2 For bug reports, read http://openocd.sourceforge.net/doc/doxygen/bugs.html Info : only one transport option; autoselect 'jtag' none separate adapter speed: 6000 kHz Error: no device found Error: unable to open ftdi device with vid 0403, pid 8a98, description '*' and serial '*' Info : clock speed 6000 kHz Info : JTAG tap: ar9331.cpu tap/device found: 0x00000001 (mfg: 0x000, part: 0x0000, ver: 0x0) target state: halted target halted in MIPS32 mode due to debug-request, pc: 0xa080078c 189216 bytes written at address 0xa0100000 downloaded 189216 bytes in 0.459115s (402.473 KiB/s) shutdown command invoked
После окончания работы openocd в окне minicom мы можем наблюдать сообщения о старте barebox.
Замечание: в моём частном случае поднять частоту тактового сигнала JTAG выше 6 МГц не представляется возможным, так как при этом наблюдается порча передаваемых данных.
Как вернуться к оригинальной прошивке?
Для одноразового запуска оригинальной прошивки годится вот такой скрипт
run-u-boot_mod.cfg
:source [find interface/ftdi/tumpa.cfg] adapter_khz 6000 source [find black-swift.cfg] init halt resume 0xbfc00400 shutdown
Замечание: скриптrun-u-boot_mod.cfg
не сработает непосредственно после подачи питания на плату, так как u-boot_mod для борьбы с таинственными ошибками AR9331 производит дополнительный сброс. Для того, чтобы скрипт сработал, перед его запуском на плату надо подать сигнал сброс. Другой вариант: запустить скриптrun-u-boot_mod.cfg
дважды.
Для перманентного восстановления оригинальной прошивки достаточно выполнить описанную выше процедуру по модификации первой инструкции ветвления загрузчика u-boot_mod с той лишь разницей, что вместо замены
0x100000ff
на 0x1000ffff
, надо заменить 0x1000ffff
обратно на 0x100000ff
.Итоги
Как было продемонстрировано выше, работать по EJTAG с Black Swift вполне возможно. Пропускная способность в 400 КБ/с позволяет комфортно грузить не только barebox, но и ядро linux; это тем более актуально, что при использовании штатного простенького адаптера интерфейс Ethernet остаётся не выведенным.
Благодарности
Автор выражает благодарность людям, без участия которых данная публикация едва ли была бы возможна:
- Павлу Ферцеру за ценные замечания и предложения;
- Алексею Ремпелю за добавление поддержки платы TP-Link MR3020 в openocd;
- Дмитрию Жеребкову за предоставленную плату Black Swift Pro.