android emulator 虚拟设备之 qemu pipe 分析 (三),android 系统开发工程师
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 知识点、常见算法题汇总。)

评论