写点什么

android emulator 虚拟设备之 qemu pipe 分析 (三),android 系统开发工程师

作者:嘟嘟侠客
  • 2021 年 11 月 27 日
  • 本文字数:10840 字

    阅读完需:约 36 分钟

0x08 ?CHANNEL ? ? ? ? ?RW: Read or set current channel id.


0x0c ?SIZE ? ? ? ? ? ? RW: Read or set current buffer size.


0x10 ?ADDRESS ? ? ? ? ?RW: Read or set current buffer physical address.


0x14 ?WAKES ? ? ? ? ? ?R: Read wake flags.


0x18 ?PARAMS_ADDR_LOW ?RW: Read/set low bytes of parameters block address.


0x1c ?PARAMS_ADDR_HIGH RW: Read/set high bytes of parameters block address.


0x20 ?ACCESS_PARAMS ? ?W: Perform access with parameter block.


This is a special device that is totally specific to QEMU, but allows guest


processes to communicate directly with the emulator with extremely high


performance. This is achieved by avoiding any in-kernel memory copies, relying


on the fact that QEMU can access guest memory at runtime (under proper


conditions controlled by the kernel).


Please refer to $QEMU/docs/ANDROID-QEMU-PIPE.TXT for full details on the


device's operations.


1、COMMAND 包括 CMD_OPEN,CMD_CLOSE,CMD_POLL,CMD_WRITE_BUFFER,CMD_WAKE_ON_WRITE(可写时唤醒),CMD_READ_BUFFER,CMD_WAKE_ON_READ(可读时唤醒)


2、CHANNEL,每次打开/dev/qemu_pipe,都将新建一个 struct qemu_pipe* pipe,相当于在/dev/qemu_pipe 上面新开了一个通道,通道号 CHANNEL=(unsigned long)pipe


3、WAKES,是否应该将读等待/写等待的线程唤醒


4、PARAMS_ADDR_LOW,PARAMS_ADDR_HIGH,ACCESS_PARAMS 用于快速读写访问,这个看不懂的话不影响理解 qemu_pipe,可以跳过。


struct access_params{


uint32_t channel;


uint32_t size;


uint32_t address;


uint32_t cmd;


uint32_t result;


/* reserved for future extension */


uint32_t flags;


};


kernel 代码中 qemu_pipe_dev 在 probe 时,会申请一个 access_params 结构体,并将它在 guest os 的内核物理地址写入 PARAMS_ADDR_LOW 和 PARAMS_ADDR_HIGH。


kernel 代码在需要进行快速读写访问时,设置 access_params 结构体的内容,然后使用 ACCESS_PARAMS 启动快速读写。


emulator 代码中虚拟设备将 PARAMS_ADDR_LOW 和 PARAMS_ADDR_HIGH 所表示的地址映射到 emulator 虚拟空间地址中,然后去获取 channel, size, address, cmd 等数据然后去操作,相同于一次 IO 访问,得到多个 IO 数据,所以叫做 batch,快速访问。


注意 PARAMS_ADDR_LOW 和 PARAMS_ADDR_HIGH 写的是 guest os 的内核物理地址,access_params 结构体里面的 buffer 还是 guest os 内核虚拟地址。


驱动程序为 goldfish 代码中的 drivers/misc/qemupipe/qemu_pipe.c


初始化代码为:


static struct platform_driver qemu_pipe = {


.probe = qemu_pipe_probe,


.remove = qemu_pipe_remove,


.driver = {


.name = "qemu_pipe"


}


};


static int __init qemu_pipe_dev_init(void)


{


return platform_driver_register(&qemu_pipe);


}


static void qemu_pipe_dev_exit(void)


{


platform_driver_unregister(&qemu_pipe);


}


qemu_pipe_probe 干的还是那些事,得到 IO 内存资源,进行 ioremap,得到中断号,设置中断函数。最后使用 misc_register 注册了一个杂项字符设备,设备文件为/dev/qemu_pipe:


static const struct file_operations qemu_pipe_fops = {


.owner = THIS_MODULE,


.read = qemu_pipe_read,


.write = qemu_pipe_write,


.poll = qemu_pipe_poll,


.open = qemu_pipe_open,


.release = qemu_pipe_release,


};


static struct miscdevice qemu_pipe_device = {


.minor = MISC_DYNAMIC_MINOR,


.name = "qemu_pipe",


.fops = &qemu_pipe_fops,


};


static int qemu_pipe_probe(struct platform_device *pdev)


{


int err;


struct resource *r;


struct qemu_pipe_dev *dev = pipe_dev;


PIPE_D("Creating device\n");


INIT_RADIX_TREE(&dev->pipes, GFP_ATOMIC);


/* not thread safe, but this should not happen */


if (dev->base != NULL) {


printk(KERN_ERR "QEMU PIPE Device: already mapped at %p\n",


dev->base);


return -ENODEV;


}


r = platform_get_resource(pdev, IORESOURCE_MEM, 0);


if (r == NULL || r->end - r->start < PAGE_SIZE - 1) {


printk(KERN_ERR "QEMU PIPE Device: can't allocate i/o page\n");


return -EINVAL;


}


dev->base = ioremap(r->start, PAGE_SIZE);


PIPE_D("The mapped IO base is %p\n", dev->base);


r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);


if (r == NULL) {


printk(KERN_ERR "QEMU PIPE Device: failure to allocate IRQ\n");


err = -EINVAL;


goto err_alloc_irq;


}


dev->irq = r->start;


PIPE_D("The IRQ is %d\n", dev->irq);


err = request_irq(dev->irq, qemu_pipe_interrupt, IRQF_SHARED,


"goldfish_pipe", dev);


if (err)


goto err_alloc_irq;


spin_lock_init(&dev->lock);


err = misc_register(&qemu_pipe_device);


if (err)


goto err_misc_register;


setup_access_params_addr(dev);


return 0;


err_misc_register:


free_irq(dev->irq, pdev);


err_alloc_irq:


iounmap(dev->base);


dev->base = NULL;


return err;


}


qemu_pipe_open,每次打开/dev/qemu_pipe 都会 alloc 一个新的 qemu_pipe 结构体,每个 qemu_pipe 结构体对应一个 CHANNEL,qemu_pipe 结构体将被添加到一个 radix_tree 中。将 qemu_pipe 的地址作为 CHANNEL(不可能重复的)写入 PIPE_REG_CHANNEL 寄存器,然后写 CMD_OPEN 到 PIPE_REG_COMMAND 中,去打开新的 CHANNEL。最后设置了 filp 的私有变量为 qemu_pipe 结构体。


static int qemu_pipe_open(struct inode *inode, struct file *file)


{


unsigned long irq_flags;


struct qemu_pipe *pipe;


struct qemu_pipe_dev *dev = pipe_dev;


int32_t status;


int ret;


/* Allocate new pipe kernel object */


pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);


if (pipe == NULL) {


PIPE_E("Not enough kernel memory to allocate new pipe\n");


return -ENOMEM;


}


PIPE_D("Opening pipe %p\n", pipe);


pipe->dev = dev;


mutex_init(&pipe->lock);


init_waitqueue_head(&pipe->wake_queue);


/* Now, tell the emulator we're opening a new pipe. We use the


* pipe object's address as the channel identifier for simplicity.


*/


spin_lock_irqsave(&dev->lock, irq_flags);


if ((ret = radix_tree_insert(&dev->pipes, (unsigned long)pipe, pipe))) {


spin_unlock_irqrestore(&dev->lock, irq_flags);


PIPE_E("opening pipe failed due to radix tree insertion failure\n");


kfree(pipe);


return ret;


}


writel((unsigned long)pipe, dev->base + PIPE_REG_CHANNEL);


writel(CMD_OPEN, dev->base + PIPE_REG_COMMAND);


status = readl(dev->base + PIPE_REG_STATUS);


spin_unlock_irqrestore(&dev->lock, irq_flags);


if (status < 0) {


PIPE_E("Could not open pipe channel, error=%d\n", status);


kfree(pipe);


return status;


}


/* All is done, save the pipe into the file's private data field */


file->private_data = pipe;


return 0;


}


qemu_pipe_read 和 qemu_pipe_write 都是使用 qemu_pipe_read_write 来实现的,注意 access_ok 和__get_user/__put_user 对于用户空间指针的检测。具体的读写比较简单,就是操作 IO 寄存器而已,需要注意的是,如果是非阻塞方式,需要进行阻塞等待。


具体的方法就是往 PIPE_REG_COMMAND 里面写 CMD_WAKE_ON_WRITE 或者 CMD_WAKE_ON_READ,然后调用 wait_event_interruptible 去等待!test_bit(wakeBit, &pipe->flags)。


当中断来临时,会检查每一个 CHANNEL 的 PIPE_REG_WAKES 寄存器,如果可读 or 可写 or 已关闭,中断函数中会清除 pipe->flags 中的对应的等待标志位,然后 wait_event_interruptible 等待结束。如果是 qemu_pipe 被关闭的情况,wait_event_interruptible 等待结束之后,检查到错误状态并退出。


/* This function is used for both reading from and writing to a given


* pipe.


*/


static ssize_t qemu_pipe_read_write(struct file *filp, char __user *buffer,


size_t bufflen, int is_write)


{


unsigned long irq_flags;


struct qemu_pipe *pipe = filp->private_data;


struct qemu_pipe_dev *dev = pipe->dev;


const int cmd_offset = is_write ? 0


: (CMD_READ_BUFFER - CMD_WRITE_BUFFER);


unsigned long address, address_end;


int ret = 0;


/* If the emulator already closed the pipe, no need to go further */


if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) {


PIPE_W("(write=%d) already closed!\n", is_write);


ret = -EIO;


goto out;


}


/* Null reads or writes succeeds */


if (unlikely(bufflen) == 0)


goto out;


/* Check the buffer range for access */


if (!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ,


buffer, bufflen)) {


ret = -EFAULT;


PIPE_W("rw access_ok failed\n");


goto out;


}


/* Serialize access to the pipe */


if (mutex_lock_interruptible(&pipe->lock)) {


PIPE_W("(write=%d) interrupted!\n", is_write);


return -ERESTARTSYS;


}


address = (unsigned long)(void *)buffer;


address_end = address + bufflen;


while (address < address_end) {


unsigned long ?page_end = (address & PAGE_MASK) + PAGE_SIZE;


unsigned long ?next ? ? = page_end < address_end ? page_end


: address_end;


unsigned long ?avail ? ?= next - address;


int status, wakeBit;


/* Ensure that the corresponding page is properly mapped */


if (is_write) {


char c;


/* Ensure that the page is mapped and readable */


if (__get_user(c, (char __user *)address)) {


PIPE_E("read fault at address 0x%08x\n",


(unsigned int)address);


if (!re


《Android 学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享


t)


ret = -EFAULT;


break;


}


} else {


/* Ensure that the page is mapped and writable */


if (__put_user(0, (char __user *)address)) {


PIPE_E("write fault at address 0x%08x\n",


(unsigned int)address);


if (!ret)


ret = -EFAULT;


break;


}


}


/* Now, try to transfer the bytes in the current page */


spin_lock_irqsave(&dev->lock, irq_flags);


if (dev->aps == NULL || access_with_param(


dev, CMD_WRITE_BUFFER + cmd_offset, address, avail,


pipe, &status) < 0)


{


writel((unsigned long)pipe,


dev->base + PIPE_REG_CHANNEL);


writel(avail, dev->base + PIPE_REG_SIZE);


writel(address, dev->base + PIPE_REG_ADDRESS);


writel(CMD_WRITE_BUFFER + cmd_offset,


dev->base + PIPE_REG_COMMAND);


status = readl(dev->base + PIPE_REG_STATUS);


}


spin_unlock_irqrestore(&dev->lock, irq_flags);


if (status > 0) { /* Correct transfer */


ret += status;


address += status;


continue;


}


if (status == 0) ?/* EOF */


break;


/* An error occured. If we already transfered stuff, just


* return with its count. We expect the next call to return


* an error code */


if (ret > 0)


break;


/* If the error is not PIPE_ERROR_AGAIN, or if we are not in


* non-blocking mode, just return the error code.


*/


if (status != PIPE_ERROR_AGAIN ||


(filp->f_flags & O_NONBLOCK) != 0) {


ret = qemu_pipe_error_convert(status);


break;


}


/* We will have to wait until more data/space is available.


* First, mark the pipe as waiting for a specific wake signal.


*/


wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;


set_bit(wakeBit, &pipe->flags);


/* Tell the emulator we're going to wait for a wake event */


spin_lock_irqsave(&dev->lock, irq_flags);


writel((unsigned long)pipe, dev->base + PIPE_REG_CHANNEL);


writel(CMD_WAKE_ON_WRITE + cmd_offset,


dev->base + PIPE_REG_COMMAND);


spin_unlock_irqrestore(&dev->lock, irq_flags);


/* Unlock the pipe, then wait for the wake signal */


mutex_unlock(&pipe->lock);


while (test_bit(wakeBit, &pipe->flags)) {


if (wait_event_interruptible(


pipe->wake_queue,


!test_bit(wakeBit, &pipe->flags))) {


ret = -ERESTARTSYS;


PIPE_W("rw, wait_event error\n");


goto out;


}


if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) {


ret = -EIO;


PIPE_W("rw, pipe already closed\n");


goto out;


}


}


/* Try to re-acquire the lock */


if (mutex_lock_interruptible(&pipe->lock)) {


ret = -ERESTARTSYS;


goto out;


}


/* Try the transfer again */


continue;


}


mutex_unlock(&pipe->lock);


out:


return ret;


}


static ssize_t qemu_pipe_read(struct file *filp, char __user *buffer,


size_t bufflen, loff_t *ppos)


{


return qemu_pipe_read_write(filp, buffer, bufflen, 0);


}


static ssize_t qemu_pipe_write(struct file *filp,


const char __user *buffer, size_t bufflen,


loff_t *ppos)


{


return qemu_pipe_read_write(filp, (char __user *)buffer, bufflen, 1);


}


qemu_pipe_poll,实现 poll,select,epoll 接口用的,没什么特殊的,标准实现方式


static unsigned int qemu_pipe_poll(struct file *filp, poll_table *wait)


{


struct qemu_pipe *pipe = filp->private_data;


struct qemu_pipe_dev *dev = pipe->dev;


unsigned long irq_flags;


unsigned int mask = 0;


int status;


mutex_lock(&pipe->lock);


poll_wait(filp, &pipe->wake_queue, wait);


spin_lock_irqsave(&dev->lock, irq_flags);


writel((unsigned long)pipe, dev->base + PIPE_REG_CHANNEL);


writel(CMD_POLL, dev->base + PIPE_REG_COMMAND);


status = readl(dev->base + PIPE_REG_STATUS);


spin_unlock_irqrestore(&dev->lock, irq_flags);


mutex_unlock(&pipe->lock);


if (status & PIPE_POLL_IN)


mask |= POLLIN | POLLRDNORM;


if (status & PIPE_POLL_OUT)


mask |= POLLOUT | POLLWRNORM;


if (status & PIPE_POLL_HUP)


mask |= POLLHUP;


if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))


mask |= POLLERR;


return mask;


}


qemu_pipe_interrupt,中断处理函数,循环处理每一个 qemu_pipe,看看是否可读 or 可写 or 关闭了,然后唤醒对应的线程


static irqreturn_t qemu_pipe_interrupt(int irq, void *dev_id)


{


struct qemu_pipe_dev *dev = dev_id;


unsigned long irq_flags;


int count = 0;


/* We're going to read from the emulator a list of (channel,flags)


* pairs corresponding to the wake events that occured on each


* blocked pipe (i.e. channel).


*/


spin_lock_irqsave(&dev->lock, irq_flags);


for (;;) {


/* First read the channel, 0 means the end of the list */


struct qemu_pipe *pipe;


unsigned long wakes;


unsigned long channel = readl(dev->base + PIPE_REG_CHANNEL);


if (channel == 0)


break;


/* Convert channel to struct pipe pointer + read wake flags */


wakes = readl(dev->base + PIPE_REG_WAKES);


pipe ?= (struct qemu_pipe *)(ptrdiff_t)channel;


/* check if pipe is still valid */


if ((pipe = radix_tree_lookup(&dev->pipes,


(unsigned long)pipe)) == NULL) {


PIPE_W("interrupt for already closed pipe\n");


break;


}


/* Did the emulator just closed a pipe? */


if (wakes & PIPE_WAKE_CLOSED) {


set_bit(BIT_CLOSED_ON_HOST, &pipe->flags);


wakes |= PIPE_WAKE_READ | PIPE_WAKE_WRITE;


}


if (wakes & PIPE_WAKE_READ)


clear_bit(BIT_WAKE_ON_READ, &pipe->flags);


if (wakes & PIPE_WAKE_WRITE)


clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags);


wake_up_interruptible(&pipe->wake_queue);


count++;


}


spin_unlock_irqrestore(&dev->lock, irq_flags);


return (count == 0) ? IRQ_NONE : IRQ_HANDLED;


}


setup_access_params_addr 和 access_with_param 用于快速读写的,看不懂的可以跳过:


/* 0 on success */


static int setup_access_params_addr(struct qemu_pipe_dev *dev)


{


uint64_t paddr;


struct access_params *aps;


aps = kmalloc(sizeof(struct access_params), GFP_KERNEL);


if (!aps)


return -1;


paddr = __pa(aps);


writel((uint32_t)(paddr >> 32), dev->base + PIPE_REG_PARAMS_ADDR_HIGH);


writel((uint32_t)paddr, dev->base + PIPE_REG_PARAMS_ADDR_LOW);


if (!valid_batchbuffer_addr(dev, aps))


return -1;


dev->aps = aps;


return 0;


}


/* A value that will not be set by qemu emulator */


#define IMPOSSIBLE_BATCH_RESULT (0xdeadbeaf)


static int access_with_param(struct qemu_pipe_dev *dev, const int cmd,


unsigned long address, unsigned long avail,


struct qemu_pipe *pipe, int *status)


{


struct access_params *aps = dev->aps;


aps->result = IMPOSSIBLE_BATCH_RESULT;


aps->channel = (unsigned long)pipe;


aps->size = avail;


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 & 0xFFFFFFFFUL);


default:


D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset);


}


return 0;


}


写函数为 pipe_dev_write,如果是写 PIPE_REG_COMMAND,有专门的子函数 pipeDevice_doCommand 处理,如果是写 PIPE_REG_ACCESS_PARAMS,相当于 batch 操作,传递了多个寄存器的值,然后去执行读写操作。


static void pipe_dev_write(void *opaque, hwaddr offset, uint32_t value)


{


PipeDevice *s = (PipeDevice *)opaque;


switch (offset) {


case PIPE_REG_COMMAND:


DR("%s: command=%d (0x%x)", __FUNCTION__, value, value);


pipeDevice_doCommand(s, value);


break;


case PIPE_REG_SIZE:


DR("%s: size=%d (0x%x)", __FUNCTION__, value, value);


s->size = value;


break;


case PIPE_REG_ADDRESS:


DR("%s: address=%d (0x%x)", __FUNCTION__, value, value);


uint64_set_low(&s->address, value);


break;


case PIPE_REG_ADDRESS_HIGH:


DR("%s: address_high=%d (0x%x)", __FUNCTION__, value, value);


uint64_set_high(&s->address, value);


break;


case PIPE_REG_CHANNEL:


DR("%s: channel=%d (0x%x)", __FUNCTION__, value, value);


uint64_set_low(&s->channel, value);


break;


case PIPE_REG_CHANNEL_HIGH:


DR("%s: channel_high=%d (0x%x)", __FUNCTION__, value, value);


uint64_set_high(&s->channel, value);


break;


case PIPE_REG_PARAMS_ADDR_HIGH:


s->params_addr = (s->params_addr & ~(0xFFFFFFFFULL << 32) ) |


((uint64_t)value << 32);


break;


case PIPE_REG_PARAMS_ADDR_LOW:


s->params_addr = (s->params_addr & ~(0xFFFFFFFFULL) ) | value;


break;


case PIPE_REG_ACCESS_PARAMS:


{


struct access_params aps;


struct access_params_64 aps64;


uint32_t cmd;


/* Don't touch aps.result if anything wrong */


if (s->params_addr == 0)


break;


if (goldfish_guest_is_64bit()) {


cpu_physical_memory_read(s->params_addr, (void*)&aps64,


sizeof(aps64));


} else {


cpu_physical_memory_read(s->params_addr, (void*)&aps,


sizeof(aps));


}


/* sync pipe device state from batch buffer */


if (goldfish_guest_is_64bit()) {


s->channel = aps64.channel;


s->size = aps64.size;


s->address = aps64.address;


cmd = aps64.cmd;


} else {


s->channel = aps.channel;


s->size = aps.size;


s->address = aps.address;


cmd = aps.cmd;


}


if ((cmd != PIPE_CMD_READ_BUFFER) && (cmd != PIPE_CMD_WRITE_BUFFER))


break;


pipeDevice_doCommand(s, cmd);


if (goldfish_guest_is_64bit()) {


aps64.result = s->status;


cpu_physical_memory_write(s->params_addr, (void*)&aps64,


sizeof(aps64));


} else {


aps.result = s->status;


cpu_physical_memory_write(s->params_addr, (void*)&aps,


sizeof(aps));


}


}


break;


default:


D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset,


offset, value, value);


break;


}


}


pipeDevice_doCommand,打开,关闭,读,写,可读时唤醒,可写时唤醒。


需要注意的是:


1、在刚打开 CHANNEL 时,pipe->funcs 函数指针指向 pipeConnector_funcs,根据 guest os 第一次写入到/dev/qemu_pipe 的内容,得到 pipe service 的名字以及 args。


之后,pipe->funcs 都将指向对应的 pipe service 中实现的函数。


2、使用 safe_get_phys_page_debug 将传递过来的 guest os 内核虚拟地址转为 guest os 内核物理地址,然后使用 qemu_get_ram_ptr 转为 emulator 进程的虚拟空间地址。


static void


pipeDevice_doCommand( PipeDevice* dev, uint32_t command )


{


Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);


Pipe* ?pipe ? = *lookup;


CPUOldState* env = cpu_single_env;


/* Check that we're referring a known pipe channel */


if (command != PIPE_CMD_OPEN && pipe == NULL) {


dev->status = PIPE_ERROR_INVAL;


return;


}


/* If the pipe is closed by the host, return an error */


if (pipe != NULL && pipe->closed && command != PIPE_CMD_CLOSE) {


dev->status = PIPE_ERROR_IO;


return;


}


switch (command) {


case PIPE_CMD_OPEN:


DD("%s: CMD_OPEN channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);


if (pipe != NULL) {


dev->status = PIPE_ERROR_INVAL;


break;


}


pipe = pipe_new(dev->channel, dev);


pipe->next = dev->pipes;


dev->pipes = pipe;


dev->status = 0;


break;


case PIPE_CMD_CLOSE:


DD("%s: CMD_CLOSE channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);


/* Remove from device's lists */


*lookup = pipe->next;


pipe->next = NULL;


pipe_list_remove_waked(&dev->signaled_pipes, pipe);


pipe_free(pipe);


break;


case PIPE_CMD_POLL:


dev->status = pipe->funcs->poll(pipe->opaque);


DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);


break;


case PIPE_CMD_READ_BUFFER: {


/* Translate virtual address into physical one, into emulator memory. */


GoldfishPipeBuffer ?buffer;


target_ulong ? ? ? ?address = dev->address;

最后

在这里我和身边一些朋友特意整理了一份快速进阶为 Android 高级工程师的系统且全面的学习资料。涵盖了 Android 初级——Android 高级架构师进阶必备的一些学习技能。


附上:我们之前因为秋招收集的二十套一二线互联网公司 Android 面试真题(含 BAT、小米、华为、美团、滴滴)和我自己整理 Android 复习笔记(包含 Android 基础知识点、Android 扩展知识点、Android 源码解析、设计模式汇总、Gradle 知识点、常见算法题汇总。)



本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

用户头像

嘟嘟侠客

关注

还未添加个人签名 2021.03.19 加入

还未添加个人简介

评论

发布
暂无评论
android emulator虚拟设备之qemu pipe分析(三),android系统开发工程师