OSDev Wiki
Advertisement

Пожалуй, одной основной из задач разработчика ОС является работа с жесткими дисками и файловыми системами. В этой статье расмотрим принципы работы с контроллерами ATA/ATAPI. Первая часть — способы поиска контроллеров дисков и проверка наличия на них устройств. Вторая часть — непосредственно чтение и запись данных на жёстких дисках с использованием всевозможных методов.

Вопросы, связанные с реализацией кэша и файловой системы, ищите в другом месте.

Вступление

С развитием компьютера рос и объём жёсткого диска. Его геометрия менялась.

Сначала хватало способа адресации CHS (Cylinder-Head-Sector — Цилиндр-Головка-Сектор). Этот способ позволял адресовать до 8ГБ. Но такая адресация была до жути неудобной, и на смену ей тут же пришёл другой способ — LBA (Linear Block Addressing — линейная адресация сектора). Изначально родился стандарт LBA28, что позволяло использовать 28-битный адрес сектора, то есть максимальный объём диска мог достигать примерно 128ГБ. Однако, в последнее время объём данных на жёстких дисках превысил эту величину, вследствие чего появился стандарт LBA48 (с 48-битной адресацией секторов).

Способ адресации CHS долго использовался BIOS в прерываниях дискового сервиса. Но после появилось и расширение, способное адресовать через LBA.

С развитием геометрии дисков возрасли и потребности в быстром доступе к дискам. Вначале было два способа доступа: PIO и DMA. Изначально контроллер располагался на шине ISA, и поэтому такой способ передачи составлял 25 МБайт/с что обусловлено частотой шины и, в свою очередь, частотой DMA контроллера. Впоследствии появилась шина PCI с возможностью каждому устройству обращаться к памяти компьютера напрямую (минуя процессор). Вследствие этого появились и PCI IDE контроллеры. Разработчики реализовали новый сверхбыстрый способ передачи данных — Ultra DMA (UDMA). PCI IDE контроллер захватывал шину памяти в режиме Bus Master, и уже винчестер начинал контролировать передачу данных. Раньше за него эту работу выполнял ISA DMA контроллер. Появилась возможность передавать данные на высоких скоростях (33/66/100 МБайт/с и выше).

С появлением PCI IDE контроллера расширился способ доступа к диску.

В дальнейшем появился SATA контроллер. Кто его звал? Все жили счастливо без него. Ну что же, примем как должное и будем программировать и его.

Поиск контроллеров и дисков на них

Как найти контроллер дисков? Собственно, как и любой другой. Надо знать, где искать. А искать будем на шине PCI.

Ищем контроллер по его базовому классу 01

+---+---+---+----------------------------------------+ 
 |BCC|SCC|PI |       Тип                              | 
 +---+---+---+----------------------------------------+ 
 |01h|00h|00h|SCSI контролер                          | 
 +   +---+---+----------------------------------------+ 
 |   |01h|xxh|IDE контролер смотри рисунок            | 
 +   +---+---+----------------------------------------+ 
 |   |02h|00h|Контролер гибких дисков                 | 
 +   +---+---+----------------------------------------+ 
 |   |03h|00h|IPI контролер                           | 
 +   +---+---+----------------------------------------+ 
 |   |04h|00h|RAID контролер                          | 
 +   +---+---+----------------------------------------+ 
 |   |05h|00h|                                        | 
 +   +---+---+----------------------------------------+ 
 |   |06h|01h|ACHI контролер                          | 
 +   +---+---+----------------------------------------+ 
 |   |80h|00h|Устройство массовой памяти другого типа | 
 +---+---+---+----------------------------------------+

Просто сканируем все устройства и проверяем их класс. Более подробно как это делается читайте в статьях про PCI.

Нас, собственно интересует три пункта:

  • 0101xx — IDE
  • 0104xx — RAID
  • 010601 — ACHI

Что такое IDE, думаю, и так понятно обыкновенный контроллер. RAID, собственно, то же самое, только с возможностью создавать RAID-массив. ACHI — это контроллер SATA-дисков с возможностями прямого доступа к портам дисков. Собственно, SATA-контроллер может иметь любой из этих классов.

SATA-контроллеры бывают с фиксированным классом IDE или, напротив, такие контроллеры, у которых можно задать класс.


Надо сказать, что Programming Interface (PI) имеет сложную структуру. Для IDE, ниже у RAID нечто наподобие.

+----+------+----------+----------------------------+ 
 |Бит | Тип  | Значение | Описание                   | 
 +----+------+----------+----------------------------+ 
 | 7  |  RO  |    1     | Bus Master                 | 
 +----+------+----------+----------------------------+ 
 |6:4 |  RO  |    0     | Зарезервировано            | 
 +----+------+----------+----------------------------+ 
 |    |      |          | Поддерживаемый тип         | 
 | 3  |  RO  |    1     | вторичного канала          | 
 |    |      |          | 1-поддерживаются оба типа  | 
 +----+------+----------+----------------------------+ 
 |    |      |          | Установленный режим        | 
 | 2  | RW/RO|   1/0    | вторичного канала          | 
 |    |      |          | расширенный/устоявшийся    | 
 +----+------+----------+----------------------------+ 
 |    |      |          | Поддерживаемый тип         | 
 | 1  |  RO  |    1     | первичного канала          | 
 |    |      |          | 1-поддерживаются оба типа  | 
 +----+------+----------+----------------------------+ 
 | 0  | RW/RO|   1/0    | Режим первичного канала    | 
 |    |      |          | расширенный/устоявшийся    | 
 +----+------+----------+----------------------------+

Найдя контроллер, мы должны получить адреса его базовых портов и каналов. Забудем про ACHI. Остановимся на IDE и RAID.

Имеем следующую табличку портов [1] [2] [3]:

+-------+---------+-------------------------------------------------+ 
 |10h-13h| PCMD_BAR| Primary Command Block Base Address              | 
 |       |         | Базовый адрес портов команд первичного канала   | 
 +-------+---------+-------------------------------------------------+ 
 |14h-17h| PCNL_BAR| Primary Control Block Base Address              | 
 |       |         | Базовый адрес порта контроля первичного канала  | 
 +-------+---------+-------------------------------------------------+ 
 |18h-1Bh| SCMD_BAR| Secondary Command Block Base Address            | 
 |       |         | Базовый адрес портов команд вторичного канала   | 
 +-------+---------+-------------------------------------------------+ 
 |1Ch-1Fh| SCNL_BAR| Secondary Control Block Base Address            | 
 |       |         | Базовый адрес порта контроля вторичного канала  | 
 +-------+---------+-------------------------------------------------+ 
 |20h-23h| BM_BASE | Bus Master Base Address                         | 
 |       |         | Базовый адрес регистров для режима захвата шины | 
 +-------+---------+-------------------------------------------------+


Надо сразу сказать, что контроллер может находиться в одном из двух состояний:

  • устаревшем — Legacy.
  • родном — Native.

Также для встроенных SATA-контроллеров есть режим совместимости (Сompatibility) когда он вместе с IDE-контроллером делит два канала в Legacy-режиме. И, при этом, в конфигурационном пространстве PCI будет виден только один контроллер.

Если контролер работает в устаревшем режиме (Legacy), то для доступа к каналам используются следующие порты:

Первичный канал: порты команд  01F0h-01F7h 
                  Порт контроля 03F4h+2
                  прерывание IRQ14
Вторичный канал: порты команд  0170h-0177h
                  порт контроля 0374h+2
                  прерывание IRQ15

У порта контроля базовым считается адрес 03F4h. А сам регистр имеет смещение 2 относительно базового. Смотри [3]

В конфигурационном пространстве PCI на месте BAR-регистров будет нулевой адрес. Что будет говорить о том что порты эти либо не используются, либо используются особые порты, как в нашем случае. При этом, BM_BASE будет выставлен.

Если контроллер находится в родном режиме (Native), то все базовые порты считываются из конфигурационного пространства PCI. И оттуда же можно извлечь номер прерывания. Адреса портов можно также изменить, но делать этого я не советую. Если номер прерывания не выставлен или вы хотите его изменить, то вам придётся воспользоваться PCI BIOS, так как это связанно с IRQ-роутингом.

Найдя контроллеры и считав базовые порты, перейдем к определению дисков.

Чтобы не усложнять жизнь, каналы будем нумеровать с нулевого и до последнего, не различая номер контроллера (по идее, так проще).

Каждый канал имеет два диска: Master и Slave. Собственно, на SATA никаких Slave дисков нет — все устройства работают параллельно. Но SATA-контроллер способен (даже вынужден) эмулировать Master/Slave.

Чтобы определить присутствие жесткого диска, нужно послать команду:

EXECUTE DEVICE DIAGNOSTIC=90h

Для определения оптического диска или другого специфичного устройства, способного выполнять пакетные команды, нужно послать ему команду:

DEVICE RESET=08h

По стандарту ATA/ATAPI После команды EXECUTE DEVICE DIAGNOSTIC, или DEVICE RESET устройством должна возвращаться сигнатура. Если подключен оптический дисковод, то должна вернуться сигнатура поддержки пакетных устройств ATAPI:

Sector Count  01h
 Sector Number 01h
 Cylinder Low  14h
 Cylinder High EBh

Для простых дисков ATA сигнатура следующая:

Sector Count  01h
 Sector Number 01h
 Cylinder Low  00h
 Cylinder High 00h

Помимо сигнатуры в регистр Error должен вернутся Diagnostic code код присутствия или отсутствия диска.

После того, как мы посылаем команду EXECUTE DEVICE DIAGNOSTIC для Master-устройства, мы должны дождаться ответа от него. Если в течение нескольких миллисекунд мы не дождались ответа, то диска нет. Признаком занятости контроллера является код BSY. Если мы дождались, то смотрим Diagnostic_code: мы должны получить Diagnostic_code=01h или 81h -диск есть. В противном случаи устройство отсутствует.

Когда мы посылаем команду EXECUTE DEVICE DIAGNOSTIC Slave-устройству, то на эту команду должен ответить Master. Если у нас есть Master, то он отвечает за Slave. Diagnostic_code=01h будет означать, что Slave есть, в противном случае его нет. Однако, для Slave это не является достоверной информацией. If Device 1 is not present, the host may see the information from Device 0 even though Device 1 is selected. Если Master отсутствует, а Slave присутствует, то Slave сам ответит за себя.

Помимо прочего, существует несогласованность:

  • ATAPI — интерфейс для пакетных устройств.
  • ATA — интерфейс для обыкновенных устройств.

Приходится также проверять и результат команды DEVICE RESET

Как уже было упомянуто, для SlaveEXECUTE DEVICE DIAGNOSTIC не возвращает достоверной информации, поэтому необходимо делать дополнительную проверку.

Вызываем команду для получения параметров жестких дисков:

IDENTIFY DEVICE=ECh

Или для пакетных:

IDENTIFY PACKET DEVICE=0A1h

Практика показала, что SATA в случае эмуляции только Master-дисков при попытке обратиться к Slave будет возвращаться данные о состоянии Master-диска. Однако, данные от команды не будут получены. Поэтому смотрим: если на запрос IDENTIFY DEVICE или IDENTIFY PACKET DEVICE вернулась ерунда или ошибка команды, или в Data регистре идут повторяющиеся слова, то считаем что диска нет.

От вторичной проверки мы получаем также и практическую пользу. Мы можем выяснить, поддерживает ли диск LBA48 и какие режимы передачи диск позволяет выполнять DMA.

Вот мой исходник (компилятор FASM). Громоздко вышло.

Файл:Library.png Исходный код
Поиск HDD и CD

Выполнение команд, примеры чтения записи

Существует несколько классов команд. Но все протоколы можно разделить на два-три типа: с приемом блока данных, с передачий блока данных и без передачи блока данных.

Основной блок регистров — это Command Block. Ниже представлены мнемонические обозночения его структуры. Есть еще старые обозночения для CHS адресации, но их я опущу. Все регистры имеют разрядность 8 бит. Исключение составляет Date, в который запись и чтение идет словами (по 2 байта, то есть, 16 бит) или двойными словами (4 байта или 32 бита), так называемый режим PIO32.

 +---+----------------+----------------+
 |ofs|         Command Block           |
 +---+----------------+----------------+
 |   |Read            |Write           |
 +---+----------------+----------------+
 |00 |Date            |Date            |
 +---+----------------+----------------+
 |01 |Error           |Features        |
 +---+----------------+----------------+
 |02 |Sector Count    |Sector Count    |
 +---+----------------+----------------+
 |03 |LBA Low         |LBA Low         |
 +---+----------------+----------------+
 |04 |LBA Mid         |LBA Mid         |
 +---+----------------+----------------+
 |05 |LBA High        |LBA High        |
 +---+----------------+----------------+
 |06 |Device          |Device          |
 +---+----------------+----------------+
 |07 |Status          |Command         |
 +---+----------------+----------------+

Также следует обратить внимание на блок контроля: в нём задействованы только регистры со смещением 2. Поэтому часто, говоря о Control Block, подразумевают Device Control, и, следовательно, сразу считают базовый адрес относительно этого регистра.

 +---+---------------------------------+
 |ofs|         Control Block           |
 +---+----------------+----------------+
 |   |Read            |Write           |
 +---+----------------+----------------+
 |02 |Alternate Status|Device Control  |
 +---+----------------+----------------+

Для начала разберемся в структуре регистра статуса.

 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Register       |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Status         |BSY  |DRDY |  DF |  #  | DRQ | obs | obs | ERR |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+

Объясним значения битов:

  • BSY (Busy) — если бит установлен в 1, то устройство занято.
  • DRDY (Device ready) — если бит установлен в 1, то устройству можно передать команду.
  • DF (Device fault) — если бит установлен в 1, то возникла критическая ошибка.
  • DRQ (Data request) — если бит установлен в 1, устройство готово к передаче данных.
  • ERR (Error) — если бит установлен в 1, то возникла ошибка, код ошибки смотрим в регистре ошибок Error.

Теперь понятно, как сделать вызов команды, не использующей передачу блока данных:

  1. Выбрать устройство установкой бита Dev (0-Master 1-Slave).
  2. Проверить DRDY=1, заодно и BSY=0.
  3. Заполнить регистры с данными для команды.
  4. Послать код команды, тем самым начнется передача.
  5. Дождаться, пока команда не будет выполнена. BSY=0 DRDY=0.

Для команд, работающих с передачей блока данных:

  1. Выбрать устройство установкой бита Dev (0-Master 1-Slave).
  2. Проверить DRDY=1, заодно и BSY=0.
  3. Заполнить регистры с данными для команды.
  4. Послать код команды, после чего начнется передача.
  5. Дождаться, пока команда не будет выполнена. BSY=0 DRDY=0.
  6. Если команда использует передачу данных, то дождаться DRQ=1.
  7. Прочитать блок данных 256 Слов (512Байт).

Резонный вопрос: как работать с LBA48, если все регистры имеют разрядность 8 бит?

При адресации через LBA48 запись адреса идет в те же регистры, но более хитрым способом. Сначала записывается старшая часть адреса и число секторов, а затем (во второй раз) в те же самые регистры записывается младшая часть.

Помимо этого, можно прочитать эти регистры, установив HOB-бит, чтобы прочитать старшую часть, или сбросив, если нужно прочитать младшую.

Все команды для LBA48 имеют суффикс EXT.

 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Register       |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Device Control | HOB |  r  |  r  |  r  |  r  | SRST| nIEN|  0  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
  • HOB — (high order Bit) служит для LBA48. Любая запись в регистр Command Block очищает этот бит.
  • r – резерв.
  • SRST — программный сброс(Reset) контролера.
  • nIEN — управляет INTRQ сигналом.

Если для адресации используется LBA48, то установка HOB=0 позволяет читать старшую часть адреса, а при HOB=1 из регистра Command Block доступна для чтения младшая часть адреса.

Посылка команды READ SECTOR(S)=20h Запрос

 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Register       |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Features       |na                                             |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Sector Count   |Sectors Count (7:0)                            |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Low        |LBA (7:0)                                      |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Mid        |LBA (15:8)                                     |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA High       |LBA (23:16)                                    |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Device         |obs  | LBA | obs | DEV |      LBA (27:24)      |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Command        | READ SECTOR(S)=20h                            |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+

Посылка команды READ SECTOR(S) EXT =24h Запрос

 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Register       |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Features|Первый|Reserved                                       |
 |Features|Второй|Reserved                                       |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Sector Count|П.|Sector Count (15:8)                            |
 |Sector Count|В.|Sector Count (7:0)                             |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Low |Первый|Native max address LBA (31:24)                 |
 |LBA Low |Второй|Native max address LBA (7:0)                   |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Mid |Первый|Native max address LBA (39:32)                 |
 |LBA Mid |Второй|Native max address LBA (15:8)                  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA High|Первый|Native max address LBA (47:40)                 |
 |LBA High|Второй|Native max address LBA (23:16)                 |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Device         |obs  | LBA | obs | DEV |      Reserved         |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Command        | READ SECTOR(S) EXT =24h                       |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+

Также важной характеристикой является максимальный адрес сектора. Код операций чтения для LBA28 и LBA48:

READ NATIVE MAX ADDRESS=0F8h
READ NATIVE MAX ADDRESS EXT=027h

В ответ на команду READ NATIVE MAX ADDRESS=0F8h

 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Register       |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Error          |na                                             |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Sector Count   |na                                             |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Low        |Native max address LBA (7:0)                   |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Mid        |Native max address LBA (15:8)                  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA High       |Native max address LBA (23:16)                 |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Device         |obs  |  na | obs | DEV |      LBA (27:24)      |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Status         |BSY  |DRDY |  DF | na  | DRQ |  na |  na | ERR |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+

После посылки команды READ NATIVE MAX ADDRESS EXT=027h

 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Register       |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Error          |na                                             |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Sector Count   |Reserved                                       |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Low |HOB=0 |Native max address LBA (7:0)                   |
 |LBA Low |HOB=1 |Native max address LBA (31:24)                 |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA Mid |HOB=0 |Native max address LBA (15:8)                  |
 |LBA Mid |HOB=1 |Native max address LBA (39:32)                 |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |LBA High|HOB=0 |Native max address LBA (23:16)                 |
 |LBA High|HOB=1 |Native max address LBA (47:40)                 |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Device         |obs  |  na | obs | DEV |      Reserved         |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
 |Status         |BSY  |DRDY |  DF | na  | DRQ |  na |  na | ERR |
 +---------------+-----+-----+-----+-----+-----+-----+-----+-----+
  • HOB — High Order Bit (бит 7) в Device Control register.

Напоминаю, что Device Control register — это порт контроля (к примеру, 03F4h+2). Читать из этого регистра нельзя, так как в нём хранятся другие данные. Бит устанавливается посылкой 80h (HOB=1) или 0h (HOB=0). Любая запись в Command Block приводит к сбросу HOB=0.

Связанные материалы

  1. Programming Interface for Bus Master IDE Controller
  2. PCI IDE Controller Specification Revision 1.0 почти тоже самое что в предыдущем.
  3. 3,0 3,1 Протоколы хост контроллера. Ошибка цитирования Неверный тег <ref>: название «ref3» определено несколько раз для различного содержимого

Стандарты ATA/ATAPI

Обозначение стандарат Наименование стандарта Ссылка Зеркало
ATA-1 Information technology -
AT Attachment Interface for Disk Drives
ATA-2 Information Technology -
AT Attachment Interface with Extensions (ATA-2)
ATA-3 Information Technology -
AT Attachment-3 Interface(ATA-3)
ATA/ATAPI-4 Information Technology -
AT Attachment
with Packet Interface Extension
(ATA/ATAPI-4)
ATA/ATAPI-5 Information Technology -
AT Attachment
with Packet Interface - 5
(ATA/ATAPI-5)
ATA/ATAPI-6 Information Technology -

AT Attachment
with Packet Interface - 6
(ATA/ATAPI-6)

ATA/ATAPI-7 глава 1 Information Technology -

AT Attachment with Packet Interface - 7
Volume 1 - Register Delivered Command Set, Logical
Register Set
(ATA/ATAPI-7 V1)

 ATA/ATAPI-7 глава 2  Information Technology -

AT Attachment with Packet Interface - 7
Volume 2 - Parallel Transport Protocols and Physical
Interconnect
(ATA/ATAPI-7 V2)

 ATA/ATAPI-7 глава 3  Information Technology -

AT Attachment with Packet Interface - 7
Volume 3 - Serial Transport Protocols and Physical
Interconnect
(ATA/ATAPI-7 V3)

 ATA/ATAPI-8 ACS-1  Information technology -

AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)

 ATA/ATAPI-8 ACS-2 
 ATA/ATAPI-8 ACS-3 

Внимание! Это незаконченная статья. Вы можете помочь доработать статью и наполнить ее материалом.

Для улучшения статьи необходимо: Перевести таблицы в вики-разметку Grindars 15:06, 15 августа 2008 (MSD)

Advertisement