auf Festplattenkopf zugreifen



  • mir büdde auch:

    commander-white@in-box.net

    :D:D:D:D:D:D



  • habt ihr mein zeug bekommen??? hab noch gar keine antwort/resonanz bekommen... *g*

    cu todo



  • Mal ne DAU-Frage:
    Was ist wenn das Programm ein Fehler hat und ihr den Kopf zu weit nach unten bewegt? Dann ist die Platte doch kaputt, oder irre ich mich da?



  • Man kann den Kopf nicht selbst steuern, deshalb kann das auch nicht passieren.



  • wäre dankbar wenn du es mir auch schicken könntest.
    doyouknowit@gmx.de

    meines wissens nach optimieren einige HDD-Controller (die können wirklich sagen wo der Kopf hin soll) die Befehle in der "Warteschlange" hinsichtlich der Anordnung.
    Bei derartigen Geräten müsste diese Funktion zunächst deaktiviert werden, um dann u.U. indirekt über "normale" Befehle den Kopf zu dirigieren. Zu beachten wäre des weiteren auf jeden Fall noch der Cache, und ... (sind nur so ein paar Dinge die mir spontan einfallen).

    mfg
    -bg-



  • Also bevor du dich an die Köpfe machst musst du die register kennen also die standart register der Festplatte sind:

    DatenRegister=( 1f0h <-- 16bit breit {(R/W
    FehlerRegister=( 1f1h <-- 8bit breit {(R
    Vorkompensation=( 1f1h <-- 8bit breit {(W
    FeatureRegiste

    Sektorenzahl=( 1f2h <-- 8bit breit {(R/W
    Sektornummer=( 1f3h <-- 8bit breit {(R/W
    ZylinderLSB=( 1f4h <-- 8bit breit {(R/W
    ZylinderMSB=( 1f5h <-- 8bit breit {(R/W
    Laufwerk/Kopf=( 1f6h <-- 8bit breit{(R/W
    StatusRegister 😞 1f7h <-- 8bit breit {(R
    BefehlRegister 😞 1f7h <-- 8bit breit {(W
    StatusRegisterAlternativ=( 3f6h <-- 8bit breit {(R
    Digitales AusgabeRegister 😞 3f6h <-- 8bit breit {(W
    Laufwerkadresse =(3f7h <-- 8bit breit {( R

    Allen Festplatten haben diesen register sonst wären...

    MFG
    OS-Developer
    Embended Developer



  • @todo: Bei der Nachfrage könntest du es ja doch mal hier reinschreiben oder mir bitte auch schicken! 🙂 Danke!
    [url=mailto:drakos@bloodtide.de">drakos@bloodtide.de[/url]



  • Wie wärs wenn du es gleich hier postest?Dann hätten alle was davon,vll auch in die FAQ.



  • ok ok, ich geb mich geschlagen 😉
    viel spaß beim lesen??

    Nach dem ATA-Standard ist die kleinste adressierbare Einheit auf einer Festplatte 1 Sektor.
    Das entspricht 512 Byte. Eine Festplatte ist auch noch unterteilt in Spuren und Köpfe.
    In jeder Spur befindet sich eine bestimmte Anzahl von Sektoren. Die Kopfnummer gibt an, auf
    welcher Platte gelesen werden soll, da eig. alle Festplatten aus mehreren (magn.) Scheiben
    besteht, die jeweils oben und unten lesbar/beschreibbar sind. Wenn also eine Festplatte 8
    solcher Scheiben besitzt, hat sie 16 Köpfe. Auf jeder Seite gibt es eine bestimmte Anzahl von
    puren (in denen es wiederum wie schon gesagt eine bestimmte Anzahl von Sektoren gibt)
    Die Gesammtzahl der Sektoren auf einer Festplatte könnte man also wie folgt ausrechnen:

    n_secs = n_secs_per_track * n_tracks_per_head * n_heads_per_drive

    Um von Festplatten zu lesen unterscheidet man zwei Adressierungsarten: CHS und LBA
    CHS steht für Cylinder/Head/Sector (Cylinder ist ein anderes Wort für Spur, der Rest dürfte
    sich von selbst erklären) Bei dieser Adressierungsart gibt man beim Lesen und Schreiben
    Spur, Kopf und Sektor an, was gewöhnlich über das BIOS und dessen Interrupt 0x13 erfolgt.
    Hier nun ein kleines Beispielprogramm in Assembler zum Auslesen des Bootsektors einer
    Festplatte:

    .Model Small
    .Stack 100h
    .Data
    Buffer DB 512 Dup(0)

    .Code
    start:
    ; beim Lesen/Schreiben muss man natürlich die Adresse eines Puffers, wo die gelesenen
    ; Daten hinkopiert werden sollen, angeben. In diesem Fall haben wir einen Puffer im
    ; Datensegment, dessen Adresse in ES:BX angegeben wird.
    Mov Ax,@Data
    Mov Ds,Ax
    Mov Es,Ax
    Mov Bx,Offset Buffer

    ; Funktion 02h = Sektor(en) lesen
    Mov Ah,02h
    ; In AL muss die Anzahl der zu lesenden Sektoren stehen (hier 1)
    Mov Al,1

    ; In CH muss die Spurnummer stehen. Da 8 Bit in den meisten Fällen nicht ausreichen (das wären
    ; nur 256 Spuren), stehen in den Bits 6 und 7 von CL noch Bit 8 und 9 der Spurnummer
    Mov Cx,0 ; CX = Spurnummer (Bootsektor in Spur 0)
    ShR Cx,8 ; jetzt stehen in CL Bit 8 und 9 der Spurnummer
    ShL Cl,6 ; Bit 8 und 9 d. Spurnummer müssen aber in Bit 6 und 7 von CL stehen
    Mov Ch,0 ; CH = unt. 8 Bits der Spurnummer

    ; Bits 0...5 mit der Sektornummer "füllen" (OR muss benutzt werden, da sonst der Teil der
    ; Spurnummer in Bits 6 und 7 gelöscht/verändert werden würde)
    ; (Bootsektor befindet sich in Sektor 1 (in Spur 0 und Seite 1)
    Or Cl,1

    Mov Dh,1 ; Kopfnummer -> DH (Bootsektor ist immer auf Seite 1)
    Mov Dl,80h ; Laufwerksnummer -> DL 1. Festplatte = 80h, 2. = 81h usw.
    ; (bei Diskettenlaufwerken geht es bei 0 los)
    Int 13h ; und nun los! BIOS-Int 13h ist für die Laufwerksverwaltung da.

    ; jetzt folgt Code zum Ausgeben des Puffers - nicht weiter kommentiert
    Mov Si,Offset Buffer
    Mov Ah,0Eh
    Mov Bx,7
    Mov Cx,512

    output_loop:
    LodSB
    Int 10h
    Dec Cx
    JNZ output_loop

    Mov Ax,4C00h
    Int 21h
    End start
    End

    Diese Routine kann man ohne weiteres auch zu einer allgemeinen Read-Sector-Routine
    umprogrammieren, indem man einfach die festen Werte durch Variablen (die über den Stack
    übergeben werden) ersetzt. (den Puffer-Ausgabe-Code muss man dann natürlich auch entfernen 🙂

    Wenn man sich aber die CHS-Adressierung genauer anschaut, wird man bald feststellen, dass es ein
    Limit gibt, und zwar die Anzahl der Spuren/Zylinder. Da (nur) 10 Bits für die Spurnummer
    zur Verfügung stehen, kann man max. 1024 Spuren lesen. Die max. Größe kann dann seitens
    des BIOS nur 516 MB betragen! Und wer hat schon noch solche kleinen Platten? (außer mir 🙂
    Einige BIOS-Hersteller haben sich dann so Sachen ausgedacht, wie z.B. das noch 4 weitere Bits
    in Bits 4...7 von Dh stehen usw.
    Da das aber absolut nicht einheitlich ist, sollte man diese Methode nicht verwenden.

    Aber es gibt ja noch eine andere Adressierungsmethode, mit der man diese Hürde ohne weiteres
    überwinden kann: LBA. LBA steht für Logical Block Adressing und ist Bestandteil des
    ATA-2-Standards, welchen alle EIDE (Enhanced IDE) beherrschen. Bei LBA wird eine absolute
    (bei Null beginnende) Sektornummer (64 Bit) angegeben, die wie folgt ausgerechnet wird:

    LBA = (((cylinder * heads_per_track) + head) * sectors_per_track) + sector - 1

    Man kann natürlich auch Spur/Zylinder, Kopf und Sektor aus einem LBA-Wert ausrechnen:

    sector = (LBA mod sectors_per_track) + 1
    head = (LBA / sectors_per_track) mod heads_per_cylinder
    cylinder = (LBA / sectors_per_track) / heads_per_cylinder

    "mod" bedeutet Modulo und ist der Rest einer Division (in C/C++ der %-Operator)

    LBA ist nicht automatisch auf allen PCs/Festplatten verfügbar. Man kann aber davon ausgehen, dass
    alle PCs/Festplatten nach 1993 LBA beherrschen. Deshalb sollte man, bevor man LBA benutzt,
    erst überprüfen, ob LBA verfügbar ist. Das geht über die unten erläuterte Funktion.

    Nun möchte ich noch einmal den BIOS-Interrupt 13h ansprechen, über den ja alle
    Laufwerk-Operationen ablaufen.

    CHS-Adressierung
    ------------------

    Sektor(en) lesen

    AH = 02h (Funktionsnummer).
    AL = Anzahl der zu lesenden Sektoren
    CH = Spurnummer Bits 0-7
    CL = Spurnummer Bits 8-9 (Bits 6-7 von CL)
    CL = Sektornummer (Bits 0-5 von CL, 1-63, Sektorennummern beginnen bei 1)
    DL = Laufwerksnummer (80h-FFh für Festplatten)
    DH = Kopfnummer
    ES:BX = Segment:Offset des Puffers

    Sektor(en) schreiben

    AH = 03h (Funktionsnummer).

    Der Rest ist genauso wie beim Lesen von Sektoren. (Der Puffer enthält die zu schreibenden
    Daten 🙂

    LBA-Adressierung
    -----------------

    Install-Check der Int13-Erweiterung (LBA)

    AH = 41h
    DL = drive number (same as with READ/WRITE SECTORS).
    BX = 55AAh

    Rückgabewerte:

    BX = AA55h (Bytes getauscht) und CF=0, wenn LBA auf dem gewünschten Laufwerk verfügbar ist.

    In CX werden auch noch andere Informationen zurückgegeben. Für genauere Details würde ich einen
    Blick in Ralph Browns Interrupt-Liste empfehlen.

    Erweitertes Sektor(en)-lesen

    AH = 42h
    DL = Laufwerksnummer
    DS:SI = Zeiger auf Disk-Request-Packet-Structure

    Aufbau der Disk-Request-Packet-Structure

    BYTE Packet-Länge (10h)
    BYTE Reserviert (00h)
    WORD Anzahl zu lesender Sektoren
    WORD Puffer-Offset
    WORD Puffer-Segment
    QWORD 64-Bit Logical Block Address

    Erweitertes Sektor(en)-schreiben

    AH = 43h
    DL = Laufwerksnummer
    DS:SI = Zeiger auf Disk-Request-Packet-Structure (ist genauso aufgebaut wie oben)

    Ein Beispiel über LBA-Programmierung kann ich bei Bedarf auch noch mailen...

    cu todo



  • ich habs, was plymtacky wollte! die richtung in die es geht, hatte <os-developer> ja schon richtig vorgegeben. die adressen (also kopf usw.) muss man erst in die entsprechenden register schreiben. dann schickt man an das befehls-register den befehl 0x70 (SEEK) und er positioniert alles so, wie es in den adressmedienregistern steht.

    ich hoffe, ihr seid mir nicht böse, wenn ich hier im ASM-Forum mit C-Code komme 😉

    Bsp in C:

    // all hd-regs
    #define HD_DATA_REG     0x1F0
    #define HD_ERROR_REG        0x1F1   /* read */
    #define HD_FEATURE_REG      0x1F1   /* write */
    #define HD_SEC_COUNT_REG    0x1F2
    #define HD_SEC_NUM_REG      0x1F3
    #define HD_CYL_NUM_LOW_REG  0x1F4
    #define HD_CYL_NUM_HI_REG   0x1F5
    #define HD_DRIVE_REG        0x1F6
    #define HD_STATE_REG        0x1F7   /* read */
    #define HD_COM_REG      0x1F7   /* write */
    
    // flags in the state-reg
    #define HD_ERROR        1
    #define HD_INDEX        2
    #define HD_CORRECTED_DATA   4
    #define HD_DATA_REQUEST     8
    #define HD_DRIVE_SEEK_COMPLETE  16
    #define HD_DRIVE_FAULT      32
    #define HD_DRIVE_READY      64
    #define HD_BUSY                 128
    
    // commands
    #define HD_READ_SEC_RETRY   0x20
    #define HD_READ_SEC     0x21
    #define HD_WRITE_SEC_RETRY  0x30
    #define HD_WRITE_SEC        0x31
    #define HD_SEEK         0x70
    #define HD_READ_MULTIPLE    0xC4
    #define HD_WRITE_MULTIPLE   0xC5
    #define HD_READ_DMA_RETRY   0xC8
    #define HD_READ_DMA     0xC9
    #define HD_WRITE_DMA_RETRY  0xCA
    #define HD_WRITE_DMA        0xCB
    #define HD_STANDBY      0xE2
    #define HD_SLEEP        0xE6
    #define HD_IDENTIFY_DEVICE  0xEC
    
    void main (void) {
      outb (HD_SEC_COUNT_REG, 1);
      outb (HD_SEC_NUM_REG, 1);
      outb (HD_CYL_NUM_LOW_REG, 0);
      outb (HD_CYL_NUM_HI_REG, 0);
      outb (HD_DRIVE_REG, 128|32|(0<<4)|1);
      outb (HD_COM_REG, HD_SEEK);
    }
    

    (outb ist eine methode zum "outen" eines bytes an den angegebenen port)

    wenn man einen sektor lesen will, kann man das so machen:

    //... hier her noch die defs von oben
    
    void insw (int port, void far * buffer, int words) {
    
      asm  Push  Di
      asm  ClD
      asm  Mov   Dx,port
      asm  Mov   Cx,words
      asm  Mov   Di,Word Ptr [buffer]
      asm  Mov   Ax,Word Ptr [buffer+2]
      asm  Mov   Es,Ax
    
     insw_loop:
      asm  In    Ax,Dx
      asm  StoSW
      asm  Loop  insw_loop
      asm  Pop   Di
    
    }
    
    void main (void) {
      int i;
      int retries = 10000;
      char buffer[512];
    
      outb (HD_SEC_COUNT_REG, 1);
      outb (HD_SEC_NUM_REG, 1);
      outb (HD_CYL_NUM_LOW_REG, 0);
      outb (HD_CYL_NUM_HI_REG, 0);
      outb (HD_DRIVE_REG, 128|32|(0<<4)|1);
      outb (HD_COM_REG, HD_READ_SEC);
    
      do {
        i = (unsigned) inb(HD_STATE_REG);
        if (i & HD_BUSY) continue;
        if (i & HD_DATA_REQUEST) goto ok_to_read;
      } while (--retries > 0);
      // output error-msg
      return;
    
     ok_to_read:
      insw (HD_DATA_REG, buffer, 256);
      for (i = 0; i < 512; ++i)
        printf ("%c", *((char*)buffer+i));
    }
    

    (liest den boot-sector der ersten festplatte)

    viel spaß damit 😉

    cu todo


Anmelden zum Antworten