Re: [PATCH v2 kvmtool 25/30] pci: Implement callbacks for toggling BAR emulation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 2/7/20 10:12 AM, Alexandru Elisei wrote:
> Hi,
>
> On 2/6/20 6:21 PM, Andre Przywara wrote:
>> On Thu, 23 Jan 2020 13:48:00 +0000
>> Alexandru Elisei <alexandru.elisei@xxxxxxx> wrote:
>>
>> Hi,
>>
>>> Implement callbacks for activating and deactivating emulation for a BAR
>>> region. This is in preparation for allowing a guest operating system to
>>> enable and disable access to I/O or memory space, or to reassign the
>>> BARs.
>>>
>>> The emulated vesa device has been refactored in the process and the static
>>> variables were removed in order to make using the callbacks less painful.
>>> The framebuffer isn't designed to allow stopping and restarting at
>>> arbitrary points in the guest execution. Furthermore, on x86, the kernel
>>> will not change the BAR addresses, which on bare metal are programmed by
>>> the firmware, so take the easy way out and refuse to deactivate emulation
>>> for the BAR regions.
>>>
>>> Signed-off-by: Alexandru Elisei <alexandru.elisei@xxxxxxx>
>>> ---
>>>  hw/vesa.c         | 120 ++++++++++++++++++++++++++++++++--------------
>>>  include/kvm/pci.h |  19 +++++++-
>>>  pci.c             |  44 +++++++++++++++++
>>>  vfio/pci.c        | 100 +++++++++++++++++++++++++++++++-------
>>>  virtio/pci.c      |  90 ++++++++++++++++++++++++----------
>>>  5 files changed, 294 insertions(+), 79 deletions(-)
>>>
>>> diff --git a/hw/vesa.c b/hw/vesa.c
>>> index e988c0425946..74ebebbefa6b 100644
>>> --- a/hw/vesa.c
>>> +++ b/hw/vesa.c
>>> @@ -18,6 +18,12 @@
>>>  #include <inttypes.h>
>>>  #include <unistd.h>
>>>  
>>> +struct vesa_dev {
>>> +	struct pci_device_header	pci_hdr;
>>> +	struct device_header		dev_hdr;
>>> +	struct framebuffer		fb;
>>> +};
>>> +
>>>  static bool vesa_pci_io_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
>>>  {
>>>  	return true;
>>> @@ -33,29 +39,52 @@ static struct ioport_operations vesa_io_ops = {
>>>  	.io_out			= vesa_pci_io_out,
>>>  };
>>>  
>>> -static struct pci_device_header vesa_pci_device = {
>>> -	.vendor_id		= cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
>>> -	.device_id		= cpu_to_le16(PCI_DEVICE_ID_VESA),
>>> -	.header_type		= PCI_HEADER_TYPE_NORMAL,
>>> -	.revision_id		= 0,
>>> -	.class[2]		= 0x03,
>>> -	.subsys_vendor_id	= cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
>>> -	.subsys_id		= cpu_to_le16(PCI_SUBSYSTEM_ID_VESA),
>>> -	.bar[1]			= cpu_to_le32(VESA_MEM_ADDR | PCI_BASE_ADDRESS_SPACE_MEMORY),
>>> -	.bar_size[1]		= VESA_MEM_SIZE,
>>> -};
>>> +static int vesa__bar_activate(struct kvm *kvm,
>>> +			      struct pci_device_header *pci_hdr,
>>> +			      int bar_num, void *data)
>>> +{
>>> +	struct vesa_dev *vdev = data;
>>> +	u32 bar_addr, bar_size;
>>> +	char *mem;
>>> +	int r;
>>>  
>>> -static struct device_header vesa_device = {
>>> -	.bus_type	= DEVICE_BUS_PCI,
>>> -	.data		= &vesa_pci_device,
>>> -};
>>> +	bar_addr = pci__bar_address(pci_hdr, bar_num);
>>> +	bar_size = pci_hdr->bar_size[bar_num];
>>>  
>>> -static struct framebuffer vesafb;
>>> +	switch (bar_num) {
>>> +	case 0:
>>> +		r = ioport__register(kvm, bar_addr, &vesa_io_ops, bar_size,
>>> +				     NULL);
>>> +		break;
>>> +	case 1:
>>> +		mem = mmap(NULL, bar_size, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
>>> +		if (mem == MAP_FAILED) {
>>> +			r = -errno;
>>> +			break;
>>> +		}
>>> +		r = kvm__register_dev_mem(kvm, bar_addr, bar_size, mem);
>>> +		if (r < 0)
>>> +			break;
>>> +		vdev->fb.mem = mem;
>>> +		break;
>>> +	default:
>>> +		r = -EINVAL;
>>> +	}
>>> +
>>> +	return r;
>>> +}
>>> +
>>> +static int vesa__bar_deactivate(struct kvm *kvm,
>>> +				struct pci_device_header *pci_hdr,
>>> +				int bar_num, void *data)
>>> +{
>>> +	return -EINVAL;
>>> +}
>>>  
>>>  struct framebuffer *vesa__init(struct kvm *kvm)
>>>  {
>>> -	u16 vesa_base_addr;
>>> -	char *mem;
>>> +	struct vesa_dev *vdev;
>>> +	u16 port_addr;
>>>  	int r;
>>>  
>>>  	BUILD_BUG_ON(!is_power_of_two(VESA_MEM_SIZE));
>>> @@ -63,34 +92,51 @@ struct framebuffer *vesa__init(struct kvm *kvm)
>>>  
>>>  	if (!kvm->cfg.vnc && !kvm->cfg.sdl && !kvm->cfg.gtk)
>>>  		return NULL;
>>> -	r = pci_get_io_port_block(PCI_IO_SIZE);
>>> -	r = ioport__register(kvm, r, &vesa_io_ops, PCI_IO_SIZE, NULL);
>>> -	if (r < 0)
>>> -		return ERR_PTR(r);
>>>  
>>> -	vesa_base_addr			= (u16)r;
>>> -	vesa_pci_device.bar[0]		= cpu_to_le32(vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO);
>>> -	vesa_pci_device.bar_size[0]	= PCI_IO_SIZE;
>>> -	r = device__register(&vesa_device);
>>> -	if (r < 0)
>>> -		return ERR_PTR(r);
>>> +	vdev = calloc(1, sizeof(*vdev));
>>> +	if (vdev == NULL)
>>> +		return ERR_PTR(-ENOMEM);
>> Is it really necessary to allocate this here? You never free this, and I don't see how you could actually do this. AFAICS conceptually there can be only one VESA device? So maybe have a static variable above and use that instead of passing the pointer around? Or use &vdev if you need a pointer argument for the callbacks.
> As far as I can tell, there can be only one VESA device, yes. I was following the
> same pattern from virtio/{net,blk,rng,scsi,9p}.c, which I prefer because it's
> explicit what function can access the device. What's wrong with passing the
> pointer around? The entire PCI emulation code works like that.
>
I took a closer look at this patch and it turns out that vesa__bar_activate is
called exactly once, at initialization, because vesa__bar_deactivate doesn't
actually deactivate emulation (and returns an error to let the PCI emulation code
know that). I will drop the changes to hw/vesa.c and keep the vesa device static
and framebuffer creation in vesa__init.

Thanks,
Alex



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux