Gexmul Device原理简述

1.初始化device模块

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模块

2.添加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)
}

2.1 device初始化:

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);
}

3. device的访问:

虚拟机通过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模拟的寄存器,及做相应的动作,例如触发中断等等,实际可参考代码
}

4. device的trick:

    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); 消除中断
}


你可能感兴趣的:(Gexmul Device原理简述)