硬盘的chs模式是指chs(Cylinder/Head/Sector)模式, 既磁头数(Heads), 柱面数(Cylinders), 扇区数(Sectors per track),以及相应的寻址方式. 刚一开始硬盘的容量还非常小, 人们采用与软盘类似的结构生产硬盘. 也就是硬盘盘片的每一条磁道都具有相同的扇区数。
其中:
磁头数(Heads) 表示硬盘总共有几个磁头,也就是有几面盘片, 最大为 256 (用 8 个二进制位存储), 取值范围为: 0 到 Heads-1;
柱面数(Cylinders) 表示硬盘每一面盘片上有几条磁道, 最大为 1024(用 10 个二进制位存储),取值范围为: 0 到 Cylinders-1;
扇区数(Sectors per track) 表示每一条磁道上有几个扇区, 最大为63 (用 6 个二进制位存储),取值范围为: 1 到 Sectors per track (注意是从 1 开始), 每个扇区一般是 512个字节.
所以磁盘最大容量为: 256 * 1024 * 63 * 512 / 1048576 = 8064 MB ( 1M = 1048576 Bytes)
有以下几种尺寸单位:扇区 (Sector) = 512 字节 (一般情况下)磁道 (Track) = (Sectors per track) 扇区
柱面 (Cylinder)= (Sectors per track) * Heads 扇区
这种方式会浪费很多磁盘空间 (与软盘一样). 为了解决这一问题, 进一步提高硬盘容量, 人们改用等密度结构生产硬盘. 也就是说, 外圈磁道的扇区比内圈磁道多. 采用这种结构后, 硬盘不再具有实际的3D参数, 寻址方式也改为线性寻址, 即以扇区为单位进行寻址.为了与使用3D寻址的老软件兼容 (如使用BIOS Int13H接口的软件), 在硬盘控制器内部安装了一个地址翻译器, 由它负责将老式3D参数翻译成新的线性参数. 这也是为什么现在硬盘的3D参数可以有多种选择的原因 (不同的工作模式, 对应不同的3D参数,如 LBA, LARGE, NORMAL). CHS模式只能识别大硬盘的前面8G.lba使用的线性寻址,突破了1024柱面的限制,能访问8G以外的空间了。
LBA寻址模式是直接以扇区为单位进行寻址的,将硬盘划分成一个一个扇区,寻址是从0扇区开始到最后一个扇区。把硬盘所有的物理扇区的C/H/S编号通过一定的规则转变为一线性的编号,不再用磁头/柱面/扇区三种单位来进行寻址,将CHS这种三维寻址方式转变为一维的线性寻址,它提高了效率简化了操作。
主要:CHS寻址是从1扇区开始,LBA寻址是从0扇区开始.实际编程时一定要注意这一点。
端口 |
LBA |
CHS |
||
|
读 |
写 |
读 |
写 |
1f0h |
数据寄存器 |
|||
1f1h |
错误寄存器 |
特征寄存器 |
错误寄存器 |
写前预补偿寄存器 |
1f2h |
扇区数寄存器 |
|||
1f3h |
LBA块地址(0~7位) |
扇区号寄存器 |
||
1f4h |
LBA块地址(8~15位) |
柱面号寄存器(高地址) |
||
1f5h |
LBA块地址(16~23位) |
柱面号寄存器(低地址) |
||
1f6h |
驱动器号+LBA块地址(24~27位) |
驱动器号/磁头号 |
||
1f7h |
状态寄存器 |
命令寄存器 |
状态寄存器 |
命令寄存器 |
36f |
交换状态寄存器 |
硬盘控制寄存器 |
无 |
硬盘控制寄存器 |
1f6寄存器
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
1 |
L |
1 |
DRV |
HS3 |
HS2 |
HS1 |
HS0 |
L:0,使用CHS模式;1,使用LBA模式
DRV:0,主盘;1,从盘
HS0~HS3:LBA模式下是LBA块地址的24~27位。CHS模式下是磁头号的低四位
添加头文件
#ifndef HDREG_H #define HDREG_H #define HD_DATA 0x1f0 #define HD_ERROR 0x1f1 #define HD_STATUS 0x1f7 #define HD_PRECOMP HD_ERROR #define HD_COMMAND HD_STATUS #define HD_CMD 0x3f6 #endif
blk_drv目录下的hd.c
#include <linux/head.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/hdreg.h> #include <asm/system.h> #include <asm/io.h> extern void hd_interrupt(void); #define port_read(port,buf,nr) \ __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):) #define port_write(port,buf,nr) \ __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):) static int controller_ready(void) { int retries=10000; while (--retries && (inb_p(HD_STATUS)&0x80)); return (retries); } #define LBA #ifdef LBA /* *drive-硬盘号(0或1) *nsect-要读写的扇区数 *lba -起始扇区 *cmd -命令码 */ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int lba,unsigned int cmd) { register int port asm("dx"); if (drive>1) printk("Trying to write bad sector"); if (!controller_ready()) printk("HD controller not ready"); outb_p(0,HD_CMD); port=HD_DATA; outb_p(0,++port); outb_p(nsect,++port); outb_p(lba&0xff,++port); outb_p((lba>>8)&0xff,++port); outb_p((lba>>16)&0xff,++port); outb_p(0xE0|(drive<<4)|((lba>>24)&0xf),++port); outb(cmd,++port); } #else /* *drive-硬盘号(0或1) *nsect-要读写的扇区数 *sect -起始扇区 *head -磁头号 *cyl -柱面号 *cmd -命令码 */ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, unsigned int head,unsigned int cyl,unsigned int cmd) { register int port asm("dx"); if (drive>1 || head>15) printk("Trying to write bad sector"); if (!controller_ready()) printk("HD controller not ready"); outb_p(0,HD_CMD); port=HD_DATA; outb_p(0,++port); outb_p(nsect,++port); outb_p(sect,++port); outb_p(cyl,++port); outb_p(cyl>>8,++port); outb_p(0xA0|(drive<<4)|head,++port); outb(cmd,++port); } #endif void hd_init(void) { set_intr_gate(0x2E,&hd_interrupt); outb_p(inb_p(0x21)&0xfb,0x21); outb(inb_p(0xA1)&0xbf,0xA1); } void print_identify_info(u16* hdinfo) { //看手册77页这张表 printk("Number of logical cylinders:%x\n",hdinfo[1]); printk("Number of logical heads:%x\n",hdinfo[3]); printk("sectors per track:%x\n",hdinfo[6]); hdinfo[20]='\0'; printk("Serial number:%s\n",&hdinfo[10]); printk("obsolete:%x\n",hdinfo[22]); } #define SECTOR_SIZE 512 u8 hdbuf[SECTOR_SIZE * 2]; void hd_identify(int drive)//读identify { #ifdef LBA hd_out(0,0,0,0xEC); #else hd_out(0,0,0,0,0,0xEC); #endif delay(12); port_read(HD_DATA, hdbuf, SECTOR_SIZE); print_identify_info((u16*)hdbuf); }
在main.c中添加测试程序
hd_init();//初始化
hd_identify(0);//读identity
move_to_user_mode();
blk_drv目录下的Makefile
AR =ar AS =as LD =ld LDFLAGS = --oformat binary -N -e start -Ttext 0x0 CC =gcc CFLAGS = -I../../include -fno-stack-protector .c.s: $(CC) $(CFLAGS) \ -S -o $*.s $< .s.o: $(AS) -c -o $*.o $< .c.o: $(CC) $(CFLAGS) \ -c -o $*.o $< OBJS = hd.o lib.a: $(OBJS) $(AR) rcs blk_drv.a $(OBJS) sync clean: rm -f *.o *.s *.a
hd_interrupt: movb $0x20,%al outb %al,$0xA0 outb %al,$0x20 iret
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a
kernel/blk_drv/blk_drv.a:
(cd kernel/blk_drv; make)
kernel/chr_drv/chr_drv.a:
(cd kernel/chr_drv; make)
system: boot/head.o init/main.o kernel/kernel.o fs/fs.o $(LIBS) $(DRIVERS)
$(LD) $(LDFLAGS) -o system boot/head.o kernel/kernel.o init/main.o fs/fs.o $(LIBS) $(DRIVERS)