Page 1 of 1

ATA R/W problems.

Posted: Sat Oct 13, 2007 9:25 pm
by StephanvanSchaik
Hey all,


When I run this code I get 2 page faults. Does somebody see what is wrong with it?

Code: Select all

char* hd_read(unsigned int lba) {
    unsigned char buffer[512] = {0};
    char* result;
    outportb(control + 1, 0x00);
    outportb(control + 2, 0x01);
    outportb(control + 3, (unsigned char)lba);
    outportb(control + 4, (unsigned char)(lba >> 8));
    outportb(control + 5, (unsigned char)(lba >> 16));
    outportb(control + 6, 0xE0 | (0xA0 << 4) | ((lba >> 24) & 0x0F));
    outportb(control + 7, 0x20);
    while (!(inportb(control + 7) & 0x8));
    int idx = 0;
    unsigned short tmpword;
    while (idx < 256) {
        tmpword = inportw(control);
        buffer[idx * 2] = (unsigned char)tmpword;
        buffer[idx * 2 + 1] = (unsigned char)(tmpword >> 8);
        idx++;
    }
    result = (char *)buffer;
    return result;
}

void hd_write(unsigned int lba, char buffer[512]) {
    outportb(control + 1, 0x00);
    outportb(control + 2, 0x01);
    outportb(control + 3, (unsigned char)lba);
    outportb(control + 4, (unsigned char)(lba >> 8));
    outportb(control + 5, (unsigned char)(lba >> 16));
    outportb(control + 6, 0xE0 | (0xA0 << 4) | ((lba >> 24) & 0x0F));
    outportb(control + 7, 0x30);
    while (!(inportb(control + 7) & 0x8));
    int idx = 0;
    unsigned short tmpword;
    while (idx < 256) {
        tmpword = buffer[8 + idx * 2] | (buffer[8 + idx * 2 + 1] << 8);
        outportw(control, tmpword);
        idx++;
    }
}

Code: Select all

    hd_write(2, "This is a test.");
    kprintf("%s", hd_read(2));
I cannot find the problem here :?. Maybe someone else does. Thank you for help.


Regards, Stephan J.R. van Schaik.

Posted: Sun Oct 14, 2007 1:57 am
by bewing
I'm pretty sure it is the value you are trying to send to "control + 6" -- and the exact error you are getting is from your implementation of outportb() -- because the value you are sending to that function is bigger than 255.

The thing is, that 0xA0 you are shifting in is totally wrong. First, when you shift 0xA0 up by 4 bits, you get the value 0xA00, which is bigger than can fit in a byte. Second, the number you REALLY want there is either a one bit, or a zero bit -- which tells your driver whether you are trying to talk to the slave or master drive on the ATA bus. If you want to send this read command to the slave drive on the bus, you take the value 1, shift it up 4 bits, OR it with 0xE0 to get 0xF0, then OR in the top 4 bits of the LBA28 as the bottom 4 bits of the byte.

The 0 that you are sending to port "control + 1" just wastes CPU time. The value is discarded. And I'm assuming that "control" is a global, since it is never declared.

And when you call the write function with "This is a test.", and then declare that input constant string as a 512 byte array? Does that actually work in modern compilers??? How bizarre. If I was a compiler, I'd bite you for doing that.

Posted: Sun Oct 14, 2007 6:56 am
by StephanvanSchaik
Thank you for help. It works now I think but I still get the page faults though.

Code: Select all

void hd_read(unsigned int lba, void* buffer) {
    outportb(control + 2, 0x01);
    outportb(control + 3, (unsigned char)lba);
    outportb(control + 4, (unsigned char)(lba >> 8));
    outportb(control + 5, (unsigned char)(lba >> 16));
    unsigned char test = 0xA0 | (lba >> 24);
    outportb(control + 6, test);
    kprintf("%x\n", test);
    outportb(control + 7, 0x20);
    while (!(inportb(control + 7) & 0x8));
    insl(control, buffer, 1 << 7);
}

void hd_write(unsigned int lba, void* buffer) {
    outportb(control + 2, 0x01);
    outportb(control + 3, (unsigned char)lba);
    outportb(control + 4, (unsigned char)(lba >> 8));
    outportb(control + 5, (unsigned char)(lba >> 16));
    unsigned char test = 0xA0 | (lba >> 24);
    outportb(control + 6, test);
    kprintf("%x\n", test);
    outportb(control + 7, 0x30);
    while (!(inportb(control + 7) & 0x8));
    outsl(buffer, 1 << 7, control);
}

Code: Select all

    unsigned char wbuf[512] = {0};
    wbuf[0] = 'T';
    wbuf[1] = 'h';
    wbuf[2] = 'i';
    wbuf[3] = 's';
    wbuf[4] = ' ';
    wbuf[5] = 'i';
    wbuf[6] = 's';
    wbuf[7] = ' ';
    wbuf[8] = 'a';
    wbuf[9] = ' ';
    wbuf[10] = 't';
    wbuf[11] = 'e';
    wbuf[12] = 's';
    wbuf[13] = 't';
        
    hd_write(2, wbuf);
    unsigned char rbuf[512];
    hd_read(2, rbuf);
    kprintf("%s", rbuf);
This what I get at the part of the HD.
000000a0
Page fault! (present ) at 0x00000000
000000a0
Page fault! (present ) at 0x00000000
This is a test
And yes control is set as global. It has 0x1F0 as default value but you can set it with a function.

Posted: Sun Oct 14, 2007 2:35 pm
by bewing
0xE0! Not A0.

(And you understand that your insl and outsl functions are only allowed to "in" and "out" 16 bit words at a time? -- Your code can combine them afterward into longs, of course.)

Posted: Sun Oct 14, 2007 3:08 pm
by StephanvanSchaik
Now I get:
[HD] Reading Sector 1: Page Fault! (present ) at 0x00000000
This is a test[HD] Reading Sector 2: Page Fault! (present ) at 0x00000000
a test.

Posted: Sun Oct 14, 2007 3:23 pm
by exkor
this is how you can read very 1st sector on hard drive

Code: Select all

  mov  edi, [ecx+16] ;edi = command regs
  mov  esi, [ecx+24] ;esi = bus master regs

  ;select master/slave drive
  mov  eax, [ecx+28] ;eax = master/slave (0ah,0bh)
  lea  edx, [edi+6]
  or   eax, 01000000b   ;select LBA, LBA [24:27] =0
  out  dx, al

  mov  byte [ebx], 5    ;50ms timeout
  add  edx, 1
.wait1:
  cmp  byte [ebx], 0
  je  .over             ;timeout hits, exit
  xor  eax, eax
  in   al, dx
  test al, 80h+08h      ;BSY & DRQ must be 0
  jnz  .wait1
  test al, 40h
  jz   .wait1           ;DRDY must be 1

  lea  edx, [edi+2]
  mov  eax, 1           ;1 sector
  out  dx, al

  ;-------
  xor  eax, eax ;entire lba addr will be 0 - 1st sector on HD
  add  edx, 1
  out  dx, al           ;LBA [0:7]
  ;--------
  add  edx, 1
  out  dx, al           ;LBA [8:15]
  ;--------
  add  edx, 1
  out  dx, al           ;LBA [16:23]
  ;--------

  mov  eax, 20h
  add  edx, 2
  mov  byte [ebx+4], 0
  out  dx, al

  ;wait for interrupt
  mov  byte [ebx], 100
  .status:
    cmp  byte [ebx+4], 1
    je   .after_int
    cmp  byte [ebx], 0
    jne  .status
    jmp  .over

.after_int:
  ;read status here or analyze status from interrupt handler

  mov  ecx, 256
  mov  edx, edi
.read:
  sub  ecx, 1
  in   ax, dx

  pushfd
  ;display stuff on screen
  popfd

  jnz  .read

  mov  edx, [ecx+20] ;alternate status register
  xor  eax, eax
  in   al, dx
  lea  edx, [edi+7]
  xor  eax, eax
  in   al, dx

.over:

first thing to do in your code is to add delays