荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: jjk (Linux Device Driver), 信区: Linux
标 题: PCI BIOS与Linux(转寄)[转载]
发信站: 荔园晨风BBS站 (Wed May 15 13:15:42 2002), 转信
【 以下文字转载自 jjk 的信箱 】
【 原文由 jjk.bbs@apue.dhs.org 所发表 】
发信人: ysqcn (岁月无声), 信区: UKP
标 题: PCI BIOS与Linux
发信站: UNIX编程 (2001年12月14日17:26:08 星期五), 转信
参考书籍:<<Linux内核源代码情景分析>> <<PCI SYSTEM ARCHITECTURE>>
PCI BIOS支持三种类型的OS:
1、实模式OS(MS-DOS)
2、286保护模式(不再使用)
3、386保护模式
一、实模式
通过int 0x1ah来调用,或者直接在物理存储器位置0x000ffe6eh调用BIOS入口点
二、386保护模式
1、BIOS32
在32位OS环境中,不使用int 来调用BIOS,系统首先要发现BIOS的入口点,然后调用
程序对BIOS入口点执行一个远程调用。 BIOS规范规定, OS必须扫描0x000e0000h至
0x00ffff0h范围内的物理存储区域,寻找一个16字节的数据结构,该数据结构必须排
列在16字节地址边界。其数据结构描述如下:
第0-3字节: ASCII标记"_32_",最左边的下标存储在偏移0处,而最右边的下标存储
在偏移3中。
第4-7字节: 32位BIOS服务程序的32位入口点。调用服务目录程序可以确定32位BIOS
提供那些服务
第8字节: 数据结构的版本号(目前为0)
第9字节: 数据结构的16字节长度。目前,该结构被定义为16字节长,故为1
第10字节: 数据结构中所有字节的校验和。
第11-15字节:保留
2、确定BIOS32是否支持相应的服务调用
在远调用BIOS32服务之前,需要传入如下参数:
.EAX 4字节的服务标识(Service Identifier),BIOS32根据提供的服务标识来查找
是否支持指定的服务,例如要判断是否支持PCI BIOS服务,需要传入的服务标识
为“$PCI”。
.BL 目前只定义为0,其含义为Service Directory Function Identifier。
.EBX 最高3字节必须清零
调用后返回:
AL = 0 指定的服务存在,此时
EBX:指定的BIOS服务的物理起始地址
ECX:指定的BIOS服务的长度
EDX:指定的BIOS服务的起始地址的偏移
AL = 80 指定的服务不存在
AL = 81 BL指定的Function没有实现
3、调用PCI BIOS的服务
如果BIOS32支持PCI BIOS服务调用,则可以使用与上面相似的方法来调用PCI BIOS服务程
序,具体方法是:
.AH 寄存器必须包含PCI功能ID B1h
.AL 寄存器必须包含PCI下级功能代号
.对具体的一些调用设置其它相应的寄存器参数
.然后执行一个远程过程调用
PCI BIOS功能部分列表
AL值 描 述
01h PCI BIOS存在检测
02h 使用 制造商ID/设备ID 发现PCI设备
03h 查找指定类别的PCI设备
06h 发生专用周期交易
08h 读配置字节
09h 读配置字
0ah 读配置双字
0bh 写配置字节
0ch 写配置字
0dh 写配置双字
0eh 获得中断路由器
0fh 设置(分配)PCI中断
三、Linux的相关代码实现
/*
* This is the standard structure used to identify the entry point
* to the BIOS32 Service Directory, as documented in
* Standard BIOS 32-bit Service Directory Proposal
* Revision 0.4 May 24, 1993
* Phoenix Technologies Ltd.
* Norwood, MA
* and the PCI BIOS specification.
*/
union bios32 {
struct {
unsigned long signature; /* _32_ */
unsigned long entry; /* 32 bit physical address */
unsigned char revision; /* Revision level, 0 */
unsigned char length; /* Length in paragraphs should be 01 */
unsigned char checksum; /* All bytes must add up to zero */
unsigned char reserved[5]; /* Must be zero */
} fields;
char chars[16];
};
/* BIOS32 signature: "_32_" */
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
/* PCI signature: "PCI " */
#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24))
/* PCI service signature: "$PCI" */
#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24))
/*
* Try to find PCI BIOS.
*/
static struct pci_ops * __init pci_find_bios(void)
{
union bios32 *check;
unsigned char sum;
int i, length;
/*
* Follow the standard procedure for locating the BIOS32 Service
* directory by scanning the permissible address range from
* 0xe0000 through 0xfffff for a valid BIOS32 structure.
*/
>>> 以16字节递增的顺序扫描物理地址0xe0000到0xffff0,查找BIOS32标签
for (check = (union bios32 *) __va(0xe0000);
check <= (union bios32 *) __va(0xffff0);
++check) {
>>> 标记不为"_32_"
if (check->fields.signature != BIOS32_SIGNATURE)
continue;
length = check->fields.length * 16;
>>> BIOS32数据结构长度为0
if (!length)
continue;
>>> 16字节相加是否为0(校验和为零)
sum = 0;
for (i = 0; i < length ; ++i)
sum += check->chars[i];
if (sum != 0)
continue;
>>> 版本不为0
if (check->fields.revision != 0) {
printk("PCI: unsupported BIOS32 revision %d at 0x%p, report to <mj@suse.cz>
\n",
check->fields.revision, check);
continue;
}
>>> 找到BIOS32的入口地址
DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check);
>>> BIOS32必须位于1M之下
if (check->fields.entry >= 0x100000) {
printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check);
return NULL;
} else {
>>> BIOS32的入口地址
unsigned long bios32_entry = check->fields.entry;
DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
>>> BIOS32入口地址页面对齐
bios32_indirect.address = bios32_entry + PAGE_OFFSET;
if (check_pcibios())
return &pci_bios_access;
}
break; /* Hopefully more than one BIOS32 cannot happen... */
}
return NULL;
}
/*
* Physical address of the service directory. I don't know if we're
* allowed to have more than one of these or not, so just in case
* we'll make pcibios_present() take a memory start parameter and store
* the array there.
*/
static struct {
unsigned long address;
unsigned short segment;
} bios32_indirect = { 0, __KERNEL_CS };
>>> 调用BIOS32,确定是否支持相应的服务(由长字service指定)
/*
* Returns the entry point for the given service, NULL on error
*/
static unsigned long bios32_service(unsigned long service)
{
unsigned char return_code; /* %al */
unsigned long address; /* %ebx */
unsigned long length; /* %ecx */
unsigned long entry; /* %edx */
unsigned long flags;
__save_flags(flags); __cli();
>>> 长字service放入EAX寄存器,EBX=0
__asm__("lcall (%%edi); cld"
: "=a" (return_code),
"=b" (address),
"=c" (length),
"=d" (entry)
: "0" (service),
"1" (0),
"D" (&bios32_indirect));
__restore_flags(flags);
switch (return_code) {
>>> 相应的服务存在,地址为EBX+EDX
case 0:
return address + entry;
case 0x80: /* Not present */
printk("bios32_service(0x%lx): not present\n", service);
return 0;
default: /* Shouldn't happen */
printk("bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n",
service, return_code);
return 0;
}
}
#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
#define PCIBIOS_FIND_PCI_DEVICE 0xb102
#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103
#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106
#define PCIBIOS_READ_CONFIG_BYTE 0xb108
#define PCIBIOS_READ_CONFIG_WORD 0xb109
#define PCIBIOS_READ_CONFIG_DWORD 0xb10a
#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b
#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
#define PCIBIOS_GET_ROUTING_OPTIONS 0xb10e
#define PCIBIOS_SET_PCI_HW_INT 0xb10f
static int __init check_pcibios(void)
{
u32 signature, eax, ebx, ecx;
u8 status, major_ver, minor_ver, hw_mech;
unsigned long flags, pcibios_entry;
>>> 确定是否支持PCI BIOS服务,返回不为0,则支持
if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
>>> PCI BIOS服务的入口地址
pci_indirect.address = pcibios_entry + PAGE_OFFSET;
>>> 检查PCI BIOS是否存在,传入参数AH=b1h AL=01h
__save_flags(flags); __cli();
__asm__(
"lcall (%%edi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=d" (signature),
"=a" (eax),
"=b" (ebx),
"=c" (ecx)
: "1" (PCIBIOS_PCI_BIOS_PRESENT),
"D" (&pci_indirect)
: "memory");
__restore_flags(flags);
>>> 下面是处理PCI BIOS 01号功能调用后的返回值
status = (eax >> 8) & 0xff;
hw_mech = eax & 0xff;
major_ver = (ebx >> 8) & 0xff;
minor_ver = ebx & 0xff;
if (pcibios_last_bus < 0)
pcibios_last_bus = ecx & 0xff;
DBG("PCI: BIOS probe returned s=%02x hw=%02x ver=%02x.%02x l=%02x\n",
status, hw_mech, major_ver, minor_ver, pcibios_last_bus);
if (status || signature != PCI_SIGNATURE) {
printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found, report to <mj@suse.cz>\n",
status, signature);
return 0;
}
printk("PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n",
major_ver, minor_ver, pcibios_entry, pcibios_last_bus);
#ifdef CONFIG_PCI_DIRECT
if (!(hw_mech & PCIBIOS_HW_TYPE1))
pci_probe &= ~PCI_PROBE_CONF1;
if (!(hw_mech & PCIBIOS_HW_TYPE2))
pci_probe &= ~PCI_PROBE_CONF2;
#endif
return 1;
}
return 0;
}
四、调用PCI BIOS服务
虽然Linux并不推荐这样做了,但可以看看它的实现,举一个例子,其它类似:
static int pci_bios_read_config_byte(struct pci_dev *dev, int where, u8 *value)
{
unsigned long ret;
unsigned long bx = (dev->bus->number << 8) | dev->devfn;
__asm__("lcall (%%esi); cld\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=c" (*value),
"=a" (ret)
>>> 功能号 AH= BLh AL=08H
: "1" (PCIBIOS_READ_CONFIG_BYTE),
"b" (bx),
"D" ((long) where),
"S" (&pci_indirect));
return (int) (ret & 0xff00) >> 8;
}
--
※ 修改:·ysqcn 於 12月14日17:29:44 修改本文·[FROM: 202.114.2.19]
※ 来源:·UNIX编程 apue.dhs.org·[FROM: 202.114.2.19]
--
※ 转寄:·UNIX编程 apue.dhs.org·[FROM: 210.39.3.50]
--
※ 转载:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.146]
[回到开始]
[上一篇][下一篇]
荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店