Чтение с IDE / SATA жестких дисков

Тема в разделе "WASM.NT.KERNEL", создана пользователем Forever, 7 ноя 2009.

  1. Forever

    Forever Виталий

    Публикаций:
    0
    Регистрация:
    12 апр 2008
    Сообщения:
    244
    Может кто-либо поделится информацией по поводу реализации взаимодействия с IDE / SATA жесткими дисками? А имеено: как получить список подключенных устройств, как прочитать данные с диска и т.д.
     
  2. Treant

    Treant Member

    Публикаций:
    0
    Регистрация:
    24 май 2009
    Сообщения:
    248
  3. Forever

    Forever Виталий

    Публикаций:
    0
    Регистрация:
    12 апр 2008
    Сообщения:
    244
    Treant
    Спасибо, не видел. Есть с чего начать.
     
  4. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    // не будем плодить темы ... продолжим мысль ...

    Заинтересовала недавно меня работа с устройствами через порты в/в.
    Сканирование устройств на шине PCI освоил.
    Освоил работу с SMBus (правда там конкретика по железу).
    Решил и работу с IDE (SATA) устройствами освоить.

    Полазил по wasm'у и вышел на статейку http://ru.osdev.wikia.com/wiki/Работа_с_жесткими_дисками_и_их_контроллерами
    Там же есть исходник на асме http://ru.osdev.wikia.com/wiki/Исходный_код:Поиск_HDD_и_CD
    Ну решил попробовать данный код в деле. Писал код дома (там стоит комп с мамкой SIS 6xx) и всё работало (за искл. одного нюанса, см. ниже).

    Попробовал данный код на чипе VIA VT8235 и понял что код не совсем и универсальный. Пришёл к такому выводу, т.к.

    команда EXECUTE DEVICE DIAGNOSTIC не работает на южнике VT8235, на котором висит сигейтовский хард (PATA).

    Вот сам код процедуры идентификации устройства dev на канале С:
    Код (Text):
    1. // buf = 512 bytes;   Result = IDE_SIGNATURE_XXXXXX
    2. function IDETestDevice(ide: PIDEBusInfo; dev: Byte; buf: PChar; ScanAsDISK: Boolean): DWORD;
    3. type
    4.   TIDEAddr = packed record
    5.     case Byte of
    6.       1: (Arr: array [0..3] of Byte);
    7.       2: (DW: DWORD);
    8.   end;
    9. var
    10.   i, a: DWORD;
    11.   b, e, cmd, dv, de: Byte;
    12.   pcmd, pcnt, w: Word;
    13.   ia: TIDEAddr;
    14.   res: DWORD;
    15.   bufw: array [0..255] of Word;
    16.   ict: TIDEControllerType;
    17. begin
    18.   Result := 0;
    19.   res := 0;
    20.   pcmd := ide^.PortCMD;   // порт команд
    21.   if pcmd = 0 then Exit;
    22.   pcnt := ide^.PortCNT;   // а он и не нужет тут
    23.   ict := ide^.Ctrl;       // контроллер IDE, RAID, ACHI
    24.   if ict = ictEmpty then Exit;
    25.   if dev > 1 then Exit;   // Master or Slave
    26.   de := dev shl 4;
    27.   dv := $A0 + de;         // CHS адрессация
    28.  
    29.   if ScanAsDISK then res := IDE_SIGNATURE_DISK;  // принудительно выставляем тип девайса 00000101h
    30.  
    31.   if not ScanAsDISK then begin    // "точное" определение через WIN_DIAGNOSE, либо WIN_DEVICE_RESET
    32.     PortOutB(Driver, pcmd + HD_CURRENT, dv);   // +6
    33.     PortOutB(Driver, pcmd + HD_NSECTOR, dv);   // +2
    34.     PortOutB(Driver, pcmd + HD_SECTOR,  dv);   // +3
    35.     PortOutB(Driver, pcmd + HD_LCYL,    dv);   // +4
    36.     PortOutB(Driver, pcmd + HD_HCYL,    dv);   // +5
    37.     PortOutB(Driver, pcmd + HD_COMMAND, WIN_DIAGNOSE);    // +7   // EXECUTE DEVICE DIAGNOSTIC
    38.     for i:=$FFFF downto 0 do begin
    39.       NEWIODELAY;   // PortOutB(Driver, $00EB, 0);
    40.       NEWIODELAY;
    41.       NEWIODELAY;
    42.       e := PortInB(Driver, pcmd + HD_STATUS);     // +7
    43.       if (e and IDE_BUSY_STAT = 0) then begin
    44.         b := PortInB(Driver, pcmd + HD_ERROR);    // +1
    45. {log}   Form1.Memo1.Lines.Add('ST = '+IntToHex(e, 2)+'  ER = '+IntToHex(b, 2));
    46.         if ((dev = 0) and (b = 1)) or
    47.            ((dev = 1) and ((b = 1) or (b = $80))) then begin
    48.           ia.Arr[3] := PortInB(Driver, pcmd + HD_HCYL);      // +5
    49.           ia.Arr[2] := PortInB(Driver, pcmd + HD_LCYL);      // +4
    50.           ia.Arr[1] := PortInB(Driver, pcmd + HD_SECTOR);    // +3
    51.           ia.Arr[0] := PortInB(Driver, pcmd + HD_NSECTOR);   // +2
    52. {log}     Form1.Memo1.Lines.Add('DevID = '+IntToHex(ia.DW, 8));
    53.           if ia.DW = IDE_SIGNATURE_DISK  then res := IDE_SIGNATURE_DISK;   // $00000101
    54.           if ia.DW = IDE_SIGNATURE_ATAPI then res := IDE_SIGNATURE_ATAPI;  // $EB140101
    55.         end;
    56.         Break;
    57.       end;
    58.     end;
    59.     NEWIODELAY;
    60.     if res = 0 then begin   // тут мы удостоверимся в отстствии/присутствии ATAPI устройства
    61.       PortOutB(Driver, pcmd + HD_CURRENT, dv);   // +6
    62.       PortOutB(Driver, pcmd + HD_HCYL, 0);
    63.       PortOutB(Driver, pcmd + HD_LCYL, 0);
    64.       PortOutB(Driver, pcmd + HD_SECTOR, 0);
    65.       PortOutB(Driver, pcmd + HD_NSECTOR, 0);
    66.       PortOutB(Driver, pcmd + HD_COMMAND, WIN_DEVICE_RESET);  // +7  // DEVICE RESET = $08
    67.       for i:=$FFFF downto 0 do begin
    68.         NEWIODELAY;
    69.         b := PortInB(Driver, pcmd + HD_STATUS);    // +7
    70.         if (b and IDE_BUSY_STAT) = 0 then Break;
    71.         if i = 0 then Exit;    // Timeout
    72.       end;
    73.       b := PortInB(Driver, pcmd + HD_ERROR);       // +1
    74.       if ((dev = 0) and (b <> 1)) or
    75.          ((dev = 1) and (b <> 1) and (b <> $80)) then begin
    76.         PortOutB(Driver, pcmd + HD_CURRENT, dv);   // +6
    77.         Exit;
    78.       end;
    79.       ia.Arr[3] := PortInB(Driver, pcmd + HD_HCYL);     // +5
    80.       ia.Arr[2] := PortInB(Driver, pcmd + HD_LCYL);     // +4
    81.       ia.Arr[1] := PortInB(Driver, pcmd + HD_SECTOR);   // +3
    82.       ia.Arr[0] := PortInB(Driver, pcmd + HD_NSECTOR);  // +2
    83.       if ia.DW = IDE_SIGNATURE_ATAPI then res := IDE_SIGNATURE_ATAPI;
    84.     end;
    85.   end;  // end of DIAG device
    86.  
    87.   PortOutB(Driver, pcmd + HD_CURRENT, dv);    // +6
    88.   if res = 0 then Exit;    // сваливаем коли не поняли с кем пытаемся работать
    89.  
    90.   if (res = IDE_SIGNATURE_DISK) or (res = IDE_SIGNATURE_ATAPI) then begin
    91.     if res = IDE_SIGNATURE_DISK  then cmd := WIN_IDENTIFY else      // $EC
    92.     if res = IDE_SIGNATURE_ATAPI then cmd := WIN_PIDENTIFY;         // $A1
    93.     PortOutB(Driver, pcmd + HD_COMMAND, cmd);    // +7
    94.     for i:=$FFFF downto 0 do begin
    95.       NEWIODELAY;
    96.       b := PortInB(Driver, pcmd + HD_STATUS);    // +7
    97.       if (b and IDE_BUSY_STAT = 0) and (b and IDE_DRQ_STAT <> 0) then Break;
    98.       if ScanAsDISK and (b = $50) then Exit;     // явно это не HDD  // IDE_SEEK_STAT + IDE_READY_STAT
    99.       if i = 0 then begin
    100. {log}   Form1.Memo1.Lines.Add('IDENT ST = '+IntToHex(b, 2));
    101.         Exit;    // Timeout
    102.       end;
    103.     end;
    104.     for i:=0 to 255 do begin   // читаем IDENTIFY Info (нулевой сектор)
    105.       w := PortInW2(Driver, pcmd + HD_DATA);     // +0
    106.       bufw[i] := w;
    107.       if buf <> nil then PWord(@buf[i*2])^ := w;
    108.     end;
    109.     for i:=0 to 254 do begin
    110.       if bufw[i] <> bufw[i+1] then Break;
    111.       if i = 254 then Exit;    // в том случае, когда все слова в буфере одинаковые
    112.     end;
    113.   end;
    114.  
    115.   Result := res;
    116. end;
    Как можно догадаться, работа с портами осуществляется из ring3 через драйвер.

    Вот результат работы на 3 разным компах:

    1) SIS 6xx, только IDE контроллер, HDD seagate (Pri Master), CDROM NEC (Sec Slave)
    Команда WIN_DIAGNOSE нормально отрабатывает. Правда в 1 случае из 10 на Primary канале обнаруживается Slave HDD, которого на самом деле нет (даже WIN_IDENTIFY выполняется, правда мусор возвращается).

    2) VIA KT400A (VT8235), только IDE контроллер, HDD seagate (Pri Slave), CDROM NEC (Sec Slave)
    Для Primary канала (для обоих устройств):
    После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0, но при этом в регистре ошибок ER = 10h (ST = 10h).
    Для Secondary канала (для обоих устройств):
    После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0, но при этом в регистре ошибок ER = 7Fh (ST = 7Fh).
    CDROM находится только через DEVICE RESET.
    Если аргумент ScanAsDISK = True (алго несколько упрощённый становится), то после вызова WIN_IDENTIFY получаю данные с HDD.

    3) nForce 570 (MCP55P), IDE + SATA, три HDD висят на SATA контроллерах, CDROM NEC (IDE Pri Slave)
    Для SATA контроллеров (эмуляция IDE):
    Команда WIN_DIAGNOSE нормально отрабатывает для трёх HDD. И WIN_IDENTIFY корректно возвращает информацию.
    Для Primary канала IDE:
    Master: После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0, но при этом в регистре ошибок ER = 7Fh (ST = 7Fh).
    Slave : После датия комады WIN_DIAGNOSE ждём IDE_BUSY_STAT=0. Читаем ER=01h. Но идентификатор устройства получаем 7F7F7F7F (за место EB140101).
    Для Secondary канала IDE (для обоих устройств):
    Комады WIN_DIAGNOSE отрабатывает корректно (а Secondary то наверное и вовсе нету).

    Поэтому возникли вопросы:
    1) Может все проблеммы из-за того что пытаюсь работать с портами из ring3 , без синхронизации работы с драйверами Windows (XP SP2) ?
    И какие могут быть проблеммы при "такой" работе с портами?
    2) Почему такое "разное поведение" команды EXECUTE DEVICE DIAGNOSTIC ?
    3) Можно ли вызывать WIN_IDENTIFY для абсолютно любого устройства, обрабатывая после ошибки? И чем череват её вызов для ATAPI устройств?
     
  5. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Можно. Сошлюсь на коллег с osdev.org они только так и делают. Не вызывают EXECUTE DEVICE DIAGNOSTIC, а вызывают сразу IDENTIFY DEVICE и IDENTIFY PACKET DEVICE.
    ATAPI устройства в ответ на IDENTIFY DEVICE выставит ошибку.
    Проблема будет если у тебя на одном шлейфе и жесткий и сидиром.
    Так как команды IDENTIFY DEVICE и IDENTIFY PACKET DEVICE по особому реагируют на бит мастер\слейв.
    То будет такое жесткий вернет данные, а сидюк выставит в тоже время вернет код ошибки.

    1) Этого можно не бояться. Периодически ОС читает на диски пишет. Поэтому срабатывают прерывания жестких дисков чревато тем что твоя команда выполниться с ошибкой. ОС это не затрагивает пока ты в прерывания не вмешиваешься. В большинстве случаев ошибок не будет.

    2) Это риторический вопрос на него нет ответа. Есть около 8 версий спецификации. Плюс диски работают по разному. Во общем. У меня тоже были подобные результаты. Так что можно еще код линукса по изучать его интеловци писали, а они вроде качественно пишут. Хотя у них тоже видать не все касяки решины.
     
  6. Forever

    Forever Виталий

    Публикаций:
    0
    Регистрация:
    12 апр 2008
    Сообщения:
    244
    Продолжаем тему...

    Интересует следующий вопрос. Каким образом можно получше организовать работу с жестким диском в услових одновременной работы с ОС. Написал код, который может находить устройства, читать с них и т.д. Но есть проблема. Если во время работы моей программы жесткий диск сильно нагружен, то чтение может и удастся, так как устройство будет занято... Точно так же иногда идентификация завершается с ошибкой, так как диск не отозвался в течении некоторого времени. К тому же, команды чтения иногда дают неверный результат (примерно 50 раз на 1000000 попыток чтения). Это связано с тем, что моя программа и ОС одновременно обращаются к диску (я записал номер сектора, ОС записала свой, я прочитал чужие данные). Данная проблема была решена поднятием IRQL на все процессорах на высокий уровень. Однако, проблема с чтением все же осталась. Если устройство занято, то чтение завершается с ошибкой по таймауту. Эту проблему можно решить, повторив попытку чтения еще раз.

    Может кто-нибудь подскажет, как получше все это организовать? Например, стоит ли повышать IRQL на время всей операции или только до того, как получим, что диск будет готов к обмену даными (по идее в этом случае его уже никто не сможет занять, пока он не освободится).

    Да, кстати, ОС - Windows.
     
  7. Rodin

    Rodin New Member

    Публикаций:
    0
    Регистрация:
    30 апр 2007
    Сообщения:
    125
    Интересный вопрос. Тож когда-то пытался читать параллельно WinXP через DMA.
    Время от времени в EventLog появлялись неприятные восклицательные знаки от atapi. Что-то типа "ошибка утройства". При этом канал переключался в PIO с невозможностью вернуть DMA из ГУЙ диспетчера устройств.
    Сделать надежную параллельную с atapi работу при сильной загрузке диска так и не смог.
     
  8. Forever

    Forever Виталий

    Публикаций:
    0
    Регистрация:
    12 апр 2008
    Сообщения:
    244
    На текущий момент меня бы и PIO устроил, главное, чтобы мы с atapi не мешали друг другу жить.