荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: 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软件 网络书店