Пожалуй, одной основной из задач разработчика ОС является работа с жесткими дисками и файловыми системами. В этой статье расмотрим принципы работы с контроллерами 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
Как уже было упомянуто, для 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.
Теперь понятно, как сделать вызов команды, не использующей передачу блока данных:
- Выбрать устройство установкой бита Dev (0-Master 1-Slave).
- Проверить DRDY=1, заодно и BSY=0.
- Заполнить регистры с данными для команды.
- Послать код команды, тем самым начнется передача.
- Дождаться, пока команда не будет выполнена. BSY=0 DRDY=0.
Для команд, работающих с передачей блока данных:
- Выбрать устройство установкой бита Dev (0-Master 1-Slave).
- Проверить DRDY=1, заодно и BSY=0.
- Заполнить регистры с данными для команды.
- Послать код команды, после чего начнется передача.
- Дождаться, пока команда не будет выполнена. BSY=0 DRDY=0.
- Если команда использует передачу данных, то дождаться DRQ=1.
- Прочитать блок данных 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.
Связанные материалы
- ↑ Programming Interface for Bus Master IDE Controller
- ↑ PCI IDE Controller Specification Revision 1.0 почти тоже самое что в предыдущем.
- ↑ 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 |
||
ATA/ATAPI-7 глава 1 | Information Technology - AT Attachment with Packet Interface - 7 |
||
ATA/ATAPI-7 глава 2 | Information Technology - AT Attachment with Packet Interface - 7 |
||
ATA/ATAPI-7 глава 3 | Information Technology - AT Attachment with Packet Interface - 7 |
||
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)