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 AHCI контроллер
80h 00h Устройство массовой памяти другого типа


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

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

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

Что такое IDE, думаю, и так понятно — обыкновенный контроллер. RAID, собственно, то же самое, только с возможностью создать RAID-массив. AHCI — это контроллер 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 Режим первичного канала




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

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


PCI CFG

Смещение

Обозначение Наименование
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.

Как уже было упомянуто, для Slave EXECUTE 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. http://suif.stanford.edu/~csapuntz/specs/idems100.ps зеркало [1]

2. http://suif.stanford.edu/~csapuntz/specs/pciide.ps зеркало.[2]

3. http://www.t13.org/Documents/UploadedDocuments/technical/e00149r0.pdf зеркало [3]

Зеркала

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


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

Обозначение стандарат Наименование стандарта Официальная ссылка Зеркало
ATA-1 Information technology -
AT Attachment Interface for Disk Drives
d0791r4c-ATA-1.pdf d0791r4c-ATA-1.pdf
ATA-2 Information Technology -
AT Attachment Interface with Extensions (ATA-2)
d0948r4c-ATA-2.pdf d0948r4c-ATA-2.pdf
ATA-3 Information Technology -
AT Attachment-3 Interface(ATA-3)
d2008r7b-ATA-3.pdf d2008r7b-ATA-3.pdf
ATA/ATAPI-4 Information Technology -
AT Attachment
with Packet Interface Extension
(ATA/ATAPI-4)
d1153r18-ATA-ATAPI-4.pdf d1153r18-ATA-ATAPI-4.pdf
ATA/ATAPI-5 Information Technology -
AT Attachment
with Packet Interface - 5
(ATA/ATAPI-5)
d1321r3-ATA-ATAPI-5.pdf d1321r3-ATA-ATAPI-5.pdf
ATA/ATAPI-6 Information Technology -

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

d1410r3b-ATA-ATAPI-6.pdf d1410r3b-ATA-ATAPI-6.pdf
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)

D1532v1r4b-AT_Attachment_with_Packet_Interface_-_7_Volume_1.pdf D1532v1r4b-AT_Attachment_with_Packet_Interface_-_7_Volume_1.pdf
ATA/ATAPI-7 глава 2  Information Technology -

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

D1532v2r4b-AT_Attachment_with_Packet_Interface_-_7_Volume_2.pdf D1532v2r4b-AT_Attachment_with_Packet_Interface_-_7_Volume_2.pdf
 ATA/ATAPI-7 глава 3  Information Technology -

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

D1532v3r4b-AT_Attachment_with_Packet_Interface_-_7_Volume_3.pdf D1532v3r4b-AT_Attachment_with_Packet_Interface_-_7_Volume_3.pdf
ATA/ATAPI-8 ACS-1  Information technology -

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

D1699r4a-ATA8-ACS.pdf D1699r4a-ATA8-ACS.pdf
ATA/ATAPI-8 ACS-2 
ATA/ATAPI-8 ACS-3 

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

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

Advertisement