И снова мы разбираемся как не писать прошивки для микроконтроллеров. Прошлая статья вызвала у людей много эмоций и, мне кажется, осталось мало кем понята и, может быть, мной плохо было объяснено зачем это все вообще затевалось.
Поэтому подготовил пример.
Хотя это всего лишь подправленный сэмпл DMA_Polling из Standard Peripherals Library.
Но в этом и преимущество такого подхода, что можно использовать все наработки из кода выполняемого на микроконтроллере, в том числе и библиотеки от производителя МК типа HAL или Standard Peripherals Library. И это должно справедливо быть для любого контроллера, который поддерживает openOCD — хоть STM32, Atmel, PIC32 и прочие по списку. При этом мы можем использовать все библиотеки хостового ПК, а так же пользоваться новыми стандартами языка С++. А если написать обертки, то и вообще можно использовать любой язык. Но я не стал сильно усложнять здесь. Просто решил показать основной функционал и возможности.
В примере, конечно, будем моргать светодиодиком. А так же отсылать данные по UART и по другому UART их принимать с помощью DMA. Использование DMA дает громадный бонус. Зачастую можно будет избавиться от прерываний, которые мы здесь не можем использовать, и поллинга, который из-за моего отладчика работает очень медленно, но тем неменее успевать захватывать данные на интерфейсах. А так же быстро генерировать. Поэтому довольно просто сделать программируемый генератор сигналов и сниффер разнообразных интерфейсов.
Оборудование на котором будем испытывать, осталось со времен первой статьи
Здесь белым проводком соединил Тх UART1 (PA9 pin) c Rx UART2 (PA3 pin).
const char * message = "AddressIntercept PinTool UART DMA example";
int main()
{
sizeMemoryTranslate_t s = 0;
memoryTranslate *p = getMemoryMap(&s);
pAddrPERIPH = p[0].start_addr;
pAddrSRAM = p[1].start_addr;
init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = GPIO_Pin_13;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &gpio);
const size_t _SIZE_MESSAGE = strlen(message);
printf("sending message ");
for (int i = 0; i < _SIZE_MESSAGE; i++) {
/* Send one byte from USARTy to USARTz */
USART_SendData(USARTy, message[i]);
GPIO_SetBits(GPIOC, GPIO_Pin_13);
/* Loop until USARTy DR register is empty */
while (USART_GetFlagStatus(USARTy, USART_FLAG_TXE) == RESET);
printf(".");
fflush(stdout);
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
}
printf("\n");
printf("qty of sent bytes %d\n", strlen(message));
const uint16_t rec = DMA_GetCurrDataCounter(USARTz_Rx_DMA_Channel);
printf("qty of received byte using DMA : %d\n", sizeDMAbuf - rec);
printf("read message from buffer DMA : ");
const uint8_t *pM = (uint8_t *)pAddrSRAM;
for (int r = 0; r < _SIZE_MESSAGE; r++) {
printf("%c", pM[r]);
fflush(stdout);
}
printf("\n");
assert(strncmp(message, (const char *)pM, _SIZE_MESSAGE) == 0);
printf("Received and sent bytes are equal!\n");
return 0;
}
То можно увидеть, что за исключением нашей ф-ции преобразования адресов и ф-ций из стандратной библиотеки, все остальное взято SPL от ST, впринципе можно было и из HAL ф-ции использовать. Но мне привычнее старый добрый SPL.
Как это все собрать и запустить
Это пример для ПК с Ubuntu 16.04 64-bit:
Для начала нужно скачать Pintool v3.7
Распаковать куда удобно, дальше можно либо переменную PIN_ROOT определить для сборки PinTool клиента либо просто расположить наш клиент в
pin-3.7-97619-g0d0c92f4f-gcc-linux/source/tools/
Я делаю вторым способом
cd pin-3.7-97619-g0d0c92f4f-gcc-linux/source/tools/
git clone git@github.com:ser-mk/AddressIntercept.git
cd AddressIntercept
Дальше потребуется собрать 32-битный клиент
make TARGET=ia32
Бинарник будет лежать здесь obj-ia32/addrIntercept.so. 32-битный требуется, потому как в ARM Сortex такой размер адреса.
Теперь можно собирать и сам пример. Его копирую прям в папку к pintool клиенту
cd pin-3.7-97619-g0d0c92f4f-gcc-linux/source/tools/AddressIntercept
Git clone https://github.com/ser-mk/addrIntercept-example-UART-DMA
Cd addrIntercept-example-UART-DMA
Make
И получаем в катологе test.elf бинарник. Дальше для простоты эксперимента, положу файл в директорию нашего Pintool клиента AddressIntercept
Перед тем что бы все запускать, нам надо бы создать именованные FIFO для общения с OpenOCD клиентом
cd pin-3.7-97619-g0d0c92f4f-gcc-linux/source/tools/AddressIntercept
mkfifo in.fifo out.fifo
in.fifo out.fifo — названия по умолчанию для наших клиентов, можно дать и другие названия, но тогда их придется указывать явно при запуске клиентов.
Запустим openOCD клиент, в моем случае ему надо передать ip openOCDсервера, это будет 192.168.0.111, порт оставил стандартный 6666, поэтому не указываю его.
Итак запускаем по порядку
cd pin-3.7-97619-g0d0c92f4f-gcc-linux/source/tools/AddressIntercept
python3.5m OCDclient.py -ip 192.168.0.111 &
../../../pin -t obj-ia32/addrIntercept.so -- addrIntercept-example-UART-DMA/test.elf
И вывод должен быть такой:
Надеюсь пример наглядный. Уже вполне получился proof of concept, который можно использовать.
Причем должно все работать в том числе на MacOS и Windows (здесь, возможно, придется подправить работу с named fifo или её заменить на то что есть в "окнах").
Дальше, в следующих статьях, если интересно, можно рассказать про REPL как на гифке из предыдущей статьи и другие способы перехвата адресов, без ограничения платформой Intel.