202 lines
4.6 KiB
C
202 lines
4.6 KiB
C
/* Copyright (c) 2018-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"
|
|
|
|
#define GIPC_VM_SET_CNT 22
|
|
|
|
/* same vmid assignment for all the vms. it should matches dt_gipc_path_name */
|
|
static int mmid_order[GIPC_VM_SET_CNT] = {
|
|
MM_AUD_1,
|
|
MM_AUD_2,
|
|
MM_AUD_3,
|
|
MM_AUD_4,
|
|
MM_CAM_1,
|
|
MM_CAM_2,
|
|
MM_DISP_1,
|
|
MM_DISP_2,
|
|
MM_DISP_3,
|
|
MM_DISP_4,
|
|
MM_DISP_5,
|
|
MM_GFX,
|
|
MM_VID,
|
|
MM_MISC,
|
|
MM_QCPE_VM1,
|
|
MM_VID_2, /* newly recycled */
|
|
0,
|
|
0,
|
|
MM_CLK_VM1,
|
|
MM_CLK_VM2,
|
|
MM_FDE_1,
|
|
MM_BUFFERQ_1,
|
|
};
|
|
|
|
struct ghs_vmm_plugin_info_s ghs_vmm_plugin_info = {
|
|
dt_gipc_path_name,
|
|
mmid_order,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
int get_dt_name_idx(int vmid_base, int mmid,
|
|
struct ghs_vmm_plugin_info_s *plugin_info)
|
|
{
|
|
int idx = -1;
|
|
int i;
|
|
|
|
if (vmid_base < 0 || vmid_base > plugin_info->probe_cnt /
|
|
GIPC_VM_SET_CNT) {
|
|
pr_err("vmid %d overflow expected max %d\n", vmid_base,
|
|
plugin_info->probe_cnt / GIPC_VM_SET_CNT);
|
|
return idx;
|
|
}
|
|
|
|
for (i = 0; i < GIPC_VM_SET_CNT; i++) {
|
|
if (mmid == plugin_info->mmid_dt_mapping[i]) {
|
|
idx = vmid_base * GIPC_VM_SET_CNT + i;
|
|
if (idx > plugin_info->probe_cnt) {
|
|
pr_err("dt name idx %d overflow max %d\n",
|
|
idx, plugin_info->probe_cnt);
|
|
idx = -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
/* static struct physical_channel *habhyp_commdev_alloc(int id) */
|
|
int habhyp_commdev_alloc(void **commdev, int is_be, char *name, int vmid_remote,
|
|
struct hab_device *mmid_device)
|
|
{
|
|
struct ghs_vdev *dev = NULL;
|
|
struct ghs_vdev_os *dev_os = NULL;
|
|
struct physical_channel *pchan = NULL;
|
|
struct physical_channel **ppchan = (struct physical_channel **)commdev;
|
|
int ret = 0;
|
|
|
|
if (ghs_vmm_plugin_info.curr > ghs_vmm_plugin_info.probe_cnt) {
|
|
pr_err("too many commdev alloc %d, supported is %d\n",
|
|
ghs_vmm_plugin_info.curr,
|
|
ghs_vmm_plugin_info.probe_cnt);
|
|
ret = -ENOENT;
|
|
goto err;
|
|
}
|
|
|
|
/* common part for hyp_data */
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev) {
|
|
ret = -ENOMEM;
|
|
pr_err("allocate struct ghs_vdev failed %zu bytes on pchan %s\n",
|
|
sizeof(*dev), name);
|
|
goto err;
|
|
}
|
|
|
|
memset(dev, 0, sizeof(*dev));
|
|
|
|
/* os specific part for hyp_data */
|
|
dev_os = kzalloc(sizeof(*dev_os), GFP_KERNEL);
|
|
if (!dev_os) {
|
|
ret = -ENOMEM;
|
|
pr_err("allocate ghs_vdev_os failed %zu bytes on pchan %s\n",
|
|
sizeof(*dev_os), name);
|
|
goto err;
|
|
}
|
|
|
|
dev->os_data = dev_os;
|
|
|
|
spin_lock_init(&dev->io_lock);
|
|
|
|
/*
|
|
* TODO: ExtractEndpoint is in ghs_comm.c because it blocks.
|
|
* Extrace and Request should be in roughly the same spot
|
|
*/
|
|
ret = hab_gipc_ep_attach(is_be, name, vmid_remote, mmid_device, dev);
|
|
|
|
if (ret)
|
|
goto err;
|
|
|
|
/* add pchan into the mmid_device list */
|
|
pchan = hab_pchan_alloc(mmid_device, vmid_remote);
|
|
if (!pchan) {
|
|
pr_err("hab_pchan_alloc failed for %s, cnt %d\n",
|
|
mmid_device->name, mmid_device->pchan_cnt);
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
pchan->closed = 0;
|
|
pchan->hyp_data = (void *)dev;
|
|
pchan->is_be = is_be;
|
|
strlcpy(dev->name, name, sizeof(dev->name));
|
|
strlcpy(pchan->name, name, sizeof(pchan->name));
|
|
*ppchan = pchan;
|
|
dev->read_data = kmalloc(GIPC_RECV_BUFF_SIZE_BYTES, GFP_KERNEL);
|
|
if (!dev->read_data) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
ret = habhyp_commdev_create_dispatcher(pchan);
|
|
if (ret)
|
|
goto err;
|
|
|
|
/* this value could be more than devp total */
|
|
ghs_vmm_plugin_info.curr++;
|
|
return 0;
|
|
err:
|
|
hab_pchan_put(pchan);
|
|
kfree(dev);
|
|
kfree(dev_os);
|
|
return ret;
|
|
}
|
|
|
|
int habhyp_commdev_dealloc(void *commdev)
|
|
{
|
|
struct physical_channel *pchan = (struct physical_channel *)commdev;
|
|
struct ghs_vdev *dev = pchan->hyp_data;
|
|
|
|
/* os specific deallocation for this commdev */
|
|
habhyp_commdev_dealloc_os(commdev);
|
|
|
|
kfree(dev->read_data);
|
|
kfree(dev->os_data);
|
|
kfree(dev);
|
|
|
|
if (get_refcnt(pchan->refcount) > 1) {
|
|
pr_warn("potential leak pchan %s vchans %d refcnt %d\n",
|
|
pchan->name, pchan->vcnt, get_refcnt(pchan->refcount));
|
|
}
|
|
hab_pchan_put(pchan);
|
|
return 0;
|
|
}
|
|
|
|
void hab_hypervisor_unregister(void)
|
|
{
|
|
pr_debug("total %d\n", hab_driver.ndevices);
|
|
|
|
hab_hypervisor_unregister_common();
|
|
|
|
ghs_vmm_plugin_info.curr = 0;
|
|
}
|
|
|
|
int hab_hypervisor_register(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* os-specific registration work */
|
|
ret = hab_hypervisor_register_os();
|
|
|
|
return ret;
|
|
}
|