hab_mem_linux.o \
hab_pipe.o \
qvm_comm.o \
- hab_qvm.o
+ hab_qvm.o \
+ hab_parser.o
obj-$(CONFIG_MSM_HAB) += msm_hab.o
.openlock = __SPIN_LOCK_UNLOCKED(&hab_devices[__num__].openlock)\
}
-/* the following has to match habmm definitions, order does not matter */
+/*
+ * The following has to match habmm definitions, order does not matter if
+ * hab config does not care either. When hab config is not present, the default
+ * is as guest VM all pchans are pchan opener (FE)
+ */
static struct hab_device hab_devices[] = {
HAB_DEVICE_CNSTR(DEVICE_AUD1_NAME, MM_AUD_1, 0),
HAB_DEVICE_CNSTR(DEVICE_AUD2_NAME, MM_AUD_2, 1),
int32_t *vcid,
uint32_t flags)
{
- struct virtual_channel *vchan;
+ struct virtual_channel *vchan = NULL;
+ struct hab_device *dev;
if (!vcid)
return -EINVAL;
vchan = frontend_open(ctx, mmid, LOOPBACK_DOM);
}
} else {
- if (hab_driver.b_server_dom)
- vchan = backend_listen(ctx, mmid);
- else
- vchan = frontend_open(ctx, mmid, 0);
+ dev = find_hab_device(mmid);
+
+ if (dev) {
+ struct physical_channel *pchan =
+ hab_pchan_find_domid(dev, HABCFG_VMID_DONT_CARE);
+
+ if (pchan->is_be)
+ vchan = backend_listen(ctx, mmid);
+ else
+ vchan = frontend_open(ctx, mmid,
+ HABCFG_VMID_DONT_CARE);
+ } else {
+ pr_err("failed to find device, mmid %d\n", mmid);
+ }
}
if (IS_ERR(vchan))
write_unlock(&ctx->ctx_lock);
}
+/*
+ * To name the pchan - the pchan has two ends, either FE or BE locally.
+ * if is_be is true, then this is listener for BE. pchane name use remote
+ * FF's vmid from the table.
+ * if is_be is false, then local is FE as opener. pchan name use local FE's
+ * vmid (self)
+ */
+static int hab_initialize_pchan_entry(struct hab_device *mmid_device,
+ int vmid_local, int vmid_remote, int is_be)
+{
+ char pchan_name[MAX_VMID_NAME_SIZE];
+ struct physical_channel *pchan = NULL;
+ int ret;
+ int vmid = is_be ? vmid_remote : vmid_local;
+
+ if (!mmid_device) {
+ pr_err("habdev %pK, vmid local %d, remote %d, is be %d\n",
+ mmid_device, vmid_local, vmid_remote, is_be);
+ return -EINVAL;
+ }
+
+ snprintf(pchan_name, MAX_VMID_NAME_SIZE, "vm%d-", vmid);
+ strlcat(pchan_name, mmid_device->name, MAX_VMID_NAME_SIZE);
+
+ ret = habhyp_commdev_alloc((void **)&pchan, is_be, pchan_name,
+ vmid_remote, mmid_device);
+ if (ret == 0) {
+ pr_debug("pchan %s added, vmid local %d, remote %d, is_be %d, total %d\n",
+ pchan_name, vmid_local, vmid_remote, is_be,
+ mmid_device->pchan_cnt);
+ } else {
+ pr_err("failed %d to allocate pchan %s, vmid local %d, remote %d, is_be %d, total %d\n",
+ ret, pchan_name, vmid_local, vmid_remote,
+ is_be, mmid_device->pchan_cnt);
+ }
+
+ return ret;
+}
+
+static void hab_generate_pchan(struct local_vmid *settings, int i, int j)
+{
+ int k, ret = 0;
+
+ pr_debug("%d as mmid %d in vmid %d\n",
+ HABCFG_GET_MMID(settings, i, j), j, i);
+
+ switch (HABCFG_GET_MMID(settings, i, j)) {
+ case MM_AUD_START/100:
+ for (k = MM_AUD_START + 1; k < MM_AUD_END; k++) {
+ /*
+ * if this local pchan end is BE, then use
+ * remote FE's vmid. If local end is FE, then
+ * use self vmid
+ */
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ case MM_CAM_START/100:
+ for (k = MM_CAM_START + 1; k < MM_CAM_END; k++) {
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ case MM_DISP_START/100:
+ for (k = MM_DISP_START + 1; k < MM_DISP_END; k++) {
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ case MM_GFX_START/100:
+ for (k = MM_GFX_START + 1; k < MM_GFX_END; k++) {
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ case MM_VID_START/100:
+ for (k = MM_VID_START + 1; k < MM_VID_END; k++) {
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ case MM_MISC_START/100:
+ for (k = MM_MISC_START + 1; k < MM_MISC_END; k++) {
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ case MM_QCPE_START/100:
+ for (k = MM_QCPE_START + 1; k < MM_QCPE_END; k++) {
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ case MM_CLK_START/100:
+ for (k = MM_CLK_START + 1; k < MM_CLK_END; k++) {
+ ret += hab_initialize_pchan_entry(
+ find_hab_device(k),
+ settings->self,
+ HABCFG_GET_VMID(settings, i),
+ HABCFG_GET_BE(settings, i, j));
+ }
+ break;
+
+ default:
+ pr_err("failed to find mmid %d, i %d, j %d\n",
+ HABCFG_GET_MMID(settings, i, j), i, j);
+
+ break;
+ }
+}
+
+/*
+ * generate pchan list based on hab settings table.
+ * return status 0: success, otherwise failure
+ */
+static int hab_generate_pchan_list(struct local_vmid *settings)
+{
+ int i, j;
+
+ /* scan by valid VMs, then mmid */
+ pr_debug("self vmid is %d\n", settings->self);
+ for (i = 0; i < HABCFG_VMID_MAX; i++) {
+ if (HABCFG_GET_VMID(settings, i) != HABCFG_VMID_INVALID &&
+ HABCFG_GET_VMID(settings, i) != settings->self) {
+ pr_debug("create pchans for vm %d\n", i);
+
+ for (j = 1; j <= HABCFG_MMID_AREA_MAX; j++) {
+ if (HABCFG_GET_MMID(settings, i, j)
+ != HABCFG_VMID_INVALID)
+ hab_generate_pchan(settings, i, j);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function checks hypervisor plug-in readiness, read in hab configs,
+ * and configure pchans
+ */
+int do_hab_parse(void)
+{
+ int result;
+ int i;
+ struct hab_device *device;
+ int pchan_total = 0;
+
+ /* first check if hypervisor plug-in is ready */
+ result = hab_hypervisor_register();
+ if (result) {
+ pr_err("register HYP plug-in failed, ret %d\n", result);
+ return result;
+ }
+
+ /* Initialize open Q before first pchan starts */
+ for (i = 0; i < hab_driver.ndevices; i++) {
+ device = &hab_driver.devp[i];
+ init_waitqueue_head(&device->openq);
+ }
+
+ /* read in hab config and create pchans*/
+ memset(&hab_driver.settings, HABCFG_VMID_INVALID,
+ sizeof(hab_driver.settings));
+
+ pr_debug("prepare default gvm 2 settings...\n");
+ fill_default_gvm_settings(&hab_driver.settings, 2,
+ MM_AUD_START, MM_ID_MAX);
+
+ /* now generate hab pchan list */
+ result = hab_generate_pchan_list(&hab_driver.settings);
+ if (result) {
+ pr_err("generate pchan list failed, ret %d\n", result);
+ } else {
+ for (i = 0; i < hab_driver.ndevices; i++) {
+ device = &hab_driver.devp[i];
+ pchan_total += device->pchan_cnt;
+ }
+ pr_debug("ret %d, total %d pchans added, ndevices %d\n",
+ result, pchan_total, hab_driver.ndevices);
+ }
+
+ return result;
+}
+
static int hab_open(struct inode *inodep, struct file *filep)
{
int result = 0;
goto err;
}
- for (i = 0; i < hab_driver.ndevices; i++) {
- device = &hab_driver.devp[i];
- init_waitqueue_head(&device->openq);
- }
-
- hab_hypervisor_register();
+ /* read in hab config, then configure pchans */
+ result = do_hab_parse();
- hab_driver.kctx = hab_ctx_alloc(1);
- if (!hab_driver.kctx) {
- pr_err("hab_ctx_alloc failed");
- result = -ENOMEM;
- hab_hypervisor_unregister();
- goto err;
- }
+ 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;
+ }
- set_dma_ops(hab_driver.dev, &hab_dma_ops);
+ set_dma_ops(hab_driver.dev, &hab_dma_ops);
- return result;
+ return result;
+ }
err:
if (!IS_ERR_OR_NULL(hab_driver.dev))
cdev_del(&hab_driver.cdev);
unregister_chrdev_region(dev, 1);
+ pr_err("Error in hab init, result %d\n", result);
return result;
}
#define DEVICE_CLK1_NAME "hab_clock_vm1"
#define DEVICE_CLK2_NAME "hab_clock_vm2"
+/* make sure concascaded name is less than this value */
+#define MAX_VMID_NAME_SIZE 30
+
+#define HABCFG_FILE_SIZE_MAX 256
+#define HABCFG_MMID_AREA_MAX (MM_ID_MAX/100)
+
+#define HABCFG_VMID_MAX 16
+#define HABCFG_VMID_INVALID (-1)
+#define HABCFG_VMID_DONT_CARE (-2)
+
+#define HABCFG_ID_LINE_LIMIT ","
+#define HABCFG_ID_VMID "VMID="
+#define HABCFG_ID_BE "BE="
+#define HABCFG_ID_FE "FE="
+#define HABCFG_ID_MMID "MMID="
+#define HABCFG_ID_RANGE "-"
+#define HABCFG_ID_DONTCARE "X"
+
+#define HABCFG_FOUND_VMID 1
+#define HABCFG_FOUND_FE_MMIDS 2
+#define HABCFG_FOUND_BE_MMIDS 3
+#define HABCFG_FOUND_NOTHING (-1)
+
+#define HABCFG_BE_FALSE 0
+#define HABCFG_BE_TRUE 1
+
+#define HABCFG_GET_VMID(_local_cfg_, _vmid_) \
+ ((settings)->vmid_mmid_list[_vmid_].vmid)
+#define HABCFG_GET_MMID(_local_cfg_, _vmid_, _mmid_) \
+ ((settings)->vmid_mmid_list[_vmid_].mmid[_mmid_])
+#define HABCFG_GET_BE(_local_cfg_, _vmid_, _mmid_) \
+ ((settings)->vmid_mmid_list[_vmid_].is_listener[_mmid_])
+
/* "Size" of the HAB_HEADER_ID and HAB_VCID_ID must match */
#define HAB_HEADER_SIZE_SHIFT 0
#define HAB_HEADER_TYPE_SHIFT 16
};
struct physical_channel {
+ char name[MAX_VMID_NAME_SIZE];
+ int is_be;
struct kref refcount;
struct hab_device *habdev;
struct list_head node;
};
struct hab_device {
- const char *name;
+ char name[MAX_VMID_NAME_SIZE];
unsigned int id;
struct list_head pchannels;
+ int pchan_cnt;
struct mutex pchan_lock;
struct list_head openq_list;
spinlock_t openlock;
int kernel;
};
+/*
+ * array to describe the VM and its MMID configuration as what is connected to
+ * so this is describing a pchan's remote side
+ */
+struct vmid_mmid_desc {
+ int vmid; /* remote vmid */
+ int mmid[HABCFG_MMID_AREA_MAX+1]; /* selected or not */
+ int is_listener[HABCFG_MMID_AREA_MAX+1]; /* yes or no */
+};
+
+struct local_vmid {
+ int32_t self; /* only this field is for local */
+ struct vmid_mmid_desc vmid_mmid_list[HABCFG_VMID_MAX];
+};
+
struct hab_driver {
struct device *dev;
struct cdev cdev;
int ndevices;
struct hab_device *devp;
struct uhab_context *kctx;
+
+ struct local_vmid settings; /* parser results */
+
int b_server_dom;
int loopback_num;
int b_loopback;
void hab_send_close_msg(struct virtual_channel *vchan);
int hab_hypervisor_register(void);
void hab_hypervisor_unregister(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);
int physical_channel_read(struct physical_channel *pchan,
void *payload,
int loopback_pchan_create(char *dev_name);
+int hab_parse(struct local_vmid *settings);
+
+int do_hab_parse(void);
+
+int fill_default_gvm_settings(struct local_vmid *settings,
+ int vmid_local, int mmid_start, int mmid_end);
+
bool hab_is_loopback(void);
/* Global singleton HAB instance */
--- /dev/null
+/* Copyright (c) 2017, 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"
+
+/*
+ * set valid mmid value in tbl to show this is valid entry. All inputs here are
+ * normalized to 1 based integer
+ */
+static int fill_vmid_mmid_tbl(struct vmid_mmid_desc *tbl, int32_t vm_start,
+ int32_t vm_range, int32_t mmid_start,
+ int32_t mmid_range, int32_t be)
+{
+ int ret = 0;
+ int i, j;
+
+ for (i = vm_start; i < vm_start+vm_range; i++) {
+ tbl[i].vmid = i; /* set valid vmid value to make it usable */
+ for (j = mmid_start; j < mmid_start + mmid_range; j++) {
+ /* sanity check */
+ if (tbl[i].mmid[j] != HABCFG_VMID_INVALID) {
+ pr_err("overwrite previous setting, i %d, j %d, be %d\n",
+ i, j, tbl[i].is_listener[j]);
+ }
+ tbl[i].mmid[j] = j;
+ tbl[i].is_listener[j] = be; /* BE IS listen */
+ }
+ }
+
+ return ret;
+}
+
+void dump_settings(struct local_vmid *settings)
+{
+ int i, j;
+
+ pr_debug("self vmid is %d\n", settings->self);
+ for (i = 0; i < HABCFG_VMID_MAX; i++) {
+ pr_debug("remote vmid %d\n",
+ settings->vmid_mmid_list[i].vmid);
+ for (j = 0; j <= HABCFG_MMID_AREA_MAX; j++) {
+ pr_debug("mmid %d, is_be %d\n",
+ settings->vmid_mmid_list[i].mmid[j],
+ settings->vmid_mmid_list[i].is_listener[j]);
+ }
+ }
+}
+
+int fill_default_gvm_settings(struct local_vmid *settings, int vmid_local,
+ int mmid_start, int mmid_end) {
+ settings->self = vmid_local;
+ /* default gvm always talks to host as vm0 */
+ return fill_vmid_mmid_tbl(settings->vmid_mmid_list, 0, 1,
+ mmid_start/100, (mmid_end-mmid_start)/100+1, HABCFG_BE_FALSE);
+}
mutex_lock(&habdev->pchan_lock);
list_add_tail(&pchan->node, &habdev->pchannels);
+ habdev->pchan_cnt++;
mutex_unlock(&habdev->pchan_lock);
return pchan;
mutex_lock(&pchan->habdev->pchan_lock);
list_del(&pchan->node);
+ pchan->habdev->pchan_cnt--;
mutex_unlock(&pchan->habdev->pchan_lock);
kfree(pchan->hyp_data);
kfree(pchan);
mutex_lock(&dev->pchan_lock);
list_for_each_entry(pchan, &dev->pchannels, node)
- if (pchan->dom_id == dom_id)
+ if (pchan->dom_id == dom_id || dom_id == HABCFG_VMID_DONT_CARE)
break;
- if (pchan->dom_id != dom_id)
+ if (pchan->dom_id != dom_id && dom_id != HABCFG_VMID_DONT_CARE)
pchan = NULL;
if (pchan && !kref_get_unless_zero(&pchan->refcount))
return rc;
}
+/*
+ * this is only for guest
+ */
static uint64_t get_guest_factory_paddr(struct qvm_channel *dev,
const char *name, uint32_t pages)
{
}
if (dev->guest_factory->signature != GUEST_SHM_SIGNATURE) {
- pr_err("shmem factory signature incorrect: %ld != %lu\n",
+ pr_err("shmem factory signature incorrect: %ld != %llu\n",
GUEST_SHM_SIGNATURE, dev->guest_factory->signature);
iounmap(dev->guest_factory);
return 0;
return dev->guest_factory->shmem;
}
-static int create_dispatcher(struct physical_channel *pchan, int id)
+static int create_dispatcher(struct physical_channel *pchan)
{
struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data;
int ret;
(unsigned long) pchan);
ret = request_irq(hab_driver.irq, shm_irq_handler, IRQF_SHARED,
- hab_driver.devp[id].name, pchan);
+ pchan->name, pchan);
if (ret)
pr_err("request_irq for %s failed: %d\n",
- hab_driver.devp[id].name, ret);
+ pchan->name, ret);
return ret;
}
pchan->is_be ? 0 : 1);
}
-static struct physical_channel *habhyp_commdev_alloc(int id)
+/*
+ * allocate hypervisor plug-in specific resource for pchan, and call hab pchan
+ * alloc common function. hab driver struct is directly accessed.
+ * commdev: pointer to store the pchan address
+ * id: index to hab_device (mmids)
+ * is_be: pchan local endpoint role
+ * name: pchan name
+ * return: status 0: success, otherwise: failures
+ */
+int habhyp_commdev_alloc(void **commdev, int is_be, char *name,
+ int vmid_remote, struct hab_device *mmid_device)
{
- struct qvm_channel *dev;
- struct physical_channel *pchan = NULL;
- int ret = 0, channel = 0;
+ struct qvm_channel *dev = NULL;
+ struct physical_channel **pchan = (struct physical_channel **)commdev;
+ int ret = 0, coid = 0, channel = 0;
char *shmdata;
uint32_t pipe_alloc_size =
hab_pipe_calc_required_bytes(PIPE_SHMEM_SIZE);
spin_lock_init(&dev->io_lock);
paddr = get_guest_factory_paddr(dev,
- hab_driver.devp[id].name,
+ name,
pipe_alloc_pages);
total_pages = dev->guest_factory->size + 1;
dev->pipe = (struct hab_pipe *) shmdata;
dev->pipe_ep = hab_pipe_init(dev->pipe, PIPE_SHMEM_SIZE,
- dev->be ? 0 : 1);
-
- pchan = hab_pchan_alloc(&hab_driver.devp[id], dev->be);
- if (!pchan) {
+ is_be ? 0 : 1);
+ /* newly created pchan is added to mmid device list */
+ *pchan = hab_pchan_alloc(mmid_device, vmid_remote);
+ if (!(*pchan)) {
ret = -ENOMEM;
goto err;
}
- pchan->closed = 0;
- pchan->hyp_data = (void *)dev;
+ (*pchan)->closed = 0;
+ (*pchan)->hyp_data = (void *)dev;
+ strlcpy((*pchan)->name, name, MAX_VMID_NAME_SIZE);
+ (*pchan)->is_be = is_be;
dev->channel = channel;
+ dev->coid = coid;
- ret = create_dispatcher(pchan, id);
- if (ret < 0)
+ ret = create_dispatcher(*pchan);
+ if (ret)
goto err;
- return pchan;
+ return ret;
err:
kfree(dev);
- if (pchan)
- hab_pchan_put(pchan);
+ if (*pchan)
+ hab_pchan_put(*pchan);
pr_err("habhyp_commdev_alloc failed: %d\n", ret);
- return ERR_PTR(ret);
+ return ret;
+}
+
+int habhyp_commdev_dealloc(void *commdev)
+{
+ struct physical_channel *pchan = (struct physical_channel *)commdev;
+ struct qvm_channel *dev = pchan->hyp_data;
+
+
+ kfree(dev);
+ hab_pchan_put(pchan);
+ return 0;
}
int hab_hypervisor_register(void)
{
- int ret = 0, i;
+ int ret = 0;
hab_driver.b_server_dom = 0;
- /*
- * Can still attempt to instantiate more channels if one fails.
- * Others can be retried later.
- */
- for (i = 0; i < hab_driver.ndevices; i++) {
- if (IS_ERR(habhyp_commdev_alloc(i)))
- ret = -EAGAIN;
- }
+ pr_info("initializing for %s VM\n", hab_driver.b_server_dom ?
+ "host" : "guest");
return ret;
}
void hab_hypervisor_unregister(void)
{
+ int status, i;
+
+ for (i = 0; i < hab_driver.ndevices; i++) {
+ struct hab_device *dev = &hab_driver.devp[i];
+ struct physical_channel *pchan;
+
+ list_for_each_entry(pchan, &dev->pchannels, node) {
+ status = habhyp_commdev_dealloc(pchan);
+ if (status) {
+ pr_err("failed to free pchan %pK, i %d, ret %d\n",
+ pchan, i, status);
+ }
+ }
+ }
}
static int hab_shmem_probe(struct platform_device *pdev)