当前位置:首页 > PMON的pci设备初始化
第二章 PCI设备初始化
系统刚上电时,CPU从0xbfc0.0000开始执行。这个地址在Rom空间中,在完成TLB,Cache,UART等初始化后,CPU就将代码拷到0x8010.0000开始的RAM空间(这个地址是编译Pmon时分配符号_start的),然后跳转到initmips(),开始在内存空间的执行。
执行initmips之前,CPU做的初始化只是初步的,其作用只是为CPU在内存中运行做一些必要的准备。主要的初始化工作:PCI设备的扫描、空间映射、资源分配都是initmips()函数所完成的。
下面我们跟踪initmips的执行来观察系统初始化的过程。 void
initmips(unsigned int memsz) { /*
* Set up memory address decoders to map entire memory. * But first move away bootrom map to high memory. */
memorysize=(memsz&0x0000ffff) << 20;//recover to original size:256M memorysize_high=((memsz&0xffff0000)>>16) << 20;//0
/*
* Probe clock frequencys so delays will work properly. */
tgt_cpufreq();
SBD_DISPLAY(\ /*
* Init PMON and debug */
cpuinfotab[0] = &DBGREG; dbginit(NULL); /*
* Set up exception vectors. */
SBD_DISPLAY(\
bcopy(MipsException, (char *)TLB_MISS_EXC_VEC, MipsExceptionEnd - MipsException);
bcopy(MipsException, (char *)GEN_EXC_VEC, MipsExceptionEnd - MipsException);
CPU_FlushCache();
CPU_SetSR(0, SR_BOOT_EXC_VEC); SBD_DISPLAY(\ /* Launch! */
main(); }
首先是获取CPU的时钟频率,这是tgt_cpufreq()完成的。它定义在tgt_machdep.c中。主要的方法就是先读取COP0中的count寄存器,然后延时一段时间,再读取count寄存器。两次的差值乘以2就是这段时间内cpu的时钟周期数。另外,CMOS中有个实时钟,在延时前后读取当前时间该相减,就可以知道延时的准确时间。从而计算出cpu的时钟频率。全局变量md_cpufreq记录了cpu频率值,md_pipefreq是流水线的频率,它们的值分别是500MHZ和1000MHZ;
接着调用的是dbginit(),这是最要的一个函数,几乎所有的初始化代码都由他直接或间接调用。
? 构造函数(constructor)的执行。
Dbginit()调用的第一个函数是__init()。这个函数的过程很简单,它就是将所有的constructor的函数执行一遍,建立一些基本的数据结构。在pmon中有三类constructor函数,它们都是静态函数。
1) 命令处理初始化函数,位于pmon/cmds目录下,其名称都叫init_cmd()。 2) 文件系统初始化函数。pmon/fs目录下。函数名称叫init_fs()或者
init_xxxfs()。
3) 可执行文件类型初始化。在pmon/loader目录下。函数名称叫init_exec()
Pmon中定义了大量的命令,每个命令都对应一个Cmd类型的结构。该结构的含义如下。
typedef struct Cmd {
const char *name; //命令的名称 const char *opts; //参数
const Optdesc *optdesc; //命令参数的option const char *desc; //命令描述 int (*func) __P((int, char *[])); //处理函数 int minac; //最小参数个数 int maxac; //最大参数个数 int flag;
#define CMD_REPEAT 1 /* Command is repeatable */ #define CMD_HIDE 2 /* Command is hidden */
#define CMD_ALIAS 4 /* Alias for another command name */ } Cmd;
CmdTable是一个指针数组。对每一个命令,CmdTable中都有一个指针指向它对应的Cmd结构。Init_cmd()所作的就是将各个Cmd结构的地址填入CmdTable中。
Pmon中所支持的每一个文件系统都有一个相应的数据结构来表示。对于磁盘文件系统,这个结构是DiskFileSystem;对于其他文件系统,这个结构叫做FileSystem。这两个结构的成员主要是一些函数指针,分别指向文件系统的open,read,write,ioctl,lseek及close函数。
文件系统初始化就是代表各个文件系统的数据结构插入到相应链表。对于磁盘文件系统,链表的头指针式DiskFileSystems。对于其他的文件系统,头指针是FileSystems。这样当需要对某个文件操作(包括虚拟文件,如与用户交互的终端termio)。通过这两个链表可以找到相应的结构,在通过里面的函数指针就可以对文件进行具体操作了。
Pmon可以载入执行几种格式的文件。其中包括elf可执行文件,二进制文件(称为 Raw binary file)。对每一个支持的文件类型,有一个ExecType类型的的结构。这个结构定义如下:
typedef struct ExecType {
char *execname; /*文件类型,如bin ,elf 等*/
long (*loader) __P((int , char *, int *, int ));/*载入文件类型,并设置执行条 件*/ #define EXECFLAGS_NONE 0x0000 #define EXECFLAGS_NOAUTO 0x0001 /* Don't auto load */ int flags;
SLIST_ENTRY(ExecType) i_next; } ExecType;
由于每种执行文件的格式不一样,因此对它们的载入执行也不一样,函数指针loader就是文件的转载寒暑。对于二进制文件(bin),这个函数是load_bin。二进制文件是最简单的,load_bin所作的只不过就是将执行文件读入到指定地址。Elf文件比较复杂,它的载入函数是load_elf()。这个函数比较复杂,但过程还是很简单的,就是依据elf文件头的内容载入各个程序段的内容,并返回可执行文件开始执行的地址。
? 环境初始化
envint()设置所有的环境变量。最多可以设置64个环境变量。每个环境变量对应一个envpair结构。Envvar是一个envpair类型的数组。envinit()就是根据标准环境变量的值(数组stdenvtab)来初始化envvar数组。更改环境变量的值可以改变pmon中命令的行为以及一些系统参数。
? 设备初始化
设备初始化主要就是PCI设备的初始化。这是由tgt_devinit()完成的。tgt_devinit又调用了_pci_businit()。
PCI设备初始化分为两步,第一步是北桥初始化。第二步是设备初始化。在Pmon中,每个PCI设备都对应一个pci_device结构(包括pci-pci桥);每个pci总线都对应一个pci_bus结构;
struct pci_device {
struct pci_attach_args pa; //设备的一些信息,如id,class,中断线 // unsigned char min_gnt; unsigned char max_lat; unsigned char int_line; pcireg_t stat;
u_int8_t intr_routing[4]; struct pci_bridge bridge; struct pci_bus *pcibus; struct pci_device *next; struct pci_device *parent; };
Min_gnt,Max_lat是PCI设备和时间相关的参数。Min_gnt说明在33Mhz时钟频率下,一个burst period所要的时间。而Max_lat说明了设备访问PCI总线的频率。从它们的定义可以看出这两个参数和设备的带宽密切相关。
Stat则说明了设备所处的状态(是否可用,是否可以作为主设备等)。
由于所有的设备都连接在总线上,而总线又总是通过pci桥联在系统中,因此结构中还有一个pci_bridge结构bridge。如果该设备本身是一个pci桥,则bridge代表了它自身。
Pcibus指向设备所在的总线。所有的pci设备通过next指针形成一个链表。最后,parent指向该设备的父设备。
struct pci_bus {
struct pci_bus *next; /* next bus pointer */ u_int8_t min_gnt; /* largest min grant */ u_int8_t max_lat; /* smallest max latency */ u_int8_t devsel; /* slowest devsel */ u_int8_t fast_b2b; /* support fast b2b */ u_int8_t prefetch; /* support prefetch */ u_int8_t freq66; /* support 66MHz */ u_int8_t width64; /* 64 bit bus */ u_int8_t bus;
u_int8_t ndev; /* # devices on bus */ u_int8_t def_ltim; /* default ltim counter */ u_int8_t max_ltim; /* maximum ltim counter */ int32_t bandwidth; /* # of .25us ticks/sec @ 33MHz */ paddr_t minpcimemaddr; /* PCI allocation min mem for bus */ paddr_t nextpcimemaddr;/* PCI allocation max mem for bus */ paddr_t minpciioaddr; /* PCI allocation min i/o for bus */ paddr_t nextpciioaddr; /* PCI allocation max i/o for bus */ paddr_t pci_mem_base; paddr_t pci_io_base; };
可以看到pci_bus中很多成员和pci_device相同,这是因为pci_bus结构的信息是和它所连接的设备相关的。
对pci_bus结构要说明的地方是minpcimemadd,nextpcimemaddr,minpciioadd,nextpciioaddr。Pci设备所要的memory空间和IO空间都是有限的。Minpcimemaddr是PCI总线所能分配的最小memory地址;minpciioaddr是能分配的最小IO地址。Pmon才用的空间分配方法是从高地址到低地址。每次为设备分配mem空间都是从nextpcimemaddr开始往下分配,分配io空间同样是从
共分享92篇相关文档