Введение
Я занимаюсь разработкой приложений и драйверов для различных устройств авиационного применения. В Авиации используются ОС с более жесткими требованиями к надежности(ARINC 653), такие как VxWorks653, PikeOS или LynkOS. Разрабатывая приложения для авионики возникла проблема медленного доступа к данным на твердотельных накопителях подключенным по интерфейсу ATA. Это происходило из-за использования медленного программного интерфейса ATA. Я решил эту проблему реализацией драйвера AHCI.
В этой статье Я хочу кратко описать работу AHCI.
Архитектура устройства с AHCI контроллером.
Описание AHCI
Advanced Host Controller Interface (AHCI) — механизм, используемый для подключения накопителей информации по протоколу Serial ATA.
Для работы с устройством в режиме AHCI следует выполнить следующие действия:
- Настроить контроллер AHCI
- Заполнить структуры управления
- Выставить флаг запуска в контроллере
- Дождаться пока контроллер через DMA прочитает из памяти структуры со списком команд и после этого вернет данные в буферы, указанный в структурах. После выполнения всех действий выставляется флаг завершения транзакции.
- Profit!
Все взаимодействие с регистрами AHCI происходит через окно PCIe BAR5. Он называется ABAR(AHCI Base Address Region).
Распределение адресного пространства ABAR.
Настройка контроллера AHCI
Спецификация протокола находится в открытом доступе по адресу: www.intel.com/content/www/us/en/io/serial-ata/ahci.html
Текущая последняя версия AHCI — 1.3.1
Сначала ищем все контроллеры массовой памяти на шине PCIe. Нас интересуют устройства имеющие class id 0x01 (mass storage device) и subclass id 0x06 (serial ATA).
Настройка контроллера заключается в следующих действиях:
- Сбросить контроллер;
- Включить AHCI
- Записать физические адреса списков команд и FIS в ABAR
- Установить необходимые флаги
- Настроить прерывания
Через регистры AHCI — ABAR можно сделать ограниченное количество действий — настроить работу AHCI, прочитать ее версию, и т.д. Доступ к конфигурации носителя данных и к самим данным через эти регистры не возможен.
Для получения доступа используются специальные списки команд. Списки команд хранятся в ОЗУ вычислителя. Физический адрес этих списков записывается в контроллер AHCI. AHCI контроллер сам обращается по DMA к ОЗУ вычислителя, считывает списки команд, выполняет их и считывает записывает блоки памяти из ОЗУ. Архитектура распределения памяти показана на рисунке ниже. пр инициализации ОС должна распределить необходимые массивы в памяти, вычислить их физический адрес и на этапе инициализации их адреса записываются в соответствующие регистры ABAR.
Распределение памяти в ABAR — HBA
Заполнение структур управления
Для каждого порта в ABAR должны быть указаны 2 структуры — список команд и FIS.
Список команд состоит из 2 частей — одного заголовка списка команд.
В заголовке указывается размер списка команд, размер FIS и ссылка на физический адрес самого списка команд.
В списке команд содержится адреса буферов, которые понадобятся при выполнении команды, указанной в FIS, и из размеры.
Структуры порта AHCI
Формирование Command header:
opts = (20 >> 2) | (sg_count << 16);
pp->cmd_slot->opts = cpu_to_le32(opts);
pp->cmd_slot->status = 0;
pp->cmd_slot->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff);
pp->cmd_slot->tbl_addr_hi = 0;
Здесь заполняется поле CFL значением размера FIS(у меня константа 20) в двойных словах. Поле PRDTL заполняется количество используемых буферов по 4Мб.
Формат заголовка списка команд
Формирование таблицы команд:
#define MAX_DATA_BYTE_COUNT (4*1024*1024)
sg_count = ((buf_len - 1) / MAX_DATA_BYTE_COUNT) + 1;
for (i = 0; i < sg_count; i++) {
ahci_sg->addr =
cpu_to_le32((uint32_t) buf + i * MAX_DATA_BYTE_COUNT);
ahci_sg->addr_hi = 0;
ahci_sg->flags_size = cpu_to_le32(0x3fffff &
(buf_len < MAX_DATA_BYTE_COUNT
? (buf_len - 1)
: (MAX_DATA_BYTE_COUNT - 1)));
ahci_sg++;
buf_len -= MAX_DATA_BYTE_COUNT;
}
return sg_count;
Функция заполняет структуру в котором указывает какие буферы будут участвовать в передачи данных. Так как размер буфера в записи не может превышать 4Мб, то функция занимается разбиением буфера на несколько, если его размер превышает 4Мб.
Формат таблицы команд
Описание FIS в документации на AHCI нет. Следует искать его в описании на SATA. В структуре FIS описывается код операции (Чтение идентификации, чтение, запись, и др.) и параметры (смещение, размер, и д.р.).
Структура FIS
При чтении идентификационной информации о накопителе формируется следующий FIS:
memset(fis, 0, 20);
fis[0] = 0x27;
fis[1] = 1 << 7;
fis[2] = ATA_CMD_IDENT;
При запросе записи блока данных формируется FIS:
memset(fis, 0, 20);
/* Construct the FIS */
fis[0] = 0x27; /* Host to device FIS. */
fis[1] = 1 << 7; /* Command FIS. */
fis[2] = ATA_CMD_WR_DMA; /* Command byte. */
/* LBA address, only support LBA28 in this driver */
fis[4] = ((unsigned char) (start))&0xff;
fis[5] = ((unsigned char) (start>>8))&0xff;
fis[6] = ((unsigned char) (start>>16))&0xff;
fis[7] = (((unsigned char) (start>>24)) & 0x0f) | 0xe0;
/* Sector Count */
fis[12] = (unsigned char) blocks & 0xff;
fis[13] = ((unsigned char) (blocks>>8))&0xff;
При запросе чтения блока данных формируется FIS:
memset(fis, 0, 20);
/* Construct the FIS */
fis[0] = 0x27; /* Host to device FIS. */
fis[1] = 1 << 7; /* Command FIS. */
fis[2] = ATA_CMD_RD_DMA; /* Command byte. */
/* LBA address, only support LBA28 in this driver */
fis[4] = ((unsigned char) (start))&0xff;
fis[5] = ((unsigned char) (start>>8))&0xff;
fis[6] = ((unsigned char) (start>>16))&0xff;
fis[7] = (((unsigned char) (start>>24)) & 0x0f) | 0xe0;
/* Sector Count */
fis[12] = (unsigned char) count & 0xff;
fis[13] = ((unsigned char) (count >> 8))&0xff;
Запуск на выполнение
После того как контроллер через ABAR настроен, все списки в памяти подготовлены, достаточно записать 0x1 в PORT_CMD_ISSUE и дождаться, пока флаг сбросится. Ждать можно в цикле(я поступил именно так) или по прерыванию.
Сразу после записи в PORT_CMD_ISSUE — контроллер через DMA будет обращаться в ОЗУ процессора и выполнять те действия, которые от него ожидали.
Так в результате выполнения операции чтения мы получим в в буферах, указанных в списке команд данные из носителя.
Заключение
До использования AHCI в проекте скорость чтения/записи была: ~100кб/с
После использования AHCI в проекте скорость чтения/записи стала: 30 / 10 Мб/с
Комментарии (10)
fireptyca
27.03.2016 18:33Ох уж эти русскоязычные термины…
Списки команд хранятся в ОЗУ вычислителя.
Вычислитель это процессор или мой русский недостаточно хорош? Потому как у меня есть подозрения в сторону контролера SATA со своей ОЗУ.krotos139
27.03.2016 18:37+1Нет — вычислитель — это не процессор. Вычислитель — это скорее компьютер. Но компьютер обычно ассоциируется в бытовым ПЭВМ — а никак авиационным бортовым компьютеров — Я использовал термин вычислитель. Это много где встречается.
Я видел схемы — и точно могу сказать что к контроллеру SATA не подключено чипов ОЗУ. SATA контроллер через шину PCIe подключен процессору, как показано на первом рисунке.dm128
28.03.2016 01:57Термин из Авионики, все правильно. Это обособленный изолированный блок, отвечающий за определенную функцию.
victor_sverdlin
27.03.2016 18:41Юрий, при заполнении заголовка не должно быть 0x20 (32 байта)?
opts = ( 20 >> 2) | (sg_countkrotos139
27.03.2016 18:45Нет, вроде все правильно. У меня размер FIS 20 в десятеричной системе исчисления. Требование по выравниванию размера в стандарте нет. Выравниваются только адреса начала буферов.
Так как в CFL записывается длина в двойных словах, поэтому делим на 4.
electronus
28.03.2016 18:30Эх. Помню пытался включить AHCI на ICH9M-ME из grub.
setpci -d 8086:2828 90.b=40
Работало для linux прекрасно, ибо линукс делал остальную инициализацию, описанную в статье.
Win падала в синий экран, как при подсовывании AHCI драйвера в готовую систему, или при подаче флопика по F6 при установке
Тогда знания матчасти мне так и не хватило довести дело до конца
Frantony
Откройте секрет, что это за такая ОС РВ для ответственных применений с поддержкой ARINC 653, что в 2016 году она не имеет поддержки SATA контроллера на PCIe?
krotos139
Основными покупателями данной ОС являются производители авионики, и для них важна функция изоляции приложений согласно стандарту ARINC 653. Ядро не содержит драйверов, они все вынесены в BSP. Так как при разработке есть требования тестировать весь функционал — производитель поставляет ОС с минимум функций и драйверов. В этой ОС много того, что кажется стандартным функционалом — отсутствует. А архитектура отличная от Linux и ограничения лицензий не позволяют перетаскивать драйвера из Linux.
Frantony
Это как раз всё понятно. Как ОС-то называется?
Из публикации я понял, что до использования AHCI использовался медленный программный интерфейс ATA. Драйвер для контроллера диска в ATA-режиме вы тоже сами писали, или он был позаимствован из какого-то BSP (AKA ППМ — пакет поддержки модуля)?
krotos139
ОС называется WindRiver VxWorks653. Драйвера для ATA там тоже не было — но с ним все было проще — мы его взяли из VxWorks (не 653).