Введение


Я занимаюсь разработкой приложений и драйверов для различных устройств авиационного применения. В Авиации используются ОС с более жесткими требованиями к надежности(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)


  1. Frantony
    27.03.2016 16:21

    Откройте секрет, что это за такая ОС РВ для ответственных применений с поддержкой ARINC 653, что в 2016 году она не имеет поддержки SATA контроллера на PCIe?


    1. krotos139
      27.03.2016 18:30

      Основными покупателями данной ОС являются производители авионики, и для них важна функция изоляции приложений согласно стандарту ARINC 653. Ядро не содержит драйверов, они все вынесены в BSP. Так как при разработке есть требования тестировать весь функционал — производитель поставляет ОС с минимум функций и драйверов. В этой ОС много того, что кажется стандартным функционалом — отсутствует. А архитектура отличная от Linux и ограничения лицензий не позволяют перетаскивать драйвера из Linux.


      1. Frantony
        27.03.2016 21:02
        -1

        Это как раз всё понятно. Как ОС-то называется?
        Из публикации я понял, что до использования AHCI использовался медленный программный интерфейс ATA. Драйвер для контроллера диска в ATA-режиме вы тоже сами писали, или он был позаимствован из какого-то BSP (AKA ППМ — пакет поддержки модуля)?


        1. krotos139
          28.03.2016 02:04
          +1

          ОС называется WindRiver VxWorks653. Драйвера для ATA там тоже не было — но с ним все было проще — мы его взяли из VxWorks (не 653).


  1. fireptyca
    27.03.2016 18:33

    Ох уж эти русскоязычные термины…
    Списки команд хранятся в ОЗУ вычислителя.
    Вычислитель это процессор или мой русский недостаточно хорош? Потому как у меня есть подозрения в сторону контролера SATA со своей ОЗУ.


    1. krotos139
      27.03.2016 18:37
      +1

      Нет — вычислитель — это не процессор. Вычислитель — это скорее компьютер. Но компьютер обычно ассоциируется в бытовым ПЭВМ — а никак авиационным бортовым компьютеров — Я использовал термин вычислитель. Это много где встречается.
      Я видел схемы — и точно могу сказать что к контроллеру SATA не подключено чипов ОЗУ. SATA контроллер через шину PCIe подключен процессору, как показано на первом рисунке.


      1. dm128
        28.03.2016 01:57

        Термин из Авионики, все правильно. Это обособленный изолированный блок, отвечающий за определенную функцию.


  1. victor_sverdlin
    27.03.2016 18:41

    Юрий, при заполнении заголовка не должно быть 0x20 (32 байта)?
    opts = ( 20 >> 2) | (sg_count


    1. krotos139
      27.03.2016 18:45

      Нет, вроде все правильно. У меня размер FIS 20 в десятеричной системе исчисления. Требование по выравниванию размера в стандарте нет. Выравниваются только адреса начала буферов.
      Так как в CFL записывается длина в двойных словах, поэтому делим на 4.


  1. electronus
    28.03.2016 18:30

    Эх. Помню пытался включить AHCI на ICH9M-ME из grub.
    setpci -d 8086:2828 90.b=40
    Работало для linux прекрасно, ибо линукс делал остальную инициализацию, описанную в статье.
    Win падала в синий экран, как при подсовывании AHCI драйвера в готовую систему, или при подаче флопика по F6 при установке
    Тогда знания матчасти мне так и не хватило довести дело до конца