完全照搬原文,原文地址:http://fire-flying.diandian.com/。
///
原文的代码可能比较旧,在我的机器上已经不能运行,下面是经我修改过的,先看domU端的代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <xen/interface/xen.h> #include <xen/interface/io/ring.h> #include <xen/grant_table.h> #include <asm/pgtable.h> #include <asm/sync_bitops.h> #include <xen/events.h> //#include <xen/gnttab.h> #include <asm/xen/page.h> #include <xen/evtchn.h> #include <asm/uaccess.h> #include <linux/proc_fs.h> unsigned long page; struct as_request { unsigned int id; unsigned int status; unsigned int operation; }; struct as_response { unsigned int id; unsigned int status; unsigned int operation; }; DEFINE_RING_TYPES(as, struct as_request, struct as_response); struct info_t { struct as_front_ring ring; grant_ref_t gref; int irq; int port; }info; #define DOM0_ID 0 static struct proc_dir_entry *proc_dir = NULL; static struct proc_dir_entry *proc_file = NULL; char proc_data[20]; int send_request_to_dom0(void) { struct as_request *ring_req; int notify; static int reqid = 9; ring_req = RING_GET_REQUEST(&(info.ring), info.ring.req_prod_pvt); ring_req->id = reqid; ring_req->operation = reqid; ring_req->status = reqid; printk(KERN_DEBUG "\nxen:DomU: Fill in IDX-%d, with id=%d, op=%d, st=%d", info.ring.req_prod_pvt, ring_req->id, ring_req->operation, ring_req->status); reqid++; info.ring.req_prod_pvt += 1; RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&(info.ring), notify); if (notify) { printk(KERN_DEBUG "\nxen: DomU: sent a req to Dom0"); notify_remote_via_irq(info.irq); } else { printk(KERN_DEBUG "\nxen:DomU: No notify req to Dom0"); notify_remote_via_irq(info.irq); } printk("...\n"); return 0; } int file_write (struct file *filp, const char __user *buff, unsigned long len, void *data) { int value; printk(KERN_DEBUG "\nxen:domU: file_write %lu bytes", len); if (copy_from_user(&proc_data[0], buff, len)) return -EFAULT; proc_data[len] = '\x0'; value = simple_strtol(proc_data, 0, 10); switch(value) { case 1: send_request_to_dom0(); printk(KERN_DEBUG " ,value = %d", value); break; default: printk(KERN_DEBUG " ,value not recongnized!"); } return len; } int file_read(char *page, char **start, off_t off, int count, int *eof, void *data) { sprintf(page, "%s", proc_data); return strlen(page); } int create_procfs_entry(void) { int ret = 0; proc_dir = proc_mkdir("demo", NULL); if (!proc_dir) { printk(KERN_DEBUG "\nxen:domU could not create demo entry in procfs"); ret = - EAGAIN; return ret; } proc_file = create_proc_entry("file", 0600, proc_dir); if (proc_file) { proc_file->read_proc = file_read; proc_file->write_proc = file_write; //proc_file->owner = THIS_MODULE; } else { printk(KERN_DEBUG "\nxen:domU Could not create /proc/demo/file"); ret = -EAGAIN; return ret; } return ret; } static irqreturn_t as_int (int irq, void *dev_id) { struct as_response *ring_resp; RING_IDX i, rp; printk("\nxen:DomU: as_int called"); again: rp = info.ring.sring->rsp_prod; printk(KERN_DEBUG "\nxen:DomU:ring pointers %d to %d", info.ring.rsp_cons, rp); for (i = info.ring.rsp_cons; i != rp; i++) { unsigned long id; ring_resp = RING_GET_RESPONSE(&(info.ring), i); printk(KERN_DEBUG "\nxen:domU: Recvd in IDX-%d, with id=%d, op=%d, st=%d", i, ring_resp->id, ring_resp->operation, ring_resp->status); id = ring_resp->id; switch(ring_resp->operation) { case 0: printk(KERN_DEBUG "\nxen:DomU: operation: 0"); break; default: break; } } info.ring.rsp_cons = i; if (i != info.ring.req_prod_pvt) { int more_to_do; RING_FINAL_CHECK_FOR_RESPONSES(&info.ring, more_to_do); if (more_to_do) goto again; } else info.ring.sring->rsp_event = i + 1; return IRQ_HANDLED; } int gnt_init(void) { int mfn; int err; //int i; struct as_sring *sring; //char *p; struct evtchn_alloc_unbound alloc_unbound; printk(KERN_INFO "gnt_init\n"); page = __get_free_pages(GFP_KERNEL, 0); if (page == 0) { printk(KERN_DEBUG "\nxen:DomU:could not get free page"); return 0; } sring = (struct as_sring *)page; SHARED_RING_INIT(sring); FRONT_RING_INIT(&(info.ring), sring, PAGE_SIZE); mfn = virt_to_mfn(page); /* p = (char *)page + PAGE_SIZE - 1; for (i = 0; i <= 10; i--, p--) *p = 'A'; */ /// /* strcpy((char *)page, "aseem sethi"); for (i = 0; i <= 10; i++) printk(KERN_DEBUG "%c", ((char *)page)[i]); */ printk(KERN_INFO "grant foreign access\n"); info.gref = gnttab_grant_foreign_access(DOM0_ID, mfn, 0); //info.gref = gnttab_grant_foreign_access(DOM0_ID, mfn, 1); if (info.gref < 0) { printk(KERN_DEBUG "\nxen:could not grant foreign access"); free_page((unsigned long)page); info.ring.sring = NULL; return 0; } printk(KERN_DEBUG "\n gref = %d", info.gref); alloc_unbound.dom = DOMID_SELF; alloc_unbound.remote_dom = DOM0_ID; err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &alloc_unbound); if (err) { printk(KERN_DEBUG "\nalloc unbound port failure"); return err; } err = bind_evtchn_to_irqhandler(alloc_unbound.port, as_int, 0, "xen-eg", &info); if (err < 0) { printk(KERN_DEBUG "\nbind evtchn to irqhandler failure"); return err; } /* err = bind_listening_port_to_irqhandler(DOM0_ID, as_int, 0, "xen-eg", &info); if (err < 0) { printk(KERN_DEBUG "\nxen:DomU failed to setup evtchn"); gnttab_end_foreign_access(info.gref, 0, page); return 0; } */ info.irq = err; info.port = alloc_unbound.port; //info.port = irq_to_evtchn_port(info.irq); printk(KERN_DEBUG " interrupt = %d, local_port = %d", info.irq, info.port); printk("...\n..."); create_procfs_entry(); return 0; } void gnt_exit(void) { printk(KERN_DEBUG "\ncleanup grant ref:"); if (gnttab_query_foreign_access(info.gref) == 0) { printk(KERN_DEBUG "\n xen:No one has mapped this frame"); gnttab_end_foreign_access(info.gref, 0, page); //gnttab_end_foreign_access(info.gref, 0, sring); } else { printk(KERN_DEBUG "\n xen:Someone has mapped this frame"); gnttab_end_foreign_access(info.gref, 0, page); //gnttab_end_foreign_access(info.gref, 0, sring); } remove_proc_entry("file", proc_dir); remove_proc_entry("demo", NULL); printk(KERN_DEBUG "...\n..."); } module_init(gnt_init); module_exit(gnt_exit); MODULE_LICENSE("GPL");
总结一下,domU端需要做的事情:
1. 获取一页空闲内存,初始化共享环及前端(__get_free_pages, SHARED_RING_INIT, FRONT_RING_INIT);
2.__get_free_pages得到的是虚拟地址,调用virt_to_mfn得到机器页帧号,然后授权Dom0可以访问,得到授权引用(gnttab_grant_foreign_access, 这个函数需要的是机器页帧号);
3. 通过超级调用HYPERVISOR_event_channel_op(EVTCHN_alloc_unbound, ...)分配端口号,调用bind_evtchn_to_irqhandler将端口号与中断处理函数绑定,得到中断号;至此,主要的工作便完成了,为了 便于控制,使用了proc-fs,通过create_procfs_entry创建proc文件系统。
然后再看dom0部分代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <xen/grant_table.h> //#include <xen/interface/io/blkif.h> #include <xen/interface/io/ring.h> #include <xen/interface/xen.h> #include <xen/xen.h> #include <linux/vmalloc.h> #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include <xen/evtchn.h> #include <xen/events.h> struct gnttab_map_grant_ref ops; struct gnttab_unmap_grant_ref unmap_ops; struct as_request { unsigned int id; unsigned int status; unsigned int operation; }; struct as_response { unsigned int id; unsigned int status; unsigned int operation; }; typedef struct as_request as_request_t; typedef struct as_response as_response_t; DEFINE_RING_TYPES(as, struct as_request, struct as_response); typedef struct as_sring as_sring_t; typedef struct as_front_ring as_front_ring_t; typedef struct as_back_ring as_back_ring_t; struct info_t { int irq; int gref; int remoteDomain; int evtchn; struct as_back_ring ring; } info; int gref; int port; module_param(gref, int, 0644); module_param(port, int, 0644); static irqreturn_t as_int(int irq, void *dev_id) { RING_IDX rc, rp; as_request_t req; as_response_t resp; int more_to_do, notify; printk(KERN_DEBUG "\nxen:Dom0: as_int called with dev_id %x info=%x", (unsigned int )dev_id, (unsigned int) &info); rc = info.ring.req_cons; rp = info.ring.sring->req_prod; printk(KERN_DEBUG " rc = %d rp = %d", rc, rp); while (rc != rp) { if (RING_REQUEST_CONS_OVERFLOW(&info.ring, rc)) break; memcpy(&req, RING_GET_REQUEST(&info.ring, rc), sizeof(req)); resp.id = req.id; resp.operation = req.operation; resp.status = req.status + 1; printk(KERN_DEBUG "\nxen:Dom0:Recvd at IDX-%d: id = %d, op=%d, status=%d", rc, req.id, req.operation, req.status); info.ring.req_cons = ++rc; barrier(); switch(req.operation) { case 0: printk(KERN_DEBUG "\nxen:dom0:req.operation = 0"); break; default: printk(KERN_DEBUG "\nxen:dom0:req.operation = %d", req.operation); break; } memcpy(RING_GET_RESPONSE(&info.ring, info.ring.rsp_prod_pvt), &resp, sizeof(resp)); info.ring.rsp_prod_pvt++; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&info.ring, notify); if (info.ring.rsp_prod_pvt == info.ring.req_cons) { RING_FINAL_CHECK_FOR_REQUESTS(&info.ring, more_to_do); } else if (RING_HAS_UNCONSUMED_REQUESTS(&info.ring)) { more_to_do = 1; } if (notify) { printk(KERN_DEBUG "\nxen:dom0:send notify to domu"); notify_remote_via_irq(info.irq); } } return IRQ_HANDLED; } static int gnt_init(void) { struct vm_struct *v_start; int err; //int i; //char *p; as_sring_t *sring; info.gref = gref; info.remoteDomain = 1; info.evtchn = port; printk(KERN_DEBUG "\nxen: dom0: gnt_init with gref = %d", info.gref); v_start = alloc_vm_area(PAGE_SIZE);//dom需要将授权访问的内存页映射到自己的地址空间中,分配一个非连续的内存区域 if (v_start == 0) { free_vm_area(v_start); printk("\nxen: dom0:could not allocate page"); return -EFAULT; } gnttab_set_map_op(&ops, (unsigned long)v_start->addr, GNTMAP_host_map, info.gref, info.remoteDomain);//为下面的hypercall的参数做准备 if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &ops, 1)) {//页面映射 printk(KERN_DEBUG "\nxen:dom0: HYPERVISOR map grant ref failed"); return -EFAULT; } if (ops.status) { printk(KERN_DEBUG "\nxen: dom0: HYPERVISOR map grant ref failed status = %d", ops.status); return -EFAULT; } printk(KERN_DEBUG "\nxen:dom0:shared_page=%x, handle = %x, status = %x", (unsigned int)v_start->addr, ops.handle, ops.status); unmap_ops.host_addr = (unsigned long)(v_start->addr); unmap_ops.handle = ops.handle; // /* p = (char *)(v_start->addr) + PAGE_SIZE - 1; printk(KERN_DEBUG "\nbytes in page"); for (i = 0;i <= 10; i++, p--) { printk(KERN_DEBUG "%c", *p); } */ //// sring = (as_sring_t *)v_start->addr; BACK_RING_INIT(&info.ring, sring, PAGE_SIZE); err = bind_interdomain_evtchn_to_irqhandler(info.remoteDomain, info.evtchn, as_int, 0, "dom0-backend", &info); if (err < 0) { printk(KERN_DEBUG "\nxen:dom0: gnt_init failed binding to evtchn"); err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmap_ops, 1); return -EFAULT; } info.irq = err; printk(KERN_DEBUG "\nxen:dom0:end gnt_int: int = %d", info.irq); return 0; } static void gnt_exit(void) { int ret; printk(KERN_DEBUG "\nxen:dom0:gnt_exit"); ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmap_ops, 1); if (ret == 0) { printk(KERN_DEBUG "gnt_exit: unmapped shared frame"); } else { printk(KERN_DEBUG "gnt_exit: unmapped shared frame failed"); } printk("...\n"); } module_init(gnt_init); module_exit(gnt_exit); MODULE_LICENSE("GPL");
dom0所做的工作:
1. 因为我们需要将domU的一部分内存映射到dom0内,因此调用alloc_vm_area预留一段内核空间和页表,但是实际并没有映射;
2. 调用gnttab_set_map_op为超级调用的参数做准备,然后调用HYPERVISOR_grant_table_op(GTNTABOP_map_grant_ref, ...)进行页面映射;
3. 调用BACK_RING_INIT初始化共享环后端;
4. 调用bind_interdomain_evtchn_to_irqhandler将端口与中断处理程序绑定,这样会得到中断号,注意端口号是模块函数的参数。