原创 DOS下BIOS中断INT13H、IO端口直接编程读取IDE、SATA硬盘的参数和TC语言程序2

2015-4-23 16:45 1725 14 14 分类: 工程师职场 文集: 学以致用

九、    直接读取SATA硬盘之(二)、PCI设备枚举、32位端口的访问、找到AHCI控制器的内存空间地址

 

(1)PCI设备枚举

SATA AHCI控制器是一个挂在PCI总线上的设备,通过对PCI设备枚举,可以找到这个设备,然后得到它的配置空间信息。

方法是直接对IO端口号CF8H、CFCH进行访问。原理是“总线号、设备号、功能号”这三者确定了一个“PCI功能设备”,其实是读这个设备的00H号寄存器,字节偏移地址是00处,这里有Device ID和Vendor ID,通过判断这个ID是否正常,来判断是否存在一个PCI设备。

很多文章有介绍,比如:《dos下枚举 pci设备》(http://blog.csdn.net/zyl910/article/details/858349)

 

问难的难点是:CF8H、CFCH端口是32位的,即访问它们的数据应是32位(4字节)的,而DOS下的汇编、TC都只有8位、16位端口访问。

解决方法有2个:

一个是在程序中插入汇编,采用32位指令前缀66H,这样就能执行32位指令。可参考上面的文章,以及《DEBUG下使用32位指令的技术》。

另一个是采用PCI BIOS中断,中断号1AH。可参见上面的文章。这里采用这种方法,用WORD方式来访问PCI配置空间。

INT 1A - PCI BIOS v2.0c+ - READ CONFIGURATION WORD

AX = B109h

BH = bus number

BL = device/function number (bits 7-3 device, bits 2-0 function)

DI = register number (0000h-00FFh, must be multiple of 2) (see #00878) ――偏移量(以字节计)

Return:

CF clear if successful

   CX = word read ―― 一次读出2个字节,即1个WORD

CF set on error

AH = status (00h,87h) (see #00729)

EAX, EBX, ECX, and EDX may be modified

all other flags (except IF) may be modified

 

 

(2)程序

从上面的文章中的程序改的,findpci.c:

  /*

File:    epcib.c

Name:    使用PCI BIOS来枚举PCI设备

Author:   zyl910

Blog:    http://blog.csdn.net/zyl910/

Version:  V1.0

Updata:   2006-6-30

*/

 

#include <stdio.h>

#include <conio.h>

#include <dos.h>

 

typedef unsigned char BYTE;

typedef unsigned int WORD;

typedef unsigned long DWORD;

 

 

/* PCI设备索引。bus/dev/func 共16位,为了方便处理可放在一个WORD中 */

#define PDI_BUS_SHIFT  8

#define PDI_BUS_SIZE   8

#define PDI_BUS_MAX    0xFF

#define PDI_BUS_MASK   0xFF00

 

#define PDI_DEVICE_SHIFT  3

#define PDI_DEVICE_SIZE   5

#define PDI_DEVICE_MAX    0x1F

#define PDI_DEVICE_MASK   0x00F8

 

#define PDI_FUNCTION_SHIFT  0

#define PDI_FUNCTION_SIZE   3

#define PDI_FUNCTION_MAX    0x7

#define PDI_FUNCTION_MASK   0x0007

 

#define MK_PDI(bus,dev,func) (WORD)((bus&PDI_BUS_MAX)<<PDI_BUS_SHIFT | (dev&PDI_DEVICE_MAX)<<PDI_DEVICE_SHIFT |(func&PDI_FUNCTION_MAX) )

 

 

int main(void)

{

int bus, dev, func;

int i;

union REGS regs;

WORD wAddr;

FILE *hF;

char szFile[0x10];

 

printf("\n");

printf("Bus#\tDevice#\tFunc#\tVendor\tDevice\tClass\tIRQ\tIntPin\n");

 

/* 枚举PCI设备 */

for(bus = 0; bus <= PDI_BUS_MAX; ++bus) // BUS编号00~FFH,256个

{

  for(dev = 0; dev <= PDI_DEVICE_MAX; ++dev)  // 设备编号00~1FH,32个

  {

     for(func = 0; func <= PDI_FUNCTION_MAX; ++func)   //功能编号00~07H,8个

     {

        /* 计算地址 */

        wAddr = MK_PDI(bus, dev, func);    //转换成16BIT地址

       

        /* 获取厂商ID */

        regs.x.ax = 0xB109;       // PCI BIOS v2.0c+ - READ CONFIGURATION WORD

        regs.x.bx = wAddr;        // BH = bus number

                                  // BL = device/function number (bits 7-3 device, bits 2-0 function)

        regs.x.di = 0;            // DI=register number=00H,即取出“PCI Configuration Data”的00H处的值,00H处的值是 Vendor ID

        regs.x.cx = 0xFFFF;       // 非法的Vendor ID   ,也可以不写这句

        int86(0x1A, &regs, &regs);   //返回 CX = word read,如果是非法Vendor ID,则会返回CX=0xFFFF

    

        /* 判断设备是否存在。FFFFh是非法厂商ID */

        if (regs.x.cx != 0xFFFF)     //如果Vendor ID合法

        {

             /* bus/dev/func */

             printf("%2.2XH\t%2.2XH\t%1XH\t", bus, dev, func);

        

             /* Vendor */

             printf("%4.4XH\t", regs.x.cx);

        

             /* Device */

             regs.x.ax = 0xB109;    // PCI BIOS v2.0c+ - READ CONFIGURATION WORD

             regs.x.bx = wAddr;    

             regs.x.di = 2;         // Device ID 。DI=register number=02H,即取出“PCI Configuration Data”的02H处的值,02H处的值是 Device ID

             int86(0x1A, &regs, &regs);

             printf("%4.4XH\t", regs.x.cx);

        

             /* Class Code */

             regs.x.ax = 0xB109; // PCI BIOS v2.0c+ - READ CONFIGURATION WORD

             regs.x.bx = wAddr;

             regs.x.di = 0xA; // 取出Class/SubClass 值

             int86(0x1A, &regs, &regs);

             printf("%4.4XH\t", regs.x.cx);

        

             /* IRQ/intPin */

             regs.x.ax = 0xB109; // PCI BIOS v2.0c+ - READ CONFIGURATION WORD

             regs.x.bx = wAddr;

             regs.x.di = 0x3C; // 取出IRQ/IntPin 值

             int86(0x1A, &regs, &regs);

             printf("%d\t", (BYTE)regs.x.cx);

             printf("%d", (BYTE)(regs.x.cx>>8));

        

             printf("\n");

        

             /* 写文件 */

             sprintf(szFile, "PCI%2.2X%2.2X%X.bin", bus, dev, func);

             hF = fopen(szFile, "wb");           //打开文件

             if (hF != NULL)

             {

                /* 256字节的PCI配置空间 */

                for (i = 0; i < 0x100; i += 2)

                {

                     /* Read */

                     regs.x.ax = 0xB109; // PCI BIOS v2.0c+ - READ CONFIGURATION WORD

                     regs.x.bx = wAddr;

                     regs.x.di = i;

                     int86(0x1A, &regs, &regs);

               

                     /* Write */

                     fwrite(&regs.x.cx, 2, 1, hF);

                }

                fclose(hF);

             }

        }

       }

    }

}

 

return 0;

}

 

 

 

(3)DOS下运行得到的结果:

Bus#     Device#        Func#  Vendor     Device     Class      IRQ     IntPin

00H      1FH        2H      8086H      1E03H      0106H      10      2      即INTEL7 SATA AHCI CONTROLLER

 

得到的“设备配置空间”的内容:

8680031E0700B0020401060100000000

B1F00000A1F0000091F0000081F00000

61F00000006090F7000000004310AC10

0000000080000000000000000A020000

00800080000000000000000000000000

00000000000000000000000000000000

00000000000000000000000000000000

01A80340080000000000000000000000

 

05700000000000000000000000000000

603A05858301003A08425C0100000000

E00000003900000012B0100048000000

13000603000000000000000000000000

00000000000000000000000000000000

00000000000000000000000000000000

00000000000000000000000000000000

0000000000000000870F040800000000

(其余为00)

 

 

分析如下:

“设备配置空间”数据的0x10偏移处:

B1 F0 00 00    第0BIT的“1”表示映射到I/O空间,即这是IO地址,0xF0B0

A1 F0 00 00  

91 F0 00 00   

81 F0 00 00

61 F0 00 00

00 60 90 F7    第0BIT的“0”则表示映射到内存空间,即这是内存地址F7906000H,这个就是AHCI控制器的内存空间地址!!

 

在win7的设备管理器上,看SATA AHCI控制器资源是:

IO范围

F0B0-F0B7, ―― 这几个是端口地址,如果在BIOS中设置成“IDE兼容”模式,应该就可以像端口1F0H~1F7H那样用,没试过。

F0A0-F0A3, ―― 这几个是端口地址,如果在BIOS中设置成“IDE兼容”模式,应该就可以像端口3F6H~3F7H那样用,没试过。

F090-F097,

F080-F083,

F060-F07F

内存 00000000F7906000 - 00000000F79067FF ――内存空间地址也是F7906000H

IRQ 0x13

 

访问这个内存空间F7906000H,就是对AHCI控制器进行控制。

难题来了,这个内存地址是高于1MB的,DOS下如何访问???

 

文章评论0条评论)

登录后参与讨论
我要评论
0
14
关闭 站长推荐上一条 /2 下一条