device.cc device_init() { autodev_init(); } autodev.cc autodev_init() { device_register("ns16550", devinit_ns16550); device_entries[n_device_entries].initf = initf; pci_register("rtl8139c", pciinit_rtl8139c); pci_entries[n_pci_entries].initf = initf; }
autodev.cc是由makeautodev.sh自动生成的,autodev_init完成注册device模块
device.cc device_add() { 查找device是否已经注册 p = device_lookup(devinit.name); 解析参数, device的参数是以字符串的形式传入 地址 devinit.addr = mystrtoull(s3, NULL, 0); devinit.addr2 = mystrtoull(s3, NULL, 0); 长度 devinit.len = mystrtoull(s3, NULL, 0); devinit.addr_mult = mystrtoull(s3, NULL, 0); PCI信息 devinit.pci_little_endian = mystrtoull(s3, NULL, 0); 中断信息 snprintf(devinit.interrupt_path, interrupt_path_len, "%s", s3); devinit.in_use = mystrtoull(s3, NULL, 0); snprintf(devinit.name2, len + 1, "%s", h); 初始化device,就是调用的device_init中注册的初始化函数 p->initf(&devinit) }
device的初始化函数在各个device文件中通过device.h中的宏定义:
#define DEVINIT(name) int devinit_ ## name (struct devinit *devinit)
device的初始化主要完成device中断的分配和device内存地址的注册及一些与device相关的设置,以ns16550为例:
DEVINIT(ns16550) { 中断分配 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); 为device注册内存地址,并将访问内存地址的函数注册进去 memory_device_register(devinit->machine->memory, name, devinit->addr, DEV_NS16550_LENGTH * d->addrmult, dev_ns16550_access, d, DM_DEFAULT, NULL); tick函数,主要是为触发中断使用 machine_add_tickfunction(devinit->machine, dev_ns16550_tick, d, TICK_SHIFT); 其它的相关设置 prepare_ns16550_device(d); }
虚拟机通过mips_memory_rw访问内存,在其中会先检查访问的地址是不是device地址,如果是的话就会找到device地址并访问见"内存”章节:
int MEMORY_RW(struct cpu *cpu, struct memory *mem, uint64_t vaddr, unsigned char *data, size_t len, int writeflag, int misc_flags) { … if (paddr >= mem->mmap_dev_minaddr && paddr < mem->mmap_dev_maxaddr) { 初始化要扫描的device number start = 0; end = mem->n_mmapped_devices - 1; i = mem->last_accessed_device; 遍历所有deivce地址 do { if (paddr >= mem->devices[i].baseaddr && paddr < mem->devices[i].endaddr) { paddr -= mem->devices[i].baseaddr; 找到device,执行device的访问(即访问device寄存器,内部FIFO等),在访问函数内部使用的是相对地址 res = mem->devices[i].f(cpu, mem, paddr, data, len, writeflag, mem->devices[i].extra); } 二分法查找 if (paddr < mem->devices[i].baseaddr) end = i - 1; if (paddr >= mem->devices[i].endaddr) start = i + 1; i = (start + end) >> 1; }while (start <= end) } … }
这里mem->devices[i].f是device初始化的时候注册进mem模块的dev_ns16550_access,在dev_ns16550.cc中结合memory.h的宏DEVICE_ACCESS定义的:
#define DEVICE_ACCESS(x) int dev_ ## x ## _access(struct cpu *cpu, \ struct memory *mem, uint64_t relative_addr, unsigned char *data, \ size_t len, int writeflag, void *extra) DEVICE_ACCESS(ns16550) { 根据访问的相对地址,访问device模拟的寄存器,及做相应的动作,例如触发中断等等,实际可参考代码 }
device的trick是在每个clock trick要检查的函数,用于模拟中断行为dev_ns16550_tick在dev_ns16550.cc中结合machine.h的宏DEVICE_TICK定义
#define DEVICE_TICK(x) void dev_ ## x ## _tick(struct cpu *cpu, void *extra) DEVICE_TICK(ns16550) { … INTERRUPT_ASSERT(d->irq); 产生中断 … INTERRUPT_DEASSERT(d->irq); 消除中断 }