硬盘驱动程序设计(下)

问题

硬盘读写数据时需要注意哪些问题?

硬盘读写注意事项一

硬盘以扇区为基本读写单位

读写数据时需要计算目标扇区的逻辑地址 (LBA)

确定逻辑地址后,读取扇区数据到内存 (512字节)

  • 读:从读取的扇区中拷贝目标数据
  • 写:数据写入读取的扇区中,之后将扇区数据写回硬盘

硬盘读写注意事项二

硬盘数据端口 (0x1F0) 的读写需要双字节为单位 (WORD)

void WritePortW(ushort port,ushort* buf,uint n)

void ReadPortW(ushort port,ushort* buf,uint n)

汇编小贴士

cld => 清空方向标志位,向高地址方向增加地址值

insw => 端口操作,从端口读取一个字 (双字节)

outsw => 端口操作,从端口写入一个字 (双字节)

rep => 重复指令,重复次数由 ecx 寄存器指定

端口读写

数据结构创建

 

 硬盘数据读写示例

硬盘驱动程序设计

 kentry.asm

;
; void ReadPortW(ushort port, ushort* buf, int n)
;
ReadPortW:
	push ebp
	mov ebp, esp
	
	mov edx, [esp + 8]     ; port
	mov edi, [esp + 12]    ; buf
	mov ecx, [esp + 16]    ; n
	
	cld
	rep insw
	
	nop
	nop
	nop
	
	leave
	ret

;
; void WritePortW(ushort port, ushort* buf, int n)
;
WritePortW:
	push ebp
	mov ebp, esp
	
	xor eax, eax
	
	mov edx, [esp + 8]     ; port
	mov esi, [esp + 12]    ; buf
	mov ecx, [esp + 16]    ; n
	
	cld
	rep outsw
	
	nop
	nop
	nop
	
	leave
	ret

hdraw.c

typedef struct
{
	byte lbaLow;
	byte lbaMid;
	byte lbaHigh;
	byte device;
	byte command;
} HDRegValue; 

static HDRegValue MakeRegVals(uint si, uint action)
{
	HDRegValue ret = {0};
	
	ret.lbaLow = si & 0xFF;
	ret.lbaMid = (si >> 8) & 0xFF;
	ret.lbaHigh = (si >> 16) & 0xFF;
	ret.device = MakeDevRegVal(si);
	ret.command = action;
	
	return ret;
}

static void WritePorts(HDRegValue hdrv)
{
	WritePort(REG_FEATURES, 0);
	WritePort(REG_NSECTOR, 1);
	WritePort(REG_LBA_LOW, hdrv.lbaLow);
	WritePort(REG_LBA_MID, hdrv.lbaMid);
	WritePort(REG_LBA_HIGH, hdrv.lbaHigh);
	WritePort(REG_DEVICE, hdrv.device);
	WritePort(REG_COMMAND, hdrv.command);
		
	WritePort(REG_DEV_CTRL, 0);
}

uint HdRawSectors()
{
	static uint ret = -1;
	
	if((ret == -1) && IsDevReady())
	{
		HDRegValue hdrv = MakeRegVals(0, ATA_IDENTIFY);
		byte* buf = (byte*)Malloc(SECT_SIZE);
		
		WritePorts(hdrv);
		
		if(!IsBusy() && IsDataReady() && buf)
		{
			ushort* data = (ushort*)buf;
			
			ReadPortW(REG_DATA, data, SECT_SIZE >> 1);
			
			ret = ((data[61] << 16) | data[60]);
		}
		
		Free(buf);
	}
	
	return ret;
}

uint HdRawWrite(uint si, byte* buf)
{
	uint ret = 0;
	
	if((si < HdRawSectors()) && buf && !IsBusy())
	{
		HDRegValue hdrv = MakeRegVals(si, ATA_WRITE);
		
		WritePorts(hdrv);
		
		if(ret = (!IsBusy() && IsDataReady()))
		{
			WritePortW(REG_DATA, (ushort*)buf, SECT_SIZE >> 1);
		}
	}
	
	return ret;
}

uint HdRawRead(uint si, byte* buf)
{
	uint ret = 0;
	
	if((si < HdRawSectors()) && buf && !IsBusy())
	{
		HDRegValue hdrv = MakeRegVals(si, ATA_READ);
		
		WritePorts(hdrv);
		
		if(ret = (!IsBusy() && IsDataReady()))
		{
			ReadPortW(REG_DATA, (ushort*)buf, SECT_SIZE >> 1);
		}
	}
	
	return ret;
}

kmain.c

void KMain()
{
	void (*AppModInit)() = (void*)BaseOfApp;

	byte* pn = (byte*)0x475;

    PrintString("L.J.OS\n");
    
    PrintString("GDT Entry: ");
    PrintIntHex((uint)gGdtInfo.entry);
    PrintChar('\n');
    
    PrintString("GDT Size: ");
    PrintIntDec((uint)gGdtInfo.size);
    PrintChar('\n');
    
    PrintString("IDT Entry: ");
    PrintIntHex((uint)gIdtInfo.entry);
    PrintChar('\n');
    
    PrintString("IDT Size: ");
    PrintIntDec((uint)gIdtInfo.size);
    PrintChar('\n');
    
    PrintString("Num Of Hard Disk: ");
    PrintIntDec(*pn);
    PrintChar('\n');
    
    MemModInit((void*)KernelHeapBase, HeapSize);
    
    KeyBoardModInit();
    
    MutexModInit();
    
    uint n = HdRawSectors();
    
    PrintString("Num Of Sectors: ");
    PrintIntDec(n);
    PrintChar('\n');
    
    pn = (byte*)Malloc(512);
    
    if(pn)
    {
    	pn[1] = 0xAB;
    	pn[127] = 0xCD;
    	pn[511] = 0xEF;
    	
    	HdRawWrite(2, pn);
		
		pn[1] = 0;
    	pn[127] = 0;
    	pn[511] = 0;
		
		HdRawRead(2, pn);
		
		PrintString("pn[1] = ");
		PrintIntHex(pn[1]);
		PrintChar('\n');
		
		PrintString("pn[127] = ");
		PrintIntHex(pn[127]);
		PrintChar('\n');
		
		PrintString("pn[511] = ");
		PrintIntHex(pn[511]);
		PrintChar('\n');
		
		Free(pn);
    }
    
    // AppModInit();
    
    TaskModInit();
    
    IntModInit();
    
    ConfigPageTable();
    
    // LaunchTask();
    
}

读写扇区都是以1个扇区为基本单位的,我们建立了512字节大小的内存,并向这片内存的3个部分写了数据,将这片内存写入第2个扇区,然后又将这片扇区读取到内存中,我们打印之前我们修改这片内存的3个部分。

读取到的数据和我们之前写入的数据是一样的。 

我们的目标是建立一个文件系统,我们选择在应用层上利用 Qt 进行开发,因为 Qt 拥有良好的调试环境。

ljfser

utility.c

void* MemCpy(byte* dst, const byte* src, uint n)
{
    int i = 0;
    uint dAddr = (uint)dst;
    uint sAddr = (uint)src;

    if(dAddr < sAddr)
    {
        for(i = 0; i < n; i++)
        {
            dst[i] = src[i];
        }
    }

    if(dAddr > sAddr)
    {
        for(i = n - 1; i >= 0; i--)
        {
            dst[i] = src[i];
        }
    }

    return dst;
}

void MemSet(byte* dst, uint n, int num)
{
    int i = 0;

    for(i = 0; i < n; i++)
    {
        dst[i] = num;
    }
}

char* StrCpy(char* dst, const char* src, uint n)
{
	int i = 0;
    uint dAddr = (uint)dst;
    uint sAddr = (uint)src;

	if((dst != NULL) && (src != NULL))
	{
        if(dAddr < sAddr)
        {
            for(; (src[i] != '\0') && (i < n); i++)
            {
                dst[i] = src[i];
            }

            dst[i] = '\0';
        }

        if(dAddr > sAddr)
        {
            int length = StrLen(src);

            n = (length > n) ? n : length;

            dst[n] = 0;

            for(i = n - 1; i >= 0; i--)
            {
                dst[i] = src[i];
            }
        }
	}
	
	return dst;
}

hdraw.c

#include "hdraw.h"
#include "utility.h"
#include <stdio.h>
#include <malloc.h>

static byte* gHDBuf = NULL;
static uint gSectors = 0;
static char gHDName[64] = {0};

void HDRawSetName(const char* name)
{
    if(name)
    {
        StrCpy(gHDName, name, -1);
    }
}

void HDRawFlush()
{
    FILE* fp = fopen(gHDName, "w");

    if(fp)
    {
        fwrite(gHDBuf, 1, HdRawSectors() * SECT_SIZE, fp);

        fclose(fp);
    }
}

void HdRawModInit()
{
    FILE* fp = fopen(gHDName, "r");

    if(fp)
    {
        long length = 0;

        fseek(fp, 0L, SEEK_END);

        length = ftell(fp);

        gHDBuf = (byte*)malloc(length);

        fseek(fp, 0, SEEK_SET);

        fread(gHDBuf, 1, length, fp);

        gSectors = length / SECT_SIZE;

        fclose(fp);
    }
}

uint HdRawSectors()
{
    return gSectors;
}

uint HdRawWrite(uint si, byte* buf)
{
    uint ret = 0;

    if(ret = ((si < HdRawSectors()) && buf))
    {
        byte* p = (byte*)AddrOff(gHDBuf, si * SECT_SIZE);

        MemCpy(p, buf, SECT_SIZE);
    }

    return ret;
}

uint HdRawRead(uint si, byte* buf)
{
    uint ret = 0;

    if(ret = ((si < HdRawSectors()) && buf))
    {
        byte* p = (byte*)AddrOff(gHDBuf, si * SECT_SIZE);

        MemCpy(buf, p, SECT_SIZE);
    }

    return ret;
}

main.c

#include <stdio.h>
#include "hdraw.h"

int main(void)
{
    byte buf[512] = {0};

    HDRawSetName("hd.img");

    HdRawModInit();

    HdRawRead(2, buf);

    HDRawFlush();

    printf("%x, %x, %x\n", buf[1], buf[127], buf[511]);

    return 0;
}

内存拷贝时要注意内存覆盖的问题。

硬盘的本质是一片连续的空间,我们可以将这片空间读取到内存中,在内存中修改数据,并写入硬盘中,就可以完成硬盘的读写。

我们在 main 函数中读取第二扇区之前被我们修改过的三个字节的数据,并打印,运行结果如下。

我们成功读取到了硬盘上的数据。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值