Loading drivers/soc/qcom/hab/Makefile +11 −4 Original line number Diff line number Diff line Loading @@ -6,16 +6,21 @@ msm_hab-objs = \ hab_pchan.o \ hab_open.o \ hab_mimex.o \ hab_mem_linux.o \ hab_pipe.o \ hab_parser.o \ khab_test.o \ hab_stat.o msm_hab_linux-objs = \ hab_linux.o \ hab_mem_linux.o ifdef CONFIG_GHS_VMM msm_hab_hyp-objs = \ ghs_comm.o \ hab_ghs.o ghs_comm_linux.o \ hab_ghs.o \ hab_ghs_linux.o ifndef CONFIG_MSM_AGL ccflags-y += -DHABMM_HC_VMID Loading @@ -25,7 +30,9 @@ else ifdef CONFIG_QTI_GVM_QUIN msm_hab_hyp-objs = \ qvm_comm.o \ hab_qvm.o qvm_comm_linux.o \ hab_qvm.o \ hab_qvm_linux.o else msm_hab_hyp-objs = \ hab_comm.o \ Loading @@ -33,4 +40,4 @@ msm_hab_hyp-objs = \ endif endif obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_hyp.o obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_linux.o msm_hab_hyp.o drivers/soc/qcom/hab/ghs_comm.c +28 −40 Original line number Diff line number Diff line Loading @@ -47,6 +47,13 @@ int physical_channel_send(struct physical_channel *pchan, hab_spin_lock(&dev->io_lock, irqs_disabled); result = hab_gipc_wait_to_send(dev->endpoint); if (result != GIPC_Success) { hab_spin_unlock(&dev->io_lock, irqs_disabled); pr_err("failed to wait to send %d\n", result); return -EBUSY; } result = GIPC_PrepareMessage(dev->endpoint, sizebytes+sizeof(*header), (void **)&msg); if (result == GIPC_Full) { Loading Loading @@ -89,30 +96,15 @@ int physical_channel_send(struct physical_channel *pchan, return 0; } void physical_channel_rx_dispatch(unsigned long physical_channel) void physical_channel_rx_dispatch_common(unsigned long physical_channel) { struct hab_header header; struct physical_channel *pchan = (struct physical_channel *)physical_channel; struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data; GIPC_Result result; uint32_t events; unsigned long flags; int irqs_disabled = irqs_disabled(); spin_lock_irqsave(&pchan->rxbuf_lock, flags); events = kgipc_dequeue_events(dev->endpoint); spin_unlock_irqrestore(&pchan->rxbuf_lock, flags); if (events & (GIPC_EVENT_RESET)) pr_err("hab gipc %s remote vmid %d RESET\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RESETINPROGRESS)) pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RECEIVEREADY)) { hab_spin_lock(&pchan->rxbuf_lock, irqs_disabled); while (1) { dev->read_size = 0; Loading @@ -137,7 +129,3 @@ void physical_channel_rx_dispatch(unsigned long physical_channel) } hab_spin_unlock(&pchan->rxbuf_lock, irqs_disabled); } if (events & (GIPC_EVENT_SENDREADY)) pr_debug("kgipc send ready\n"); } drivers/soc/qcom/hab/ghs_comm_linux.c 0 → 100644 +49 −0 Original line number Diff line number Diff line /* Copyright (c) 2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "hab.h" #include "hab_ghs.h" inline int hab_gipc_wait_to_send(GIPC_Endpoint endpoint) { (void)endpoint; return GIPC_Success; } void physical_channel_rx_dispatch(unsigned long physical_channel) { struct physical_channel *pchan = (struct physical_channel *)physical_channel; struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data; uint32_t events; unsigned long flags; spin_lock_irqsave(&pchan->rxbuf_lock, flags); events = kgipc_dequeue_events(dev->endpoint); spin_unlock_irqrestore(&pchan->rxbuf_lock, flags); if (events & (GIPC_EVENT_RESET)) pr_err("hab gipc %s remote vmid %d RESET\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RESETINPROGRESS)) pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RECEIVEREADY)) physical_channel_rx_dispatch_common(physical_channel); if (events & (GIPC_EVENT_SENDREADY)) pr_debug("kgipc send ready\n"); } drivers/soc/qcom/hab/hab.c +1 −367 Original line number Diff line number Diff line /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -999,11 +999,6 @@ int do_hab_parse(void) return result; } unsigned int get_refcnt(struct kref ref) { return kref_read(&ref); } void hab_hypervisor_unregister_common(void) { int status, i; Loading Loading @@ -1040,364 +1035,3 @@ void hab_hypervisor_unregister_common(void) } spin_unlock_bh(&hab_driver.drvlock); } static int hab_open(struct inode *inodep, struct file *filep) { int result = 0; struct uhab_context *ctx; ctx = hab_ctx_alloc(0); if (!ctx) { pr_err("hab_ctx_alloc failed\n"); filep->private_data = NULL; return -ENOMEM; } ctx->owner = task_pid_nr(current); filep->private_data = ctx; pr_debug("ctx owner %d refcnt %d\n", ctx->owner, get_refcnt(ctx->refcount)); return result; } static int hab_release(struct inode *inodep, struct file *filep) { struct uhab_context *ctx = filep->private_data; struct virtual_channel *vchan, *tmp; struct hab_open_node *node; if (!ctx) return 0; pr_debug("inode %pK, filep %pK ctx %pK\n", inodep, filep, ctx); write_lock(&ctx->ctx_lock); /* notify remote side on vchan closing */ list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) { /* local close starts */ vchan->closed = 1; list_del(&vchan->node); /* vchan is not in this ctx anymore */ ctx->vcnt--; write_unlock(&ctx->ctx_lock); hab_vchan_stop_notify(vchan); hab_vchan_put(vchan); /* there is a lock inside */ write_lock(&ctx->ctx_lock); } /* notify remote side on pending open */ list_for_each_entry(node, &ctx->pending_open, node) { /* no touch to the list itself. it is allocated on the stack */ if (hab_open_cancel_notify(&node->request)) pr_err("failed to send open cancel vcid %x subid %d openid %d pchan %s\n", node->request.xdata.vchan_id, node->request.xdata.sub_id, node->request.xdata.open_id, node->request.pchan->habdev->name); } write_unlock(&ctx->ctx_lock); hab_ctx_put(ctx); filep->private_data = NULL; return 0; } static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct uhab_context *ctx = (struct uhab_context *)filep->private_data; struct hab_open *open_param; struct hab_close *close_param; struct hab_recv *recv_param; struct hab_send *send_param; struct hab_info *info_param; struct hab_message *msg = NULL; void *send_data; unsigned char data[256] = { 0 }; long ret = 0; char names[30]; if (_IOC_SIZE(cmd) && (cmd & IOC_IN)) { if (_IOC_SIZE(cmd) > sizeof(data)) return -EINVAL; if (copy_from_user(data, (void __user *)arg, _IOC_SIZE(cmd))) { pr_err("copy_from_user failed cmd=%x size=%d\n", cmd, _IOC_SIZE(cmd)); return -EFAULT; } } switch (cmd) { case IOCTL_HAB_VC_OPEN: open_param = (struct hab_open *)data; ret = hab_vchan_open(ctx, open_param->mmid, &open_param->vcid, open_param->timeout, open_param->flags); break; case IOCTL_HAB_VC_CLOSE: close_param = (struct hab_close *)data; hab_vchan_close(ctx, close_param->vcid); break; case IOCTL_HAB_SEND: send_param = (struct hab_send *)data; if (send_param->sizebytes > HAB_HEADER_SIZE_MASK) { ret = -EINVAL; break; } send_data = kzalloc(send_param->sizebytes, GFP_KERNEL); if (!send_data) { ret = -ENOMEM; break; } if (copy_from_user(send_data, (void __user *)send_param->data, send_param->sizebytes)) { ret = -EFAULT; } else { ret = hab_vchan_send(ctx, send_param->vcid, send_param->sizebytes, send_data, send_param->flags); } kfree(send_data); break; case IOCTL_HAB_RECV: recv_param = (struct hab_recv *)data; if (!recv_param->data) { ret = -EINVAL; break; } ret = hab_vchan_recv(ctx, &msg, recv_param->vcid, &recv_param->sizebytes, recv_param->flags); if (ret == 0 && msg) { if (copy_to_user((void __user *)recv_param->data, msg->data, msg->sizebytes)) { pr_err("copy_to_user failed: vc=%x size=%d\n", recv_param->vcid, (int)msg->sizebytes); recv_param->sizebytes = 0; ret = -EFAULT; } } else if (ret && msg) { pr_warn("vcid %X recv failed %d and msg is still of %zd bytes\n", recv_param->vcid, (int)ret, msg->sizebytes); } if (msg) hab_msg_free(msg); break; case IOCTL_HAB_VC_EXPORT: ret = hab_mem_export(ctx, (struct hab_export *)data, 0); break; case IOCTL_HAB_VC_IMPORT: ret = hab_mem_import(ctx, (struct hab_import *)data, 0); break; case IOCTL_HAB_VC_UNEXPORT: ret = hab_mem_unexport(ctx, (struct hab_unexport *)data, 0); break; case IOCTL_HAB_VC_UNIMPORT: ret = hab_mem_unimport(ctx, (struct hab_unimport *)data, 0); break; case IOCTL_HAB_VC_QUERY: info_param = (struct hab_info *)data; if (!info_param->names || !info_param->namesize || info_param->namesize > sizeof(names)) { pr_err("wrong param for vm info vcid %X, names %llX, sz %d\n", info_param->vcid, info_param->names, info_param->namesize); ret = -EINVAL; break; } ret = hab_vchan_query(ctx, info_param->vcid, (uint64_t *)&info_param->ids, names, info_param->namesize, 0); if (!ret) { if (copy_to_user((void __user *)info_param->names, names, info_param->namesize)) { pr_err("copy_to_user failed: vc=%x size=%d\n", info_param->vcid, info_param->namesize*2); info_param->namesize = 0; ret = -EFAULT; } } break; default: ret = -ENOIOCTLCMD; } if (_IOC_SIZE(cmd) && (cmd & IOC_OUT)) if (copy_to_user((void __user *) arg, data, _IOC_SIZE(cmd))) { pr_err("copy_to_user failed: cmd=%x\n", cmd); ret = -EFAULT; } return ret; } static long hab_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { return hab_ioctl(filep, cmd, arg); } static const struct file_operations hab_fops = { .owner = THIS_MODULE, .open = hab_open, .release = hab_release, .mmap = habmem_imp_hyp_mmap, .unlocked_ioctl = hab_ioctl, .compat_ioctl = hab_compat_ioctl }; /* * These map sg functions are pass through because the memory backing the * sg list is already accessible to the kernel as they come from a the * dedicated shared vm pool */ static int hab_map_sg(struct device *dev, struct scatterlist *sgl, int nelems, enum dma_data_direction dir, unsigned long attrs) { /* return nelems directly */ return nelems; } static void hab_unmap_sg(struct device *dev, struct scatterlist *sgl, int nelems, enum dma_data_direction dir, unsigned long attrs) { /*Do nothing */ } static const struct dma_map_ops hab_dma_ops = { .map_sg = hab_map_sg, .unmap_sg = hab_unmap_sg, }; static int hab_power_down_callback( struct notifier_block *nfb, unsigned long action, void *data) { switch (action) { case SYS_DOWN: case SYS_HALT: case SYS_POWER_OFF: pr_debug("reboot called %ld\n", action); hab_hypervisor_unregister(); /* only for single VM guest */ break; } pr_debug("reboot called %ld done\n", action); return NOTIFY_DONE; } static struct notifier_block hab_reboot_notifier = { .notifier_call = hab_power_down_callback, }; static int __init hab_init(void) { int result; dev_t dev; result = alloc_chrdev_region(&hab_driver.major, 0, 1, "hab"); if (result < 0) { pr_err("alloc_chrdev_region failed: %d\n", result); return result; } cdev_init(&hab_driver.cdev, &hab_fops); hab_driver.cdev.owner = THIS_MODULE; hab_driver.cdev.ops = &hab_fops; dev = MKDEV(MAJOR(hab_driver.major), 0); result = cdev_add(&hab_driver.cdev, dev, 1); if (result < 0) { unregister_chrdev_region(dev, 1); pr_err("cdev_add failed: %d\n", result); return result; } hab_driver.class = class_create(THIS_MODULE, "hab"); if (IS_ERR(hab_driver.class)) { result = PTR_ERR(hab_driver.class); pr_err("class_create failed: %d\n", result); goto err; } hab_driver.dev = device_create(hab_driver.class, NULL, dev, &hab_driver, "hab"); if (IS_ERR(hab_driver.dev)) { result = PTR_ERR(hab_driver.dev); pr_err("device_create failed: %d\n", result); goto err; } result = register_reboot_notifier(&hab_reboot_notifier); if (result) pr_err("failed to register reboot notifier %d\n", result); /* read in hab config, then configure pchans */ result = do_hab_parse(); if (!result) { hab_driver.kctx = hab_ctx_alloc(1); if (!hab_driver.kctx) { pr_err("hab_ctx_alloc failed"); result = -ENOMEM; hab_hypervisor_unregister(); goto err; } else set_dma_ops(hab_driver.dev, &hab_dma_ops); } hab_stat_init(&hab_driver); return result; err: if (!IS_ERR_OR_NULL(hab_driver.dev)) device_destroy(hab_driver.class, dev); if (!IS_ERR_OR_NULL(hab_driver.class)) class_destroy(hab_driver.class); cdev_del(&hab_driver.cdev); unregister_chrdev_region(dev, 1); pr_err("Error in hab init, result %d\n", result); return result; } static void __exit hab_exit(void) { dev_t dev; hab_hypervisor_unregister(); hab_stat_deinit(&hab_driver); hab_ctx_put(hab_driver.kctx); dev = MKDEV(MAJOR(hab_driver.major), 0); device_destroy(hab_driver.class, dev); class_destroy(hab_driver.class); cdev_del(&hab_driver.cdev); unregister_chrdev_region(dev, 1); unregister_reboot_notifier(&hab_reboot_notifier); pr_debug("hab exit called\n"); } subsys_initcall(hab_init); module_exit(hab_exit); MODULE_DESCRIPTION("Hypervisor abstraction layer"); MODULE_LICENSE("GPL v2"); drivers/soc/qcom/hab/hab.h +6 −33 Original line number Diff line number Diff line Loading @@ -13,39 +13,7 @@ #ifndef __HAB_H #define __HAB_H #ifdef pr_fmt #undef pr_fmt #endif #define pr_fmt(fmt) "hab:%s:%d " fmt, __func__, __LINE__ #include <linux/types.h> #include <linux/habmm.h> #include <linux/hab_ioctl.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/sched.h> #include <linux/cdev.h> #include <linux/list.h> #include <linux/spinlock.h> #include <linux/rbtree.h> #include <linux/idr.h> #include <linux/module.h> #include <linux/uaccess.h> #include <linux/dma-direction.h> #include <linux/dma-mapping.h> #include <linux/jiffies.h> #include <linux/reboot.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/delay.h> #include "hab_os.h" /* OS-specific part in the core header file */ enum hab_payload_type { HAB_PAYLOAD_TYPE_MSG = 0x0, Loading Loading @@ -520,12 +488,16 @@ static inline void hab_ctx_put(struct uhab_context *ctx) } void hab_send_close_msg(struct virtual_channel *vchan); int hab_hypervisor_register(void); int hab_hypervisor_register_os(void); void hab_hypervisor_unregister(void); void hab_hypervisor_unregister_common(void); int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote, struct hab_device *mmid_device); int habhyp_commdev_dealloc(void *commdev); void habhyp_commdev_dealloc_os(void *commdev); int habhyp_commdev_create_dispatcher(struct physical_channel *pchan); int physical_channel_read(struct physical_channel *pchan, void *payload, Loading @@ -536,6 +508,7 @@ int physical_channel_send(struct physical_channel *pchan, void *payload); void physical_channel_rx_dispatch(unsigned long physical_channel); void physical_channel_rx_dispatch_common(unsigned long physical_channel); int loopback_pchan_create(struct hab_device *dev, char *pchan_name); Loading Loading
drivers/soc/qcom/hab/Makefile +11 −4 Original line number Diff line number Diff line Loading @@ -6,16 +6,21 @@ msm_hab-objs = \ hab_pchan.o \ hab_open.o \ hab_mimex.o \ hab_mem_linux.o \ hab_pipe.o \ hab_parser.o \ khab_test.o \ hab_stat.o msm_hab_linux-objs = \ hab_linux.o \ hab_mem_linux.o ifdef CONFIG_GHS_VMM msm_hab_hyp-objs = \ ghs_comm.o \ hab_ghs.o ghs_comm_linux.o \ hab_ghs.o \ hab_ghs_linux.o ifndef CONFIG_MSM_AGL ccflags-y += -DHABMM_HC_VMID Loading @@ -25,7 +30,9 @@ else ifdef CONFIG_QTI_GVM_QUIN msm_hab_hyp-objs = \ qvm_comm.o \ hab_qvm.o qvm_comm_linux.o \ hab_qvm.o \ hab_qvm_linux.o else msm_hab_hyp-objs = \ hab_comm.o \ Loading @@ -33,4 +40,4 @@ msm_hab_hyp-objs = \ endif endif obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_hyp.o obj-$(CONFIG_MSM_HAB) += msm_hab.o msm_hab_linux.o msm_hab_hyp.o
drivers/soc/qcom/hab/ghs_comm.c +28 −40 Original line number Diff line number Diff line Loading @@ -47,6 +47,13 @@ int physical_channel_send(struct physical_channel *pchan, hab_spin_lock(&dev->io_lock, irqs_disabled); result = hab_gipc_wait_to_send(dev->endpoint); if (result != GIPC_Success) { hab_spin_unlock(&dev->io_lock, irqs_disabled); pr_err("failed to wait to send %d\n", result); return -EBUSY; } result = GIPC_PrepareMessage(dev->endpoint, sizebytes+sizeof(*header), (void **)&msg); if (result == GIPC_Full) { Loading Loading @@ -89,30 +96,15 @@ int physical_channel_send(struct physical_channel *pchan, return 0; } void physical_channel_rx_dispatch(unsigned long physical_channel) void physical_channel_rx_dispatch_common(unsigned long physical_channel) { struct hab_header header; struct physical_channel *pchan = (struct physical_channel *)physical_channel; struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data; GIPC_Result result; uint32_t events; unsigned long flags; int irqs_disabled = irqs_disabled(); spin_lock_irqsave(&pchan->rxbuf_lock, flags); events = kgipc_dequeue_events(dev->endpoint); spin_unlock_irqrestore(&pchan->rxbuf_lock, flags); if (events & (GIPC_EVENT_RESET)) pr_err("hab gipc %s remote vmid %d RESET\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RESETINPROGRESS)) pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RECEIVEREADY)) { hab_spin_lock(&pchan->rxbuf_lock, irqs_disabled); while (1) { dev->read_size = 0; Loading @@ -137,7 +129,3 @@ void physical_channel_rx_dispatch(unsigned long physical_channel) } hab_spin_unlock(&pchan->rxbuf_lock, irqs_disabled); } if (events & (GIPC_EVENT_SENDREADY)) pr_debug("kgipc send ready\n"); }
drivers/soc/qcom/hab/ghs_comm_linux.c 0 → 100644 +49 −0 Original line number Diff line number Diff line /* Copyright (c) 2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "hab.h" #include "hab_ghs.h" inline int hab_gipc_wait_to_send(GIPC_Endpoint endpoint) { (void)endpoint; return GIPC_Success; } void physical_channel_rx_dispatch(unsigned long physical_channel) { struct physical_channel *pchan = (struct physical_channel *)physical_channel; struct ghs_vdev *dev = (struct ghs_vdev *)pchan->hyp_data; uint32_t events; unsigned long flags; spin_lock_irqsave(&pchan->rxbuf_lock, flags); events = kgipc_dequeue_events(dev->endpoint); spin_unlock_irqrestore(&pchan->rxbuf_lock, flags); if (events & (GIPC_EVENT_RESET)) pr_err("hab gipc %s remote vmid %d RESET\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RESETINPROGRESS)) pr_err("hab gipc %s remote vmid %d RESETINPROGRESS\n", dev->name, pchan->vmid_remote); if (events & (GIPC_EVENT_RECEIVEREADY)) physical_channel_rx_dispatch_common(physical_channel); if (events & (GIPC_EVENT_SENDREADY)) pr_debug("kgipc send ready\n"); }
drivers/soc/qcom/hab/hab.c +1 −367 Original line number Diff line number Diff line /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -999,11 +999,6 @@ int do_hab_parse(void) return result; } unsigned int get_refcnt(struct kref ref) { return kref_read(&ref); } void hab_hypervisor_unregister_common(void) { int status, i; Loading Loading @@ -1040,364 +1035,3 @@ void hab_hypervisor_unregister_common(void) } spin_unlock_bh(&hab_driver.drvlock); } static int hab_open(struct inode *inodep, struct file *filep) { int result = 0; struct uhab_context *ctx; ctx = hab_ctx_alloc(0); if (!ctx) { pr_err("hab_ctx_alloc failed\n"); filep->private_data = NULL; return -ENOMEM; } ctx->owner = task_pid_nr(current); filep->private_data = ctx; pr_debug("ctx owner %d refcnt %d\n", ctx->owner, get_refcnt(ctx->refcount)); return result; } static int hab_release(struct inode *inodep, struct file *filep) { struct uhab_context *ctx = filep->private_data; struct virtual_channel *vchan, *tmp; struct hab_open_node *node; if (!ctx) return 0; pr_debug("inode %pK, filep %pK ctx %pK\n", inodep, filep, ctx); write_lock(&ctx->ctx_lock); /* notify remote side on vchan closing */ list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) { /* local close starts */ vchan->closed = 1; list_del(&vchan->node); /* vchan is not in this ctx anymore */ ctx->vcnt--; write_unlock(&ctx->ctx_lock); hab_vchan_stop_notify(vchan); hab_vchan_put(vchan); /* there is a lock inside */ write_lock(&ctx->ctx_lock); } /* notify remote side on pending open */ list_for_each_entry(node, &ctx->pending_open, node) { /* no touch to the list itself. it is allocated on the stack */ if (hab_open_cancel_notify(&node->request)) pr_err("failed to send open cancel vcid %x subid %d openid %d pchan %s\n", node->request.xdata.vchan_id, node->request.xdata.sub_id, node->request.xdata.open_id, node->request.pchan->habdev->name); } write_unlock(&ctx->ctx_lock); hab_ctx_put(ctx); filep->private_data = NULL; return 0; } static long hab_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct uhab_context *ctx = (struct uhab_context *)filep->private_data; struct hab_open *open_param; struct hab_close *close_param; struct hab_recv *recv_param; struct hab_send *send_param; struct hab_info *info_param; struct hab_message *msg = NULL; void *send_data; unsigned char data[256] = { 0 }; long ret = 0; char names[30]; if (_IOC_SIZE(cmd) && (cmd & IOC_IN)) { if (_IOC_SIZE(cmd) > sizeof(data)) return -EINVAL; if (copy_from_user(data, (void __user *)arg, _IOC_SIZE(cmd))) { pr_err("copy_from_user failed cmd=%x size=%d\n", cmd, _IOC_SIZE(cmd)); return -EFAULT; } } switch (cmd) { case IOCTL_HAB_VC_OPEN: open_param = (struct hab_open *)data; ret = hab_vchan_open(ctx, open_param->mmid, &open_param->vcid, open_param->timeout, open_param->flags); break; case IOCTL_HAB_VC_CLOSE: close_param = (struct hab_close *)data; hab_vchan_close(ctx, close_param->vcid); break; case IOCTL_HAB_SEND: send_param = (struct hab_send *)data; if (send_param->sizebytes > HAB_HEADER_SIZE_MASK) { ret = -EINVAL; break; } send_data = kzalloc(send_param->sizebytes, GFP_KERNEL); if (!send_data) { ret = -ENOMEM; break; } if (copy_from_user(send_data, (void __user *)send_param->data, send_param->sizebytes)) { ret = -EFAULT; } else { ret = hab_vchan_send(ctx, send_param->vcid, send_param->sizebytes, send_data, send_param->flags); } kfree(send_data); break; case IOCTL_HAB_RECV: recv_param = (struct hab_recv *)data; if (!recv_param->data) { ret = -EINVAL; break; } ret = hab_vchan_recv(ctx, &msg, recv_param->vcid, &recv_param->sizebytes, recv_param->flags); if (ret == 0 && msg) { if (copy_to_user((void __user *)recv_param->data, msg->data, msg->sizebytes)) { pr_err("copy_to_user failed: vc=%x size=%d\n", recv_param->vcid, (int)msg->sizebytes); recv_param->sizebytes = 0; ret = -EFAULT; } } else if (ret && msg) { pr_warn("vcid %X recv failed %d and msg is still of %zd bytes\n", recv_param->vcid, (int)ret, msg->sizebytes); } if (msg) hab_msg_free(msg); break; case IOCTL_HAB_VC_EXPORT: ret = hab_mem_export(ctx, (struct hab_export *)data, 0); break; case IOCTL_HAB_VC_IMPORT: ret = hab_mem_import(ctx, (struct hab_import *)data, 0); break; case IOCTL_HAB_VC_UNEXPORT: ret = hab_mem_unexport(ctx, (struct hab_unexport *)data, 0); break; case IOCTL_HAB_VC_UNIMPORT: ret = hab_mem_unimport(ctx, (struct hab_unimport *)data, 0); break; case IOCTL_HAB_VC_QUERY: info_param = (struct hab_info *)data; if (!info_param->names || !info_param->namesize || info_param->namesize > sizeof(names)) { pr_err("wrong param for vm info vcid %X, names %llX, sz %d\n", info_param->vcid, info_param->names, info_param->namesize); ret = -EINVAL; break; } ret = hab_vchan_query(ctx, info_param->vcid, (uint64_t *)&info_param->ids, names, info_param->namesize, 0); if (!ret) { if (copy_to_user((void __user *)info_param->names, names, info_param->namesize)) { pr_err("copy_to_user failed: vc=%x size=%d\n", info_param->vcid, info_param->namesize*2); info_param->namesize = 0; ret = -EFAULT; } } break; default: ret = -ENOIOCTLCMD; } if (_IOC_SIZE(cmd) && (cmd & IOC_OUT)) if (copy_to_user((void __user *) arg, data, _IOC_SIZE(cmd))) { pr_err("copy_to_user failed: cmd=%x\n", cmd); ret = -EFAULT; } return ret; } static long hab_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { return hab_ioctl(filep, cmd, arg); } static const struct file_operations hab_fops = { .owner = THIS_MODULE, .open = hab_open, .release = hab_release, .mmap = habmem_imp_hyp_mmap, .unlocked_ioctl = hab_ioctl, .compat_ioctl = hab_compat_ioctl }; /* * These map sg functions are pass through because the memory backing the * sg list is already accessible to the kernel as they come from a the * dedicated shared vm pool */ static int hab_map_sg(struct device *dev, struct scatterlist *sgl, int nelems, enum dma_data_direction dir, unsigned long attrs) { /* return nelems directly */ return nelems; } static void hab_unmap_sg(struct device *dev, struct scatterlist *sgl, int nelems, enum dma_data_direction dir, unsigned long attrs) { /*Do nothing */ } static const struct dma_map_ops hab_dma_ops = { .map_sg = hab_map_sg, .unmap_sg = hab_unmap_sg, }; static int hab_power_down_callback( struct notifier_block *nfb, unsigned long action, void *data) { switch (action) { case SYS_DOWN: case SYS_HALT: case SYS_POWER_OFF: pr_debug("reboot called %ld\n", action); hab_hypervisor_unregister(); /* only for single VM guest */ break; } pr_debug("reboot called %ld done\n", action); return NOTIFY_DONE; } static struct notifier_block hab_reboot_notifier = { .notifier_call = hab_power_down_callback, }; static int __init hab_init(void) { int result; dev_t dev; result = alloc_chrdev_region(&hab_driver.major, 0, 1, "hab"); if (result < 0) { pr_err("alloc_chrdev_region failed: %d\n", result); return result; } cdev_init(&hab_driver.cdev, &hab_fops); hab_driver.cdev.owner = THIS_MODULE; hab_driver.cdev.ops = &hab_fops; dev = MKDEV(MAJOR(hab_driver.major), 0); result = cdev_add(&hab_driver.cdev, dev, 1); if (result < 0) { unregister_chrdev_region(dev, 1); pr_err("cdev_add failed: %d\n", result); return result; } hab_driver.class = class_create(THIS_MODULE, "hab"); if (IS_ERR(hab_driver.class)) { result = PTR_ERR(hab_driver.class); pr_err("class_create failed: %d\n", result); goto err; } hab_driver.dev = device_create(hab_driver.class, NULL, dev, &hab_driver, "hab"); if (IS_ERR(hab_driver.dev)) { result = PTR_ERR(hab_driver.dev); pr_err("device_create failed: %d\n", result); goto err; } result = register_reboot_notifier(&hab_reboot_notifier); if (result) pr_err("failed to register reboot notifier %d\n", result); /* read in hab config, then configure pchans */ result = do_hab_parse(); if (!result) { hab_driver.kctx = hab_ctx_alloc(1); if (!hab_driver.kctx) { pr_err("hab_ctx_alloc failed"); result = -ENOMEM; hab_hypervisor_unregister(); goto err; } else set_dma_ops(hab_driver.dev, &hab_dma_ops); } hab_stat_init(&hab_driver); return result; err: if (!IS_ERR_OR_NULL(hab_driver.dev)) device_destroy(hab_driver.class, dev); if (!IS_ERR_OR_NULL(hab_driver.class)) class_destroy(hab_driver.class); cdev_del(&hab_driver.cdev); unregister_chrdev_region(dev, 1); pr_err("Error in hab init, result %d\n", result); return result; } static void __exit hab_exit(void) { dev_t dev; hab_hypervisor_unregister(); hab_stat_deinit(&hab_driver); hab_ctx_put(hab_driver.kctx); dev = MKDEV(MAJOR(hab_driver.major), 0); device_destroy(hab_driver.class, dev); class_destroy(hab_driver.class); cdev_del(&hab_driver.cdev); unregister_chrdev_region(dev, 1); unregister_reboot_notifier(&hab_reboot_notifier); pr_debug("hab exit called\n"); } subsys_initcall(hab_init); module_exit(hab_exit); MODULE_DESCRIPTION("Hypervisor abstraction layer"); MODULE_LICENSE("GPL v2");
drivers/soc/qcom/hab/hab.h +6 −33 Original line number Diff line number Diff line Loading @@ -13,39 +13,7 @@ #ifndef __HAB_H #define __HAB_H #ifdef pr_fmt #undef pr_fmt #endif #define pr_fmt(fmt) "hab:%s:%d " fmt, __func__, __LINE__ #include <linux/types.h> #include <linux/habmm.h> #include <linux/hab_ioctl.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/sched.h> #include <linux/cdev.h> #include <linux/list.h> #include <linux/spinlock.h> #include <linux/rbtree.h> #include <linux/idr.h> #include <linux/module.h> #include <linux/uaccess.h> #include <linux/dma-direction.h> #include <linux/dma-mapping.h> #include <linux/jiffies.h> #include <linux/reboot.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/delay.h> #include "hab_os.h" /* OS-specific part in the core header file */ enum hab_payload_type { HAB_PAYLOAD_TYPE_MSG = 0x0, Loading Loading @@ -520,12 +488,16 @@ static inline void hab_ctx_put(struct uhab_context *ctx) } void hab_send_close_msg(struct virtual_channel *vchan); int hab_hypervisor_register(void); int hab_hypervisor_register_os(void); void hab_hypervisor_unregister(void); void hab_hypervisor_unregister_common(void); int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote, struct hab_device *mmid_device); int habhyp_commdev_dealloc(void *commdev); void habhyp_commdev_dealloc_os(void *commdev); int habhyp_commdev_create_dispatcher(struct physical_channel *pchan); int physical_channel_read(struct physical_channel *pchan, void *payload, Loading @@ -536,6 +508,7 @@ int physical_channel_send(struct physical_channel *pchan, void *payload); void physical_channel_rx_dispatch(unsigned long physical_channel); void physical_channel_rx_dispatch_common(unsigned long physical_channel); int loopback_pchan_create(struct hab_device *dev, char *pchan_name); Loading