这个部分没有分析过,先试试看,按照代码执行的顺序,现在应该是这个位置来了。
1. 直入主题吧,tgt_devinit函数
2. _pci_businit (int init),init的参数值是1
void
_pci_businit (int init)
{
char *v;
tgt_putchar('P');
v = getenv("pciverbose");
tgt_putchar('1');
if (v) {
_pciverbose = atol(v);
}
tgt_putchar('2');
/* intialise the PCI bridge */
if (/*init*/ 1) {
SBD_DISPLAY ("PCIH", CHKPNT_PCIH);
init = _pci_hwinit (init, &def_bus_iot, &def_bus_memt);
pci_roots = init;
SBD_DISPLAY ("PCIH", CHKPNT_PCIH);
if (init < 1)
return;
}
SBD_DISPLAY ("PCID", 0);
if(monarch_mode) {
int i;
struct pci_device *pb;
if (_pciverbose) {
printf("setting up %d bus\n", init);
}
for(i = 0, pb = _pci_head; i < pci_roots; i++, pb = pb->next) {
pb->bridge.pribus_num = i?++_pci_nbus:_pci_nbus;
_pci_scan_dev(pb, pb->bridge.pribus_num, 0, init); //参数2:0,0,1
}
_setup_pcibuses(init);
}
}
这个函数我把它分成3个部分吧。分别就是最外层的3个if
2.1 第一个if 获得环境变量pciverbose,这个就是控制pci打印信息的。
如果不想有打印信息,设置pciverbose 为0,如果需要调试信息,设置大于0,默认值好像是5.
不配置的时候,环境变量默认就是5,这时候在pmon启动阶段是会打印详细信息的
这样的好处就是不用重新编译程序,就可以控制pci的信息,挺好的。修改环境变量的值就好。
2.2 第二个if ,条件为1,必须执行
好像写init似乎也没毛病。呵呵!!,但是里面有一个init的判断,小于1,返回。
这个块里面,两条打印语句PCIH,
一个函数 _pci_hwinit,参数,1,两个全局变量的地址。
结构体也简单,两个32位的无符号数。(先放这吧,可能后面要用到)
一个全局变量,pci_roots = init ,就是1了。指的是cpu出来有pci总线的条数。
2.3 _pci_hwinit 函数
这个函数非常奇怪的是没有使用传进来的两个参数:iot和memt
这个函数初始化了struct pci_deice和struct pci_bus两个这样结构。
同时这两个结构赋值给全局指针变量_pci_head 和 _pci_bushead,这也分别是这两个链表的第一个元素。全局变量就是链表的头指针。
还有几个全局变量:bus_dmamap_tag 似乎是DMA相关的一些操作的集合。(函数指针)
bus_dmamap_tag._dmamap_offs = 0; //可能是指偏移地址。
pci_probe_only 由环境变量设置,环境变量无设置时为0
_pci_bus[]; struct pci_deice指针数组,大小16个元素。 这里已经使用了0这个下标元素
_max_pci_bus : 指定_pci_bus的下标,这里由0增加为1.
/*set pci base0 address and window size*/
pci_local_mem_pci_base = 0x0; //似乎是个偏移地址??
/****************************/
/*initial PCI */
/****************************/
int
_pci_hwinit(initialise, iot, memt)
int initialise;
bus_space_tag_t iot;
bus_space_tag_t memt;
{
/*pcireg_t stat;*/
struct pci_device *pd;
struct pci_bus *pb;
int newcfg=0;
char *env;
SBD_DISPLAY ("HW-0", 0);
if(getenv("newcfg"))newcfg=1; //根据环境变量取值
if ((env = getenv("pci_probe_only"))) //环境变量取值
pci_probe_only = strtoul(env, 0, 0);
if (!initialise) { //参数为1,不成立。
return(0);
}
SBD_DISPLAY ("HW-1", 0);
/*
* Allocate and initialize PCI bus heads.
*/
/*
* PCI Bus 0
*/
pd = pmalloc(sizeof(struct pci_device)); //分配空间
pb = pmalloc(sizeof(struct pci_bus)); //分配空间
if(pd == NULL || pb == NULL) {
printf("pci: can't alloc memory. pci not initialized\n");
return(-1);
}
SBD_DISPLAY ("HW-2", 0);
//对结构体进行初始化,这是pd,device设备
pd->pa.pa_flags = PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED;
pd->pa.pa_iot = pmalloc(sizeof(bus_space_tag_t));
pd->pa.pa_iot->bus_reverse = 1;
pd->pa.pa_iot->bus_base = BONITO_PCIIO_BASE_VA; //0xbfd0,0000,所有的io都在这1M的空间内
pd->pa.pa_memt = pmalloc(sizeof(bus_space_tag_t));
pd->pa.pa_memt->bus_reverse = 1;
pd->pa.pa_memt->bus_base = 0xc0000000;
pd->pa.pa_dmat = &bus_dmamap_tag;
pd->bridge.secbus = pb;
_pci_head = pd; //全局变量保存这个结构体的首地址
SBD_DISPLAY ("HW-3", 0);
if(pci_probe_only)
{
pb->minpcimemaddr = 0x40100000;
pb->nextpcimemaddr = 0x80000000;
}
else
{
pb->minpcimemaddr = 0x10000000;
pb->nextpcimemaddr = 0x18000000;
}
pb->minpciioaddr = PCI_IO_SPACE_BASE+0x0004000;
pb->nextpciioaddr = PCI_IO_SPACE_BASE+ BONITO_PCIIO_SIZE; // 0 + 0x0200_0000
pb->pci_mem_base = BONITO_PCILO_BASE_VA;
pb->pci_io_base = BONITO_PCIIO_BASE_VA;
#if 0 //low pci mem base
pd->pa.pa_memt->bus_base = 0xa0000000;
pb->minpcimemaddr = 0x10000000;
pb->nextpcimemaddr = 0x17ffffff;
pb->minpciioaddr = 0x18000000;
pb->nextpciioaddr = 0x1800ffff;
pb->pci_mem_base = 0x10000000;
pb->pci_io_base = 0x18100000;
#endif
pb->max_lat = 255;
pb->fast_b2b = 1;
pb->prefetch = 1;
pb->bandwidth = 4000000;
pb->ndev = 1;
_pci_bushead = pb;
_pci_bus[_max_pci_bus++] = pd;
SBD_DISPLAY ("HW-5", 0);
bus_dmamap_tag._dmamap_offs = 0;
/*set pci base0 address and window size*/
pci_local_mem_pci_base = 0x0;
return(1);
}
宏定义 PCI_IO_SPACE_BASE 为0
BONITO_PCILO_BASE_VA 为0xb000,0000
BONITO_PCIIO_BASE_VA 为0xbfd0,0000
2.4 第三个if块,这里调用的函数比较复杂,到下次的内容再分析函数吧。
因为pci_roots = 1所以for循环执行了一次。即i=0
_pci_scan_dev 。这个函数的功能就是枚举pci总线上的所有的设备(包括pci桥片)。
特别说明一下,需要了解一些pci的基本知识。我之前也是没接触过,现在大概知道了一点概念,所以我分析的也不一定正确。请各位多多指教。
第一次调用的时候,第一个参数就是刚刚初始化的struct pci_bus的头指针,第二个参数总线号,现在是0,第三个参数0,第四个参数1.
这里对device做循环。
_pci_query_dev 还需要进一步分析。
_setup_pcibuses(init):这个函数功能是分配带宽并处理空间申请。(后面再看吧。挖个坑先)。
2.5 _pci_query_dev函数
static void
_pci_query_dev (struct pci_device *dev, int bus, int device, int initialise)
{
pcitag_t tag;
pcireg_t id;
pcireg_t misc;
tag = _pci_make_tag(bus, device, 0);
if (!_pci_canscan (tag))
return;
if (_pciverbose >= 2)
_pci_bdfprintf (bus, device, -1, "probe...");
#if defined(LOONGSON_2G5536)||defined(LOONGSON_2G1A) || defined(LOONGSON_2F1A) || defined(LOONGSON_2K)
id = _pci_conf_read(tag, PCI_ID_REG); //读id寄存器
if (_pciverbose >= 2) {
PRINTF ("2222completed\n"); //打印这里
}
#else //else 不执行
#ifdef CONFIG_LSI_9260
if ((bus == 2 && device == 0) ||(bus == 3 && device == 0) ||
(bus == 4 && device == 0))
delay(2000000);
#endif
delay(1000); //fix that the correct id sometimes can not read;
id = _pci_conf_read(tag, PCI_ID_REG);
if (_pciverbose >= 2) {
PRINTF ("completed\n");
}
if((bus == 2) && (device ==0))
{
if(PCI_VENDOR(id) != 0x1002 && (PCI_PRODUCT(id) != 0x9615))
printf("pcie-slot device: vendor:%x product:%x\n",PCI_VENDOR(id),PCI_PRODUCT(id));
}
#if defined(USE_BMC)
if (PCI_VENDOR(id) == 0x1a03 && PCI_PRODUCT(id) == 0x2000)
{
printf("AST2050's VGA discover !!!!!!!!!!!!!!\n");
}
#endif
#endif //else 不执行
if (id == 0 || id == 0xffffffff) { //没有读到id就返回了。说明没有挂载设备
return;
}
misc = _pci_conf_read(tag, PCI_BHLC_REG); //读misc寄存器(地址0xc)
if (PCI_HDRTYPE_MULTIFN(misc)) { //根据读取的值判断是否为多功能设备
int function;
for (function = 0; function < 8; function++) { //从0开始枚举
tag = _pci_make_tag(bus, device, function);
id = _pci_conf_read(tag, PCI_ID_REG);
if (id == 0 || id == 0xffffffff) {
//return;
continue;
}
_pci_query_dev_func (dev, tag, initialise); //初始化结构体,并加入到列表中
}
}
else { //不是多功能设备
_pci_query_dev_func (dev, tag, initialise);
}
}
#else ....#endif 那一段是没有执行的,所以实际代码也不多。
_pci_query_dev_func 函数是关键。
2.6 _pci_query_dev_func 函数
这个函数本身可以递归了。
参数:dev 一般就是 pci_device 的结构体指针了。第一次调用的时候是那个链表的头部_pci_head.
tag 由bus,device,func组合的一个数字
initialise 1 。
2.6.1 根据读取设备的数据,初始化结构体(struct pci_device)。
printf("03-16 dev = %p tag = %#x\n",dev,tag);
class = _pci_conf_read(tag, PCI_CLASS_REG); //读取class寄存器(0x8),用于区分是桥还是设备
id = _pci_conf_read(tag, PCI_ID_REG); //读出id,包含厂家信息
if (_pciverbose) { //打印信息
int supported;
char devinfo[256];
_pci_devinfo(id, class, &supported, devinfo);
_pci_tagprintf (tag, "%s\n", devinfo);
}
pd = pmalloc(sizeof(struct pci_device));
if(pd == NULL) {
PRINTF ("pci: can't alloc memory for device\n");
return;
}
_pci_break_tag (tag, &bus, &device, &function); //把tag分解为bus,device,function
//printf("2022-03-16 bus = %d device = %d func = %d\n",bus, device, function);
pd->pa.pa_bus = bus; //结构体初始化,设备所在的总线号
pd->pa.pa_device = device; //设备所在的设备号
pd->pa.pa_function = function; //设备所在的功能号
pd->pa.pa_tag = tag; //tag也一起保存起来
pd->pa.pa_id = id; //id也保存
pd->pa.pa_class = class; //class也保存
pd->pa.pa_flags = PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED; //使能标志
pd->pa.pa_iot = dev->pa.pa_iot; //指向同一个位置,
pd->pa.pa_memt = dev->pa.pa_memt; //指向同一个位置,
pd->pa.pa_dmat = dev->pa.pa_dmat; //指向同一个位置,
pd->parent = dev; //父节点
pd->pcibus = dev->bridge.secbus; //指向同一个位置,头节点是指向bus0,
pb = pd->pcibus; //bus指针 ,头节点是指向bus0
_pci_device_insert(dev, pd); //加入到链表中
/*
* Calculated Interrupt routing
*/
_pci_setupIntRouting(pd); //计算中断路由。。。暂时放一边
/*
* Shut off device if we initialize from non reset.
*/
stat = _pci_conf_read(tag, PCI_COMMAND_STATUS_REG); //读命令寄存器(0x4)
stat &= ~(PCI_COMMAND_MASTER_ENABLE |
PCI_COMMAND_IO_ENABLE |
PCI_COMMAND_MEM_ENABLE); //去掉某些位(bit0,1,2)。
#ifdef USE_SM502_UART0
if(device!=14)
{
#endif
_pci_conf_write(tag, PCI_COMMAND_STATUS_REG, stat); //写回
#ifdef USE_SM502_UART0
}
#endif
pd->stat = stat;
/* do all devices support fast back-to-back */ //以下根据命令寄存器的值进一步初始化,注意设置的是pb
if ((stat & PCI_STATUS_BACKTOBACK_SUPPORT) == 0) {
pb->fast_b2b = 0; /* no, sorry */ //连续传输,一个设备不支持,总线就不支持
}
/* do all devices run at 66 MHz */
if ((stat & PCI_STATUS_66MHZ_SUPPORT) == 0) {
pb->freq66 = 0; /* no, sorry */ //一个设备不支持,总线就不支持
}
/* find slowest devsel */
x = stat & PCI_STATUS_DEVSEL_MASK;
if (x > pb->devsel) {
pb->devsel = x; //x:0表示快速,1表示中等速度,2表示慢速设备
}
/* Funny looking code which deals with any 32bit read only cfg... */ //根据寄存器的值,设置结构体
bparam = _pci_conf_read(tag, (PCI_MINGNT & ~0x3));
pd->min_gnt = 0xff & (bparam >> ((PCI_MINGNT & 3) * 8));
bparam = _pci_conf_read(tag, (PCI_MAXLAT & ~0x3));
pd->max_lat = 0xff & (bparam >> ((PCI_MAXLAT & 3) * 8));
if (pd->min_gnt != 0 || pd->max_lat != 0) {
/* find largest minimum grant time of all devices */
if (pd->min_gnt != 0 && pd->min_gnt > pb->min_gnt) {
pb->min_gnt = pd->min_gnt;
}
/* find smallest maximum latency time of all devices */
if (pd->max_lat != 0 && pd->max_lat < pb->max_lat) {
pb->max_lat = pd->max_lat;
}
/* subtract our min on-bus time per second from bus bandwidth */
if (initialise) {
pb->bandwidth -= pd->min_gnt * 4000000 / (pd->min_gnt + pd->max_lat);
}
}
/* Map interrupt to interrupt line (software function only) */ //读取寄存器中中断相关设置
bparam = _pci_conf_read(tag, PCI_INTERRUPT_REG);
bparam &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
bparam |= ((_pci_getIntRouting(pd) & 0xff) << PCI_INTERRUPT_LINE_SHIFT);
_pci_conf_write(tag, PCI_INTERRUPT_REG, bparam);
2.6.2 对pci桥的特殊处理,还有IDE硬盘的处理
IDE硬盘处理就不继续向后执行了,但是桥的话,还将继续初始化。
/*
* Check to see if device is a PCI Bridge
*/
if (PCI_ISCLASS(class, PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI)) {
struct pci_device *pcidev;
struct pci_win *pm_mem = NULL;
struct pci_win *pm_io = NULL;
struct pci_win *pm;
pcireg_t tmp;
isbridge = 1;
printf("2022-03-15 device is a PCI Bridge\n");
pd->bridge.pribus_num = bus;
#if 1
_pci_nbus = pci_get_busno(pd, _pci_nbus); //获得pci总线号,这里不是连续的
pd->bridge.secbus_num = _pci_nbus;
#else
pd->bridge.secbus_num = ++_pci_nbus;
#endif
/* Set it temperary to same as secondary bus number */
pd->bridge.subbus_num = pd->bridge.secbus_num; //桥,设置所在的总线号
tmp = _pci_conf_read(tag, PCI_PRIBUS_1); //设置桥的编号信息
tmp &= 0xff000000;
tmp |= pd->bridge.pribus_num;
tmp |= pd->bridge.secbus_num << 8;
tmp |= pd->bridge.subbus_num << 16;
_pci_conf_write(tag, PCI_PRIBUS_1, tmp);
/* Update sub bus number */
for(pcidev = dev; pcidev != NULL; pcidev = pcidev->parent) { //把自己的总线号更新到父桥的最大总线号
//printf("pcidev = 0x%x\n", pcidev);
pcidev->bridge.subbus_num = pd->bridge.secbus_num;
tmp = _pci_conf_read(pcidev->pa.pa_tag, PCI_PRIBUS_1);
tmp &= 0xff00ffff;
tmp |= pd->bridge.secbus_num << 16;
if(pcidev->parent)
_pci_conf_write(pcidev->pa.pa_tag, PCI_PRIBUS_1, tmp);
}
pd->bridge.secbus = pmalloc(sizeof(struct pci_bus)); //新的总线结构体
if(pd->bridge.secbus == NULL) {
PRINTF ("pci: can't alloc memory for new pci bus\n");
return;
}
//初始化新的结构体
pd->bridge.secbus->max_lat = 255;
pd->bridge.secbus->fast_b2b = 1;
pd->bridge.secbus->prefetch = 1;
pd->bridge.secbus->freq66 = 1;
pd->bridge.secbus->bandwidth = 4000000; //64M
pd->bridge.secbus->ndev = 1;
pd->bridge.secbus->bus = pd->bridge.secbus_num;
_pci_bus_insert(pd->bridge.secbus); //插入到总线链表末尾
{
extern struct pci_device *_pci_bus[];
extern int _max_pci_bus;
_pci_bus[_max_pci_bus++] = pd; //数组增加,下标值加1
}
set_pcie_port_type(pd); //....
/* Scan secondary bus of the bridge */
_pci_scan_dev(pd, pd->bridge.secbus_num, 0, initialise); //开始递归搜索
/*
* Sum up the address space needed by secondary side of bridge
*/
if(pm_io == NULL) {
pm_io = pmalloc(sizeof(struct pci_win));
if(pm_io == NULL) {
PRINTF ("pci: can't alloc memory for pci memory window\n");
return;
}
pm_io->device = pd;
pm_io->reg = PCI_IOBASEL_1;
pm_io->flags = PCI_MAPREG_TYPE_IO;
}
if(pm_mem == NULL) {
pm_mem = pmalloc(sizeof(struct pci_win));
if(pm_mem == NULL) {
PRINTF ("pci: can't alloc memory for pci memory window\n");
return;
}
pm_mem->device = pd;
pm_mem->reg = PCI_MEMBASE_1;
pm_mem->flags = PCI_MAPREG_MEM_TYPE_32BIT;
}
/* Sum up I/O Space needed */
{
int max=0;
for(pm = pd->bridge.iospace; pm != NULL; pm = pm->next) {
if(max < pm->align)
max = pm->align;
pm_io->size += pm->size;
}
pm_io->size = (pm_io->size + max -1) & ~(max-1);
if(max < 0x1000) max = 0x1000;
pd->bridge.io_mask = ~(max-1);
pm_io->align = max;
}
/* Sum up Memory Space needed */
{
int max = 0;
for(pm = pd->bridge.memspace; pm != NULL; pm = pm->next) {
if(max < pm->align)
max = pm->align;
pm_mem->size += pm->size;
}
pm_mem->size = (pm_mem->size + max -1) & ~(max-1);
if(max<0x100000) max = 0x100000;
pd->bridge.mem_mask = ~(max-1);
pm_mem->align = max;
}
if(pm_io) {
/* Round to minimum granularity requierd for a bridge */
pm_io->size = _pci_roundup(pm_io->size, 0x1000);
_insertsort_window(&pd->parent->bridge.iospace, pm_io);
}
if(pm_mem) {
/* Round to minimum granularity requierd for a bridge */
pm_mem->size = _pci_roundup(pm_mem->size, 0x100000);
_insertsort_window(&pd->parent->bridge.memspace,pm_mem);
}
}
else if (PCI_ISCLASS(class, PCI_CLASS_MASS_STORAGE, PCI_SUBCLASS_MASS_STORAGE_IDE) &&
dev->bridge.secbus->minpciioaddr == 0) {
/*
* There is no need to setup memory regions for IDE storage devices
* but only if PCI/ISA I/O space is accessables
*/
return;
}
2.6.3 继续桥和设备的初始化部分,根据bar计算存储类型和空间
//set BAR for this dev
{
int skipnext = 0;
#if defined(LOONGSON_2K) || defined(LS7A)
/* skip bus 0, device 0, function 0 */
if (pd->pa.pa_tag != 0 && !PCI_ISCLASS(class, PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_PCI)) {
pcie_write_mps(pd);
pcie_write_mrrs(pd);
}
#endif
for (reg = PCI_MAPREG_START; reg < (isbridge ? PCI_MAPREG_PPB_END : PCI_MAPREG_END); reg += 4) {
struct pci_win *pm;
if (skipnext) { //对于64位的memspace ,跳过一个寄存器
skipnext = 0;
continue;
}
old = _pci_conf_read(tag, reg); //for循环内,reg不断变化,返回的值也是不同的
_pci_conf_write(tag, reg, 0xfffffffe);
mask = _pci_conf_read(tag, reg);
_pci_conf_write(tag, reg, old); //返回的mask用来判断分配什么类型空间。
//有些寄存器mask为0,表示不需要分配,直接跳过
if (mask == 0 || mask == 0xffffffff) {
continue;
}
if (_pciverbose >= 3) {
_pci_tagprintf (tag, "reg 0x%x = 0x%x\n", reg, mask);
}
if (PCI_MAPREG_TYPE(mask) == PCI_MAPREG_TYPE_IO) { //最低位为1,表示io空间
//for IO bar, device is free to hardwire high 16 bits to zero, here we mask high 16 bits to all 1
//for convenience of size calculation
mask |= 0xffff0000;
pm = pmalloc(sizeof(struct pci_win));
if(pm == NULL) {
PRINTF ("pci: can't alloc memory for pci memory window\n");
return;
}
pm->device = pd;
pm->reg = reg; //起始地址?
pm->flags = PCI_MAPREG_TYPE_IO;
pm->size = -(PCI_MAPREG_IO_ADDR(mask));
pm->align = pm->size;
_insertsort_window(&pd->parent->bridge.iospace, pm);
printf("03-16 iospace size = 0x%x reg = 0x%x\n",pm->size,reg);
}
else { //内存空间
switch (PCI_MAPREG_MEM_TYPE(mask)) {
case PCI_MAPREG_MEM_TYPE_32BIT:
case PCI_MAPREG_MEM_TYPE_32BIT_1M:
break;
case PCI_MAPREG_MEM_TYPE_64BIT:
_pci_conf_write(tag, reg + 4, 0x0);
printf("03-16 PCI_MAPREG_MEM_TYPE_64BIT\n");
skipnext = 1; //两个32位当一个,接下的那个32位也用了
break;
default:
_pci_tagprintf (tag, "reserved mapping type 0x%x\n",
PCI_MAPREG_MEM_TYPE(mask));
continue;
}
if (!PCI_MAPREG_MEM_PREFETCHABLE(mask)) {
pb->prefetch = 0;
}
pm = pmalloc(sizeof(struct pci_win));
if(pm == NULL) {
PRINTF ("pci: can't alloc memory for pci memory window\n");
return;
}
pm->device = pd;
pm->reg = reg;
pm->flags = PCI_MAPREG_MEM_TYPE_32BIT;
pm->size = -(PCI_MAPREG_MEM_ADDR(mask));
pm->align = pm->size;
_insertsort_window(&pd->parent->bridge.memspace, pm); //插入到有序的链表中
printf("03-16 memspace size = 0x%x reg = 0x%x\n",pm->size,reg);
}
}
printf("03-16 end for\n");
{ //扩展存储空间,一般没有使用到。
/* Finally check for Expansion ROM */
reg = isbridge ? PCI_MAPREG_PPB_ROM : PCI_MAPREG_ROM;
old = _pci_conf_read(tag, reg);
_pci_conf_write(tag, reg, 0xfffffffe);
mask = _pci_conf_read(tag, reg);
_pci_conf_write(tag, reg, old);
printf("03-16-2 reg 0x%x = 0x%x\n", reg, mask);
if (mask != 0 && mask != 0xffffffff) {
struct pci_win *pm;
if (_pciverbose >= 3) {
_pci_tagprintf (tag, "reg 0x%x = 0x%x\n", reg, mask);
}
pm = pmalloc(sizeof(struct pci_win));
if(pm == NULL) {
PRINTF ("pci: can't alloc memory for pci memory window\n");
return;
}
pm->device = pd;
pm->reg = reg;
pm->size = -(PCI_MAPREG_ROM_ADDR(mask));
pm->align = pm->size; //记录了空间的大小
_insertsort_window(&pd->parent->bridge.memspace, pm); //插入到有序的链表中
}
}
}