android emulator虚拟设备之qemu pipe分析(三),2024年Android工作或许更难找

本文详细分析了Android模拟器中的qemu pipe虚拟设备,包括地址类型、设备初始化、读写操作以及中断处理。重点讨论了如何在模拟设备和内核之间进行内存操作,以及pipe的中断机制。通过对pipe设备的读写函数的剖析,展示了如何处理 guest OS 的内存地址转换。最后提到了pipe服务的注册和查找,以及pipe状态管理和唤醒机制。适合深入理解Android模拟器底层工作原理的技术人员阅读。
摘要由CSDN通过智能技术生成

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

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值