2014-02-21 08:57:09

by Gerd Hoffmann

[permalink] [raw]
Subject: [PATCH 0/4] hyperv-fb: add support for generation 2 virtual machines

Hi,

This patch series adds support for uefi-based gen2 virtual machines to
the hyperv-fb driver.

First patch is included just for completeness. Greg picked it up
already and it is in -next, but it isn't in master yet.

Second patch is a bugfix for the first patch.

Third patch changes vmbus to properly hook the hyperv mmio area into
the linux resource management.

Final patch actually implements gen2 vm support for hyperv-fb, building
on the hyperv mmio support added by the other patches.

Known issue: There is a long delay when loading the hyperv-fb driver on
gen2 virtual machines which isn't present on gen1 virtual machines.

Gerd Hoffmann (3):
vmbus: add missing breaks
vmbus: use resource for hyperv mmio region
hyperv-fb: add support for generation 2 virtual machines.

K. Y. Srinivasan (1):
Drivers: hv: vmbus: Extract the mmio information from DSDT

drivers/hv/vmbus_drv.c | 51 +++++++++++++++++++++-------
drivers/video/hyperv_fb.c | 86 +++++++++++++++++++++++++++++++++--------------
include/linux/hyperv.h | 2 ++
3 files changed, 100 insertions(+), 39 deletions(-)

--
1.8.3.1


2014-02-21 08:57:14

by Gerd Hoffmann

[permalink] [raw]
Subject: [PATCH 2/4] vmbus: add missing breaks

Signed-off-by: Gerd Hoffmann <[email protected]>
---
drivers/hv/vmbus_drv.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index b37c91b..2352ae48 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -899,10 +899,12 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
switch (res->type) {
case ACPI_RESOURCE_TYPE_IRQ:
irq = res->data.irq.interrupts[0];
+ break;

case ACPI_RESOURCE_TYPE_ADDRESS64:
hyperv_mmio_start = res->data.address64.minimum;
hyperv_mmio_size = res->data.address64.address_length;
+ break;
}

return AE_OK;
--
1.8.3.1

2014-02-21 08:57:13

by Gerd Hoffmann

[permalink] [raw]
Subject: [PATCH 1/4] Drivers: hv: vmbus: Extract the mmio information from DSDT

From: "K. Y. Srinivasan" <[email protected]>

On Gen2 firmware, Hyper-V does not emulate the PCI bus. However, the MMIO
information is packaged up in DSDT. Extract this information and export it
for use by the synthetic framebuffer driver. This is the only driver that
needs this currently.

In this version of the patch mmio, I have updated the hyperv header file
(linux/hyperv.h) with mmio definitions.

Signed-off-by: K. Y. Srinivasan <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
drivers/hv/vmbus_drv.c | 45 ++++++++++++++++++++++++++++++++-------------
include/linux/hyperv.h | 3 +++
2 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 077bb1b..b37c91b 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -43,6 +43,10 @@ static struct acpi_device *hv_acpi_dev;
static struct tasklet_struct msg_dpc;
static struct completion probe_event;
static int irq;
+u64 hyperv_mmio_start;
+EXPORT_SYMBOL_GPL(hyperv_mmio_start);
+u64 hyperv_mmio_size;
+EXPORT_SYMBOL_GPL(hyperv_mmio_size);

static int vmbus_exists(void)
{
@@ -886,18 +890,19 @@ void vmbus_device_unregister(struct hv_device *device_obj)


/*
- * VMBUS is an acpi enumerated device. Get the the IRQ information
- * from DSDT.
+ * VMBUS is an acpi enumerated device. Get the the information we
+ * need from DSDT.
*/

-static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
+static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
{
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ irq = res->data.irq.interrupts[0];

- if (res->type == ACPI_RESOURCE_TYPE_IRQ) {
- struct acpi_resource_irq *irqp;
- irqp = &res->data.irq;
-
- *((unsigned int *)irq) = irqp->interrupts[0];
+ case ACPI_RESOURCE_TYPE_ADDRESS64:
+ hyperv_mmio_start = res->data.address64.minimum;
+ hyperv_mmio_size = res->data.address64.address_length;
}

return AE_OK;
@@ -906,18 +911,32 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq)
static int vmbus_acpi_add(struct acpi_device *device)
{
acpi_status result;
+ int ret_val = -ENODEV;

hv_acpi_dev = device;

result = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
- vmbus_walk_resources, &irq);
+ vmbus_walk_resources, NULL);

- if (ACPI_FAILURE(result)) {
- complete(&probe_event);
- return -ENODEV;
+ if (ACPI_FAILURE(result))
+ goto acpi_walk_err;
+ /*
+ * The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that
+ * has the mmio ranges. Get that.
+ */
+ if (device->parent) {
+ result = acpi_walk_resources(device->parent->handle,
+ METHOD_NAME__CRS,
+ vmbus_walk_resources, NULL);
+
+ if (ACPI_FAILURE(result))
+ goto acpi_walk_err;
}
+ ret_val = 0;
+
+acpi_walk_err:
complete(&probe_event);
- return 0;
+ return ret_val;
}

static const struct acpi_device_id vmbus_acpi_device_ids[] = {
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 344883d..be3028f 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -1459,6 +1459,9 @@ int hv_vss_init(struct hv_util_service *);
void hv_vss_deinit(void);
void hv_vss_onchannelcallback(void *);

+extern u64 hyperv_mmio_start;
+extern u64 hyperv_mmio_size;
+
/*
* Negotiated version with the Host.
*/
--
1.8.3.1

2014-02-21 08:57:11

by Gerd Hoffmann

[permalink] [raw]
Subject: [PATCH 3/4] vmbus: use resource for hyperv mmio region

Use a resource for the hyperv mmio region instead of start/size
variables. Register the region properly so it shows up in
/proc/iomem.

Signed-off-by: Gerd Hoffmann <[email protected]>
---
drivers/hv/vmbus_drv.c | 16 ++++++++++------
include/linux/hyperv.h | 3 +--
2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 2352ae48..a14f603 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -43,10 +43,12 @@ static struct acpi_device *hv_acpi_dev;
static struct tasklet_struct msg_dpc;
static struct completion probe_event;
static int irq;
-u64 hyperv_mmio_start;
-EXPORT_SYMBOL_GPL(hyperv_mmio_start);
-u64 hyperv_mmio_size;
-EXPORT_SYMBOL_GPL(hyperv_mmio_size);
+
+struct resource hyperv_mmio = {
+ .name = "hyperv mmio",
+ .flags = IORESOURCE_MEM,
+};
+EXPORT_SYMBOL_GPL(hyperv_mmio);

static int vmbus_exists(void)
{
@@ -902,8 +904,8 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
break;

case ACPI_RESOURCE_TYPE_ADDRESS64:
- hyperv_mmio_start = res->data.address64.minimum;
- hyperv_mmio_size = res->data.address64.address_length;
+ hyperv_mmio.start = res->data.address64.minimum;
+ hyperv_mmio.end = res->data.address64.maximum;
break;
}

@@ -933,6 +935,8 @@ static int vmbus_acpi_add(struct acpi_device *device)

if (ACPI_FAILURE(result))
goto acpi_walk_err;
+ if (hyperv_mmio.start && hyperv_mmio.end)
+ request_resource(&iomem_resource, &hyperv_mmio);
}
ret_val = 0;

diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index be3028f..c93e342 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -1459,8 +1459,7 @@ int hv_vss_init(struct hv_util_service *);
void hv_vss_deinit(void);
void hv_vss_onchannelcallback(void *);

-extern u64 hyperv_mmio_start;
-extern u64 hyperv_mmio_size;
+extern struct resource hyperv_mmio;

/*
* Negotiated version with the Host.
--
1.8.3.1

2014-02-21 08:58:13

by Gerd Hoffmann

[permalink] [raw]
Subject: [PATCH 4/4] hyperv-fb: add support for generation 2 virtual machines.

UEFI-based generation 2 virtual machines support vmbus devices only.
There is no pci bus. Thus they use a different mechanism for the
graphics framebuffer: Instead of using the vga pci bar a chunk of
memory muct be allocated from the hyperv mmio region declared using
APCI. This patch implements support for it.

Based on a patch by Haiyang Zhang <[email protected]>

Signed-off-by: Gerd Hoffmann <[email protected]>
---
drivers/video/hyperv_fb.c | 86 +++++++++++++++++++++++++++++++++--------------
1 file changed, 60 insertions(+), 26 deletions(-)

diff --git a/drivers/video/hyperv_fb.c b/drivers/video/hyperv_fb.c
index 130708f..f956cd9 100644
--- a/drivers/video/hyperv_fb.c
+++ b/drivers/video/hyperv_fb.c
@@ -42,6 +42,7 @@
#include <linux/completion.h>
#include <linux/fb.h>
#include <linux/pci.h>
+#include <linux/efi.h>

#include <linux/hyperv.h>

@@ -212,6 +213,7 @@ struct synthvid_msg {

struct hvfb_par {
struct fb_info *info;
+ struct resource mem;
bool fb_ready; /* fb device is ready */
struct completion wait;
u32 synthvid_version;
@@ -460,13 +462,13 @@ static int synthvid_connect_vsp(struct hv_device *hdev)
goto error;
}

- if (par->synthvid_version == SYNTHVID_VERSION_WIN7) {
+ if (par->synthvid_version == SYNTHVID_VERSION_WIN7)
screen_depth = SYNTHVID_DEPTH_WIN7;
- screen_fb_size = SYNTHVID_FB_SIZE_WIN7;
- } else {
+ else
screen_depth = SYNTHVID_DEPTH_WIN8;
- screen_fb_size = SYNTHVID_FB_SIZE_WIN8;
- }
+
+ screen_fb_size = hdev->channel->offermsg.offer.
+ mmio_megabytes * 1024 * 1024;

return 0;

@@ -627,26 +629,46 @@ static void hvfb_get_option(struct fb_info *info)
/* Get framebuffer memory from Hyper-V video pci space */
static int hvfb_getmem(struct fb_info *info)
{
- struct pci_dev *pdev;
- ulong fb_phys;
+ struct hvfb_par *par = info->par;
+ struct pci_dev *pdev = NULL;
void __iomem *fb_virt;
+ bool gen2vm = efi_enabled(EFI_BOOT);
+ int ret;

- pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
+ par->mem.name = "hyperv_fb";
+ par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ if (gen2vm) {
+ ret = allocate_resource(&hyperv_mmio, &par->mem,
+ screen_fb_size,
+ 0, -1,
+ screen_fb_size,
+ NULL, NULL);
+ if (ret != 0) {
+ pr_err("Unable to allocate framebuffer memory\n");
+ return -ENODEV;
+ }
+ } else {
+ pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
- if (!pdev) {
- pr_err("Unable to find PCI Hyper-V video\n");
- return -ENODEV;
- }
+ if (!pdev) {
+ pr_err("Unable to find PCI Hyper-V video\n");
+ return -ENODEV;
+ }

- if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
- pci_resource_len(pdev, 0) < screen_fb_size)
- goto err1;
+ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
+ pci_resource_len(pdev, 0) < screen_fb_size)
+ goto err1;

- fb_phys = pci_resource_end(pdev, 0) - screen_fb_size + 1;
- if (!request_mem_region(fb_phys, screen_fb_size, KBUILD_MODNAME))
- goto err1;
+ par->mem.end = pci_resource_end(pdev, 0);
+ par->mem.start = par->mem.end - screen_fb_size + 1;
+ ret = request_resource(&pdev->resource[0], &par->mem);
+ if (ret != 0) {
+ pr_err("Unable to request framebuffer memory\n");
+ return -ENODEV;
+ }
+ }

- fb_virt = ioremap(fb_phys, screen_fb_size);
+ fb_virt = ioremap(par->mem.start, screen_fb_size);
if (!fb_virt)
goto err2;

@@ -654,30 +676,42 @@ static int hvfb_getmem(struct fb_info *info)
if (!info->apertures)
goto err3;

- info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
- info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
- info->fix.smem_start = fb_phys;
+ if (gen2vm) {
+ info->apertures->ranges[0].base = screen_info.lfb_base;
+ info->apertures->ranges[0].size = screen_info.lfb_size;
+ } else {
+ info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
+ info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
+ }
+
+ info->fix.smem_start = par->mem.start;
info->fix.smem_len = screen_fb_size;
info->screen_base = fb_virt;
info->screen_size = screen_fb_size;

- pci_dev_put(pdev);
+ if (!gen2vm)
+ pci_dev_put(pdev);
+
return 0;

err3:
iounmap(fb_virt);
err2:
- release_mem_region(fb_phys, screen_fb_size);
+ release_resource(&par->mem);
err1:
- pci_dev_put(pdev);
+ if (!gen2vm)
+ pci_dev_put(pdev);
+
return -ENOMEM;
}

/* Release the framebuffer */
static void hvfb_putmem(struct fb_info *info)
{
+ struct hvfb_par *par = info->par;
+
iounmap(info->screen_base);
- release_mem_region(info->fix.smem_start, screen_fb_size);
+ release_resource(&par->mem);
}


--
1.8.3.1

2014-02-21 14:49:31

by KY Srinivasan

[permalink] [raw]
Subject: RE: [PATCH 4/4] hyperv-fb: add support for generation 2 virtual machines.



> -----Original Message-----
> From: Gerd Hoffmann [mailto:[email protected]]
> Sent: Friday, February 21, 2014 12:57 AM
> To: [email protected]
> Cc: [email protected]; [email protected]; KY Srinivasan;
> Haiyang Zhang; [email protected]; Gerd Hoffmann; Jean-Christophe
> Plagniol-Villard; Tomi Valkeinen; open list:Hyper-V CORE AND...
> Subject: [PATCH 4/4] hyperv-fb: add support for generation 2 virtual machines.
>
> UEFI-based generation 2 virtual machines support vmbus devices only.
> There is no pci bus. Thus they use a different mechanism for the
> graphics framebuffer: Instead of using the vga pci bar a chunk of
> memory muct be allocated from the hyperv mmio region declared using
> APCI. This patch implements support for it.
>
> Based on a patch by Haiyang Zhang <[email protected]>
>
> Signed-off-by: Gerd Hoffmann <[email protected]>
> ---
> drivers/video/hyperv_fb.c | 86 +++++++++++++++++++++++++++++++++-------
> -------
> 1 file changed, 60 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/video/hyperv_fb.c b/drivers/video/hyperv_fb.c
> index 130708f..f956cd9 100644
> --- a/drivers/video/hyperv_fb.c
> +++ b/drivers/video/hyperv_fb.c
> @@ -42,6 +42,7 @@
> #include <linux/completion.h>
> #include <linux/fb.h>
> #include <linux/pci.h>
> +#include <linux/efi.h>
>
> #include <linux/hyperv.h>
>
> @@ -212,6 +213,7 @@ struct synthvid_msg {
>
> struct hvfb_par {
> struct fb_info *info;
> + struct resource mem;
> bool fb_ready; /* fb device is ready */
> struct completion wait;
> u32 synthvid_version;
> @@ -460,13 +462,13 @@ static int synthvid_connect_vsp(struct hv_device
> *hdev)
> goto error;
> }
>
> - if (par->synthvid_version == SYNTHVID_VERSION_WIN7) {
> + if (par->synthvid_version == SYNTHVID_VERSION_WIN7)
> screen_depth = SYNTHVID_DEPTH_WIN7;
> - screen_fb_size = SYNTHVID_FB_SIZE_WIN7;
> - } else {
> + else
> screen_depth = SYNTHVID_DEPTH_WIN8;
> - screen_fb_size = SYNTHVID_FB_SIZE_WIN8;
> - }
> +
> + screen_fb_size = hdev->channel->offermsg.offer.
> + mmio_megabytes * 1024 * 1024;
>
> return 0;
>
> @@ -627,26 +629,46 @@ static void hvfb_get_option(struct fb_info *info)
> /* Get framebuffer memory from Hyper-V video pci space */
> static int hvfb_getmem(struct fb_info *info)
> {
> - struct pci_dev *pdev;
> - ulong fb_phys;
> + struct hvfb_par *par = info->par;
> + struct pci_dev *pdev = NULL;
> void __iomem *fb_virt;
> + bool gen2vm = efi_enabled(EFI_BOOT);

Gerd,
efi_enabled() returns an int and not a bool. That is the fix that Haiyang made last night.

K. Y
> + int ret;
>
> - pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
> + par->mem.name = "hyperv_fb";
> + par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
> + if (gen2vm) {
> + ret = allocate_resource(&hyperv_mmio, &par->mem,
> + screen_fb_size,
> + 0, -1,
> + screen_fb_size,
> + NULL, NULL);
> + if (ret != 0) {
> + pr_err("Unable to allocate framebuffer memory\n");
> + return -ENODEV;
> + }
> + } else {
> + pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
> PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
> - if (!pdev) {
> - pr_err("Unable to find PCI Hyper-V video\n");
> - return -ENODEV;
> - }
> + if (!pdev) {
> + pr_err("Unable to find PCI Hyper-V video\n");
> + return -ENODEV;
> + }
>
> - if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
> - pci_resource_len(pdev, 0) < screen_fb_size)
> - goto err1;
> + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
> + pci_resource_len(pdev, 0) < screen_fb_size)
> + goto err1;
>
> - fb_phys = pci_resource_end(pdev, 0) - screen_fb_size + 1;
> - if (!request_mem_region(fb_phys, screen_fb_size,
> KBUILD_MODNAME))
> - goto err1;
> + par->mem.end = pci_resource_end(pdev, 0);
> + par->mem.start = par->mem.end - screen_fb_size + 1;
> + ret = request_resource(&pdev->resource[0], &par->mem);
> + if (ret != 0) {
> + pr_err("Unable to request framebuffer memory\n");
> + return -ENODEV;
> + }
> + }
>
> - fb_virt = ioremap(fb_phys, screen_fb_size);
> + fb_virt = ioremap(par->mem.start, screen_fb_size);
> if (!fb_virt)
> goto err2;
>
> @@ -654,30 +676,42 @@ static int hvfb_getmem(struct fb_info *info)
> if (!info->apertures)
> goto err3;
>
> - info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
> - info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
> - info->fix.smem_start = fb_phys;
> + if (gen2vm) {
> + info->apertures->ranges[0].base = screen_info.lfb_base;
> + info->apertures->ranges[0].size = screen_info.lfb_size;
> + } else {
> + info->apertures->ranges[0].base = pci_resource_start(pdev, 0);
> + info->apertures->ranges[0].size = pci_resource_len(pdev, 0);
> + }
> +
> + info->fix.smem_start = par->mem.start;
> info->fix.smem_len = screen_fb_size;
> info->screen_base = fb_virt;
> info->screen_size = screen_fb_size;
>
> - pci_dev_put(pdev);
> + if (!gen2vm)
> + pci_dev_put(pdev);
> +
> return 0;
>
> err3:
> iounmap(fb_virt);
> err2:
> - release_mem_region(fb_phys, screen_fb_size);
> + release_resource(&par->mem);
> err1:
> - pci_dev_put(pdev);
> + if (!gen2vm)
> + pci_dev_put(pdev);
> +
> return -ENOMEM;
> }
>
> /* Release the framebuffer */
> static void hvfb_putmem(struct fb_info *info)
> {
> + struct hvfb_par *par = info->par;
> +
> iounmap(info->screen_base);
> - release_mem_region(info->fix.smem_start, screen_fb_size);
> + release_resource(&par->mem);
> }
>
>
> --
> 1.8.3.1