251 lines
6.0 KiB
C
251 lines
6.0 KiB
C
|
|
/* Copyright (c) 2016-2020, 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_qvm.h"
|
||
|
|
|
||
|
|
/*
|
||
|
|
* this is for platform does not provide probe features. the size should match
|
||
|
|
* hab device side (all mmids)
|
||
|
|
*/
|
||
|
|
static struct shmem_irq_config pchan_factory_settings[] = {
|
||
|
|
{0x1b000000, 7},
|
||
|
|
{0x1b001000, 8},
|
||
|
|
{0x1b002000, 9},
|
||
|
|
{0x1b003000, 10},
|
||
|
|
{0x1b004000, 11},
|
||
|
|
{0x1b005000, 12},
|
||
|
|
{0x1b006000, 13},
|
||
|
|
{0x1b007000, 14},
|
||
|
|
{0x1b008000, 15},
|
||
|
|
{0x1b009000, 16},
|
||
|
|
{0x1b00a000, 17},
|
||
|
|
{0x1b00b000, 18},
|
||
|
|
{0x1b00c000, 19},
|
||
|
|
{0x1b00d000, 20},
|
||
|
|
{0x1b00e000, 21},
|
||
|
|
{0x1b00f000, 22},
|
||
|
|
{0x1b010000, 23},
|
||
|
|
{0x1b011000, 24},
|
||
|
|
{0x1b012000, 25},
|
||
|
|
{0x1b013000, 26},
|
||
|
|
{0x1b014000, 27},
|
||
|
|
{0x1b015000, 28},
|
||
|
|
{0x1b016000, 29},
|
||
|
|
{0x1b017000, 30},
|
||
|
|
};
|
||
|
|
|
||
|
|
struct qvm_plugin_info qvm_priv_info = {
|
||
|
|
pchan_factory_settings,
|
||
|
|
ARRAY_SIZE(pchan_factory_settings),
|
||
|
|
0,
|
||
|
|
ARRAY_SIZE(pchan_factory_settings)
|
||
|
|
};
|
||
|
|
|
||
|
|
/*
|
||
|
|
* this is common but only for guest
|
||
|
|
*/
|
||
|
|
uint64_t get_guest_ctrl_paddr(struct qvm_channel *dev,
|
||
|
|
unsigned long factory_addr, int irq, const char *name, uint32_t pages)
|
||
|
|
{
|
||
|
|
int i;
|
||
|
|
unsigned long factory_va;
|
||
|
|
|
||
|
|
pr_debug("name = %s, factory paddr = 0x%lx, irq %d, pages %d\n",
|
||
|
|
name, factory_addr, irq, pages);
|
||
|
|
|
||
|
|
/* get guest factory's va */
|
||
|
|
factory_va = hab_shmem_factory_va(factory_addr);
|
||
|
|
dev->guest_factory = (struct guest_shm_factory *)factory_va;
|
||
|
|
|
||
|
|
if (dev->guest_factory->signature != GUEST_SHM_SIGNATURE) {
|
||
|
|
pr_err("signature error: %ld != %llu, factory addr %lx\n",
|
||
|
|
GUEST_SHM_SIGNATURE, dev->guest_factory->signature,
|
||
|
|
factory_addr);
|
||
|
|
iounmap(dev->guest_factory);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
dev->guest_intr = dev->guest_factory->vector;
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Set the name field on the factory page to identify the shared memory
|
||
|
|
* region
|
||
|
|
*/
|
||
|
|
for (i = 0; i < strlen(name) && i < GUEST_SHM_MAX_NAME - 1; i++)
|
||
|
|
dev->guest_factory->name[i] = name[i];
|
||
|
|
dev->guest_factory->name[i] = (char) 0;
|
||
|
|
|
||
|
|
guest_shm_create(dev->guest_factory, pages);
|
||
|
|
|
||
|
|
/* See if we successfully created/attached to the region. */
|
||
|
|
if (dev->guest_factory->status != GSS_OK) {
|
||
|
|
pr_err("create failed: %d\n", dev->guest_factory->status);
|
||
|
|
iounmap(dev->guest_factory);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
pr_debug("shm creation size %x, paddr=%llx, vector %d, dev %pK\n",
|
||
|
|
dev->guest_factory->size,
|
||
|
|
dev->guest_factory->shmem,
|
||
|
|
dev->guest_intr,
|
||
|
|
dev);
|
||
|
|
|
||
|
|
dev->factory_addr = factory_addr;
|
||
|
|
dev->irq = irq;
|
||
|
|
|
||
|
|
return dev->guest_factory->shmem;
|
||
|
|
}
|
||
|
|
|
||
|
|
void hab_pipe_reset(struct physical_channel *pchan)
|
||
|
|
{
|
||
|
|
struct hab_pipe_endpoint *pipe_ep;
|
||
|
|
struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data;
|
||
|
|
|
||
|
|
pipe_ep = hab_pipe_init(dev->pipe, PIPE_SHMEM_SIZE,
|
||
|
|
pchan->is_be ? 0 : 1);
|
||
|
|
if (dev->pipe_ep != pipe_ep)
|
||
|
|
pr_warn("The pipe endpoint must not change\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* 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 = NULL;
|
||
|
|
struct qvm_channel_os *dev_os = NULL;
|
||
|
|
struct physical_channel **pchan = (struct physical_channel **)commdev;
|
||
|
|
int ret = 0;
|
||
|
|
char *shmdata;
|
||
|
|
uint32_t pipe_alloc_size =
|
||
|
|
hab_pipe_calc_required_bytes(PIPE_SHMEM_SIZE);
|
||
|
|
uint32_t pipe_alloc_pages =
|
||
|
|
(pipe_alloc_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||
|
|
|
||
|
|
pr_debug("%s: pipe_alloc_size is %d\n", __func__, pipe_alloc_size);
|
||
|
|
|
||
|
|
/* allocate common part for the commdev */
|
||
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||
|
|
if (!dev) {
|
||
|
|
ret = -ENOMEM;
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* allocate the os-specific data for the commdev */
|
||
|
|
dev_os = kzalloc(sizeof(*dev_os), GFP_KERNEL);
|
||
|
|
if (!dev_os) {
|
||
|
|
ret = -ENOMEM;
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
dev->os_data = dev_os;
|
||
|
|
|
||
|
|
spin_lock_init(&dev->io_lock);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* create/attach to the shmem region, and get back the
|
||
|
|
* shmem data vaddr
|
||
|
|
*/
|
||
|
|
shmdata = hab_shmem_attach(dev, name, pipe_alloc_pages);
|
||
|
|
|
||
|
|
if (IS_ERR(shmdata)) {
|
||
|
|
ret = PTR_ERR(shmdata);
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
dev->pipe = (struct hab_pipe *)shmdata;
|
||
|
|
pr_debug("\"%s\": pipesize %d, addr 0x%pK, be %d\n", name,
|
||
|
|
pipe_alloc_size, dev->pipe, is_be);
|
||
|
|
dev->pipe_ep = hab_pipe_init(dev->pipe, PIPE_SHMEM_SIZE,
|
||
|
|
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;
|
||
|
|
strlcpy((*pchan)->name, name, MAX_VMID_NAME_SIZE);
|
||
|
|
(*pchan)->is_be = is_be;
|
||
|
|
|
||
|
|
ret = habhyp_commdev_create_dispatcher(*pchan);
|
||
|
|
if (ret < 0)
|
||
|
|
goto err;
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
err:
|
||
|
|
pr_err("%s failed\n", __func__);
|
||
|
|
|
||
|
|
if (*commdev)
|
||
|
|
habhyp_commdev_dealloc(*commdev);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int habhyp_commdev_dealloc(void *commdev)
|
||
|
|
{
|
||
|
|
struct physical_channel *pchan = (struct physical_channel *)commdev;
|
||
|
|
struct qvm_channel *dev = pchan->hyp_data;
|
||
|
|
|
||
|
|
/* os specific deallocation for this commdev */
|
||
|
|
habhyp_commdev_dealloc_os(commdev);
|
||
|
|
|
||
|
|
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));
|
||
|
|
}
|
||
|
|
|
||
|
|
kfree(dev->os_data);
|
||
|
|
kfree(dev);
|
||
|
|
|
||
|
|
if (pchan)
|
||
|
|
hab_pchan_put(pchan);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int hab_hypervisor_register(void)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
/* os-specific registration work */
|
||
|
|
ret = hab_hypervisor_register_os();
|
||
|
|
|
||
|
|
if (ret)
|
||
|
|
goto done;
|
||
|
|
|
||
|
|
pr_info("initializing for %s VM\n", hab_driver.b_server_dom ?
|
||
|
|
"host" : "guest");
|
||
|
|
|
||
|
|
hab_driver.hyp_priv = &qvm_priv_info;
|
||
|
|
|
||
|
|
done:
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
void hab_hypervisor_unregister(void)
|
||
|
|
{
|
||
|
|
pr_info("unregistration is called, but do nothing\n");
|
||
|
|
}
|