babyos (十) —— 通过IO端口读取硬盘扇区
2013-01-07 23:05
323 查看
注:以下代码为原创,若发现bug,万望指出,若有问题,欢迎交流,转载请指明出处。若能有助于一二访客,幸甚。
欢迎关注babyos 的成长历程和宝贝计划:https://github.com/guzhoudiaoke/babyos/wiki
上次实现了显示bitmap的功能,却只显示了几个按钮,很不爽,但引导软盘只有1.44M,实在装不下太大的图片。而要想做个桌面背景,需要读取800x600的图片。所以是时候让baby 学会读硬盘扇区了。当然只是通过IO端口读取硬盘扇区,而不会设计文件的概念。
一个硬盘有很多个盘片(一个盘片有两个盘面),它们垂直排列成圆柱,所以也有很多个磁头来读写不同的盘面。
2)磁道:硬盘转动时磁头是不动的,动的是盘片。则磁头会在盘片上划出一个个圆形轨迹,这些轨迹就是磁道。磁道由外向内编号,编号从0开始。
3)柱面:硬盘有很多个盘片,从上到下排成柱状,不同盘片,相同磁道,组成的就叫柱面(cylinder)。
柱面从0开始编号,柱面号即磁道号。
4)扇区:磁盘上每个磁道被等分成若干个弧段,这些弧段就是扇区。
一个扇区通常512byte,扇区从1开始编号。
1)CHS模式(Cylinder/Head/Sector)
就是给定柱面号、磁头号、扇区号。柱面号给定在哪一个圆环上,磁头号指定了在哪一层,扇区号指定了圆上的位置,于是就定位到了一个准确的扇区了。
2)LBA(Logical Block Addressing,逻辑块寻址)
就是只给一个逻辑号码,根据硬盘的柱面数、每个柱面的磁头数、每个磁道的扇区数来计算柱面号、磁头号、扇区号。
编号方法:
按照柱面、磁头、扇区顺序来编,即编完0号柱面0号磁头所在磁道的若干扇区后,再编0号柱面1号磁头所在磁道的所以扇区,编完0号柱面的所有磁头后再编1号柱面。
3)相互转换
LBA = (柱面号 * 一个柱面的磁头数 + 磁头号) * 一个磁道上的扇区数 + (扇区号-1)
柱面号 = LBA / (一个柱面的磁头数 * 每个磁道扇区数)
令 x = LBA % (一个柱面的磁头数 * 每个磁道扇区数)
磁头号 = x / 每个磁道上的扇区数
扇区号 = x % 每个磁道上的扇区数 + 1
每个连接到I/O总线上的设备都由自己的I/O地址集,即所谓的I/O端口(I/O port)。
每个设备的I/O端口都被组织成一组专用的寄存器,CPU可给控制寄存器发命令对设备进行控制、从状态寄存器读取设备状态、可以向输出寄存器写入数据来把数据输出到设备、可通过读取输入寄存器的内容来从设备取得数据。
总之就是通过读写端口来控制设备。
一个普通的PC主板上通常有两个IDE口,分别对应两个IDE通道:primary和secondary有时也成IDE0和IDE1。
每个IDE通道又能连接两个设备,称为主设备(Master)和从设备(Slave),对不同的IDE通道的访问是通过I/O端口来区分的。
IDE(integrated drive electronics)即电子集成驱动器,主要接硬盘和光驱。
接到主设备上的硬盘称为0号硬盘。
与0号硬盘有关的I/O端口:
1F0H 0号硬盘数据寄存器
1F1H 0号硬盘错误寄存器(读时)、0号硬盘Features寄存器(写时)
1F2H 0号硬盘数据扇区计数
1F3H 0号硬盘扇区数
1F4H 0号硬盘柱面(低字节)
1F5H 0号硬盘柱面(高字节)
1F6H 0号硬盘驱动器/磁头寄存器
1F7H 0号硬盘状态寄存器(读时)、0号硬盘命令寄存器(写时)
注:下图来自于渊《Orange‘s 一个操作系统的实现》,他使用的是LBA方式,babyos暂时使用CHS方式,故bit0~bit3表示磁头号,bit4为驱动器号为0,bit6为0,表示使用CHS方式。
即:
可以从端口0x1F0读取数据;
若发生错误可以从0x1F1读取错误;
若要从硬盘读数据可以从0x1F2指定读取的扇区数,0x1F3、0x1F4、0x1F5、0x1F6指定CHS(也可以是LBA,此处暂时不研究)
可以从0x1F7读取硬盘状态或向硬盘发送命令。
接口控制方式:
1)程序查询方式:CPU通过I/O指令询问指定外设当前状态,如果外设准备就绪,进行操作,否则CPU等待,循环查询。
2)中断处理方式:外设数据交换准备就绪,向CPU提出服务请求。
3)DMA(直接存储器存取):采用一个专门的控制器来控制内存与外设直接的数据交流,无须CPU介入。
下面将要使用的方式是程序查询方式,因为babyos 还没有实现中断。
步骤:
1)通过状态寄存器查询硬盘状态,看是否空闲,若忙,则等待
2)把读取扇区的个数、CHS写入相应端口
3)通过命令寄存器向硬盘发送读命令
4)从数据寄存器读取数据
注:
babyos目前只想从硬盘读若干个扇区,不想实现高级的硬盘操作功能和文件系统;
只考虑主硬盘;
暂时使用CHS方式,但会实现一个LBA到CHS的转换方法;
欢迎关注babyos 的成长历程和宝贝计划:https://github.com/guzhoudiaoke/babyos/wiki
上次实现了显示bitmap的功能,却只显示了几个按钮,很不爽,但引导软盘只有1.44M,实在装不下太大的图片。而要想做个桌面背景,需要读取800x600的图片。所以是时候让baby 学会读硬盘扇区了。当然只是通过IO端口读取硬盘扇区,而不会设计文件的概念。
1.硬盘长什么样
硬盘拆开盖,如下图所示:2.物理结构
一个简单的物理模型:3.磁头、磁道、柱面、扇区
1)磁头:如上图所示,它是硬盘中最贵的部分了,读写操作就靠它,它通过感应旋转的盘片上磁场的变化来读取数据,通过改变盘片上的磁场来写入数据。磁头从0开始编号。一个硬盘有很多个盘片(一个盘片有两个盘面),它们垂直排列成圆柱,所以也有很多个磁头来读写不同的盘面。
2)磁道:硬盘转动时磁头是不动的,动的是盘片。则磁头会在盘片上划出一个个圆形轨迹,这些轨迹就是磁道。磁道由外向内编号,编号从0开始。
3)柱面:硬盘有很多个盘片,从上到下排成柱状,不同盘片,相同磁道,组成的就叫柱面(cylinder)。
柱面从0开始编号,柱面号即磁道号。
4)扇区:磁盘上每个磁道被等分成若干个弧段,这些弧段就是扇区。
一个扇区通常512byte,扇区从1开始编号。
4.寻址
如何找到我们想要的数据呢?即如何在硬盘上找到任意一个物理地址。1)CHS模式(Cylinder/Head/Sector)
就是给定柱面号、磁头号、扇区号。柱面号给定在哪一个圆环上,磁头号指定了在哪一层,扇区号指定了圆上的位置,于是就定位到了一个准确的扇区了。
2)LBA(Logical Block Addressing,逻辑块寻址)
就是只给一个逻辑号码,根据硬盘的柱面数、每个柱面的磁头数、每个磁道的扇区数来计算柱面号、磁头号、扇区号。
编号方法:
按照柱面、磁头、扇区顺序来编,即编完0号柱面0号磁头所在磁道的若干扇区后,再编0号柱面1号磁头所在磁道的所以扇区,编完0号柱面的所有磁头后再编1号柱面。
3)相互转换
LBA = (柱面号 * 一个柱面的磁头数 + 磁头号) * 一个磁道上的扇区数 + (扇区号-1)
柱面号 = LBA / (一个柱面的磁头数 * 每个磁道扇区数)
令 x = LBA % (一个柱面的磁头数 * 每个磁道扇区数)
磁头号 = x / 每个磁道上的扇区数
扇区号 = x % 每个磁道上的扇区数 + 1
5.硬盘操作
CPU与外设、存储器的连接和数据交换都需要通过接口设备来实现。每个连接到I/O总线上的设备都由自己的I/O地址集,即所谓的I/O端口(I/O port)。
每个设备的I/O端口都被组织成一组专用的寄存器,CPU可给控制寄存器发命令对设备进行控制、从状态寄存器读取设备状态、可以向输出寄存器写入数据来把数据输出到设备、可通过读取输入寄存器的内容来从设备取得数据。
总之就是通过读写端口来控制设备。
一个普通的PC主板上通常有两个IDE口,分别对应两个IDE通道:primary和secondary有时也成IDE0和IDE1。
每个IDE通道又能连接两个设备,称为主设备(Master)和从设备(Slave),对不同的IDE通道的访问是通过I/O端口来区分的。
IDE(integrated drive electronics)即电子集成驱动器,主要接硬盘和光驱。
接到主设备上的硬盘称为0号硬盘。
与0号硬盘有关的I/O端口:
1F0H 0号硬盘数据寄存器
1F1H 0号硬盘错误寄存器(读时)、0号硬盘Features寄存器(写时)
1F2H 0号硬盘数据扇区计数
1F3H 0号硬盘扇区数
1F4H 0号硬盘柱面(低字节)
1F5H 0号硬盘柱面(高字节)
1F6H 0号硬盘驱动器/磁头寄存器
1F7H 0号硬盘状态寄存器(读时)、0号硬盘命令寄存器(写时)
注:下图来自于渊《Orange‘s 一个操作系统的实现》,他使用的是LBA方式,babyos暂时使用CHS方式,故bit0~bit3表示磁头号,bit4为驱动器号为0,bit6为0,表示使用CHS方式。
/* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +-----+-----+-----+-----+-----+-----+-----+-----+ | 1 | L | 1 | DRV | HS3 | HS2 | HS1 | HS0 | +-----+-----+-----+-----+-----+-----+-----+-----+ | | \_____________________/ | | | | | `------------ If L=0, Head Select. | | These four bits select the head number. | | HS0 is the least significant. | | If L=1, HS0 through HS3 contain bit 24-27 of the LBA. | `--------------------------- Drive. When DRV=0, drive 0 (master) is selected. | When DRV=1, drive 1 (slave) is selected. `--------------------------------------- LBA mode. This bit selects the mode of operation. When L=0, addressing is by 'CHS' mode. When L=1, addressing is by 'LBA' mode. */
即:
可以从端口0x1F0读取数据;
若发生错误可以从0x1F1读取错误;
若要从硬盘读数据可以从0x1F2指定读取的扇区数,0x1F3、0x1F4、0x1F5、0x1F6指定CHS(也可以是LBA,此处暂时不研究)
可以从0x1F7读取硬盘状态或向硬盘发送命令。
接口控制方式:
1)程序查询方式:CPU通过I/O指令询问指定外设当前状态,如果外设准备就绪,进行操作,否则CPU等待,循环查询。
2)中断处理方式:外设数据交换准备就绪,向CPU提出服务请求。
3)DMA(直接存储器存取):采用一个专门的控制器来控制内存与外设直接的数据交流,无须CPU介入。
下面将要使用的方式是程序查询方式,因为babyos 还没有实现中断。
6.读取若干个扇区
现在只学习最简单的硬盘操作——从硬盘读取若干个扇区,其他功能以后学习文件系统时再研究。步骤:
1)通过状态寄存器查询硬盘状态,看是否空闲,若忙,则等待
2)把读取扇区的个数、CHS写入相应端口
3)通过命令寄存器向硬盘发送读命令
4)从数据寄存器读取数据
注:
babyos目前只想从硬盘读若干个扇区,不想实现高级的硬盘操作功能和文件系统;
只考虑主硬盘;
暂时使用CHS方式,但会实现一个LBA到CHS的转换方法;
/************************************************************************* > File: harddisk.c > Describe: 实现基本硬盘操作功能 > Author: 孤舟钓客 > Mail: guzhoudiaoke@126.com > Time: 2013年01月06日 星期日 17时40分32秒 ************************************************************************/ #include <harddisk.h> #include <io.h> #include <font.h> #include <graphics.h> BOOL harddisk_read(u32 lba, u32 sects_to_read, u8* buffer) { u32 cylinder_no, head_no, sect_no, temp; u32 num_of_dwords; cylinder_no = lba / (HD0_HEAD_PER_CYLINDER * HD0_SECT_PER_TRACK); temp = lba % (HD0_HEAD_PER_CYLINDER * HD0_SECT_PER_TRACK); head_no = temp / HD0_SECT_PER_TRACK; sect_no = temp % HD0_SECT_PER_TRACK + 1; /* 检查硬盘是否忙,忙则等待 */ while ((inb(HD_PORT_STATUS) & 0x80) != 0) ; /* 设置读取的扇区数和CHS, HD_PORT_DRIVE_HEAD端口bit7、bit5需要为1,bit6为0时bit0~bit3表示磁头号, bit4为驱动器号,0 表示HD0,故下面head_no要或操作10100000即0xa0 */ outb(sects_to_read, HD_PORT_SECT_COUNT); outb(sect_no, HD_PORT_SECT_NO); outb(cylinder_no, HD_PORT_CYLINDER_LOW); cylinder_no >>= 8; outb((cylinder_no), HD_PORT_CYLINDER_HIGH); head_no |= 0xa0; outb((head_no), HD_PORT_DRIVE_HEAD); /* 发送读命令 */ outb(HD_CMD_READ, HD_PORT_COMMAND); num_of_dwords = (sects_to_read << 7); /* 从HD_PORT_DATA读取数据,每个扇区512字节,即sects_to_read << 7个双字 */ insl(HD_PORT_DATA, buffer, num_of_dwords); return TRUE; }
相关文章推荐
- linux读取硬盘指定扇区
- 读取硬盘的MBR引导扇区(Windows各系统通用)
- 通过文件的方式对硬盘扇区进行直接读写操作
- windows sdk编程系列文章 --- 直接从硬盘扇区读取文件内容
- 通过CreateFile来读取磁盘扇区的方法
- 通过CreateFile来读取磁盘扇区的方法
- 在PB中通过读取硬盘序列号
- 直接读取硬盘扇区
- 如何在PB中通过读取硬盘序列号实现软件加密
- 通过IO端口读取外部数据,带中断
- 读取硬盘的第一扇区数据(MBR+DPT+Signature)
- 通过IO端口读取外部数据,带中断
- 通过IO端口读取外部数据,带中断
- 通过扇区读取文件内容
- linux读取硬盘指定扇区
- 读取硬盘扇区的数据
- 把一个类存放在硬盘,并通过代码读取类的信息,输出到屏幕上
- 通过读取硬盘中的文档,来统计该文档中指定字符的个数,并对指定字符进行替换
- 如何在PB中通过读取硬盘序列号实现软件加密
- 读取笔记本的摄像头的原始yuv数据,通过libav(ffmpeg编码)