aps->address = address;
aps->cmd = cmd;
writel(cmd, dev->base + PIPE_REG_ACCESS_PARAMS);
/* If aps->result unchanged, then batch command failed */
if (aps->result == IMPOSSIBLE_BATCH_RESULT)
return -1;
*status = aps->result;
return 0;
}
**另外需要说明的是几种不同的地址:
1、guest os进程虚拟地址,用户空间的地址,内核想使用这种地址时,需要调用copy_from_user与copy_to_user去验证是否正确然后才能读写
2、guest os内核虚拟地址,3GB~4GB
3、guest os内核物理地址,经典情况下,就是内核虚拟地址减去一个偏移量(3GB),物理内存较大时,情况不同。在qemu中通过safe_get_phys_page_debug可以把guest os内核虚拟地址转为guest os内核物理地址
4、emulator所在虚拟空间地址,我们的host os中的用户空间地址,qemu可以操作的内存地址。guest os内核物理地址通过cpu_physical_memory_map后可以map为qemu所在的虚拟空间的地址,然后qemu可以去使用内核传递过来的内存。**
**三、虚拟设备
pipe虚拟设备的代码为:http://androidxref.com/5.1.0_r1/xref/external/qemu/hw/android/goldfish/pipe.c**
初始化代码为pipe_dev_init,没啥好说的,比battery的简单多了。最后有三个调试用的东西,可以不看:
/* initialize the trace device */
void pipe_dev_init(bool newDeviceNaming)
{
PipeDevice *s;
s = (PipeDevice *) g_malloc0(sizeof(*s));
s->dev.name = newDeviceNaming ? “goldfish_pipe” : “qemu_pipe”;
s->dev.id = -1;
s->dev.base = 0; // will be allocated dynamically
s->dev.size = 0x2000;
s->dev.irq = 0;
s->dev.irq_count = 1;
goldfish_device_add(&s->dev, pipe_dev_readfn, pipe_dev_writefn, s);
register_savevm(NULL,
“goldfish_pipe”,
0,
GOLDFISH_PIPE_SAVE_VERSION,
goldfish_pipe_save,
goldfish_pipe_load,
s);
#if DEBUG_ZERO_PIPE
goldfish_pipe_add_type(“zero”, NULL, &zeroPipe_funcs);
#endif
#if DEBUG_PINGPONG_PIPE
goldfish_pipe_add_type(“pingpong”, NULL, &pingPongPipe_funcs);
#endif
#if DEBUG_THROTTLE_PIPE
goldfish_pipe_add_type(“throttle”, NULL, &throttlePipe_funcs);
#endif
}
读函数为pipe_dev_read,需要注意的是PIPE_REG_CHANNEL。
kernel中的中断处理函数每次读取PIPE_REG_CHANNEL时,模拟设备都会将dev->signaled_pipes链表上的一个CHANNEL返回,并设置PIPE_REG_WAKES寄存器,告知kernel中pipe的驱动程序可以唤醒哪一个CHANNEL上的读等待 or 写等待的线程。
dev->signaled_pipes时满足条件,等待被唤醒的pipe列表,里面的节点是在goldfish_pipe_wake函数中添加的。
当dev->signaled_pipes为NULL时,通过goldfish_device_set_irq(&dev->dev, 0, 0)清除中断请求位。
/* I/O read */
static uint32_t pipe_dev_read(void *opaque, hwaddr offset)
{
PipeDevice *dev = (PipeDevice *)opaque;
switch (offset) {
case PIPE_REG_STATUS:
DR(“%s: REG_STATUS status=%d (0x%x)”, __FUNCTION__, dev->status, dev->status);
return dev->status;
case PIPE_REG_CHANNEL:
if (dev->signaled_pipes != NULL) {
Pipe* pipe = dev->signaled_pipes;
DR(“%s: channel=0x%llx wanted=%d”, __FUNCTION__,
(unsigned long long)pipe->channel, pipe->wanted);
dev->wakes = pipe->wanted;
pipe->wanted = 0;
dev->signaled_pipes = pipe->next_waked;
pipe->next_waked = NULL;
if (dev->signaled_pipes == NULL) {
goldfish_device_set_irq(&dev->dev, 0, 0);
DD(“%s: lowering IRQ”, __FUNCTION__);
}
return (uint32_t)(pipe->channel & 0xFFFFFFFFUL);
}
DR(“%s: no signaled channels”, __FUNCTION__);
return 0;
case PIPE_REG_CHANNEL_HIGH:
if (dev->signaled_pipes != NULL) {
Pipe* pipe = dev->signaled_pipes;
DR(“%s: channel_high=0x%llx wanted=%d”, __FUNCTION__,
(unsigned long long)pipe->channel, pipe->wanted);
return (uint32_t)(pipe->channel >> 32);
}
DR(“%s: no signaled channels”, __FUNCTION__);
return 0;
case PIPE_REG_WAKES:
DR(“%s: wakes %d”, __FUNCTION__, dev->wakes);
return dev->wakes;
case PIPE_REG_PARAMS_ADDR_HIGH:
return (uint32_t)(dev->params_addr >> 32);
case PIPE_REG_PARAMS_ADDR_LOW:
return (uint32_t)(dev->params_addr & 0xFFFFFF