|
@@ -102,10 +102,7 @@ static struct notifier_block hyperv_panic_block = {
|
|
.notifier_call = hyperv_panic_event,
|
|
.notifier_call = hyperv_panic_event,
|
|
};
|
|
};
|
|
|
|
|
|
-struct resource hyperv_mmio = {
|
|
|
|
- .name = "hyperv mmio",
|
|
|
|
- .flags = IORESOURCE_MEM,
|
|
|
|
-};
|
|
|
|
|
|
+struct resource *hyperv_mmio;
|
|
EXPORT_SYMBOL_GPL(hyperv_mmio);
|
|
EXPORT_SYMBOL_GPL(hyperv_mmio);
|
|
|
|
|
|
static int vmbus_exists(void)
|
|
static int vmbus_exists(void)
|
|
@@ -1013,30 +1010,105 @@ void vmbus_device_unregister(struct hv_device *device_obj)
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
- * VMBUS is an acpi enumerated device. Get the the information we
|
|
|
|
|
|
+ * VMBUS is an acpi enumerated device. Get the information we
|
|
* need from DSDT.
|
|
* need from DSDT.
|
|
*/
|
|
*/
|
|
-
|
|
|
|
|
|
+#define VTPM_BASE_ADDRESS 0xfed40000
|
|
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
|
|
static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
|
|
{
|
|
{
|
|
|
|
+ resource_size_t start = 0;
|
|
|
|
+ resource_size_t end = 0;
|
|
|
|
+ struct resource *new_res;
|
|
|
|
+ struct resource **old_res = &hyperv_mmio;
|
|
|
|
+ struct resource **prev_res = NULL;
|
|
|
|
+
|
|
switch (res->type) {
|
|
switch (res->type) {
|
|
case ACPI_RESOURCE_TYPE_IRQ:
|
|
case ACPI_RESOURCE_TYPE_IRQ:
|
|
irq = res->data.irq.interrupts[0];
|
|
irq = res->data.irq.interrupts[0];
|
|
|
|
+ return AE_OK;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * "Address" descriptors are for bus windows. Ignore
|
|
|
|
+ * "memory" descriptors, which are for registers on
|
|
|
|
+ * devices.
|
|
|
|
+ */
|
|
|
|
+ case ACPI_RESOURCE_TYPE_ADDRESS32:
|
|
|
|
+ start = res->data.address32.address.minimum;
|
|
|
|
+ end = res->data.address32.address.maximum;
|
|
break;
|
|
break;
|
|
|
|
|
|
case ACPI_RESOURCE_TYPE_ADDRESS64:
|
|
case ACPI_RESOURCE_TYPE_ADDRESS64:
|
|
- hyperv_mmio.start = res->data.address64.address.minimum;
|
|
|
|
- hyperv_mmio.end = res->data.address64.address.maximum;
|
|
|
|
|
|
+ start = res->data.address64.address.minimum;
|
|
|
|
+ end = res->data.address64.address.maximum;
|
|
break;
|
|
break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ /* Unused resource type */
|
|
|
|
+ return AE_OK;
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
+ /*
|
|
|
|
+ * Ignore ranges that are below 1MB, as they're not
|
|
|
|
+ * necessary or useful here.
|
|
|
|
+ */
|
|
|
|
+ if (end < 0x100000)
|
|
|
|
+ return AE_OK;
|
|
|
|
+
|
|
|
|
+ new_res = kzalloc(sizeof(*new_res), GFP_ATOMIC);
|
|
|
|
+ if (!new_res)
|
|
|
|
+ return AE_NO_MEMORY;
|
|
|
|
+
|
|
|
|
+ /* If this range overlaps the virtual TPM, truncate it. */
|
|
|
|
+ if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS)
|
|
|
|
+ end = VTPM_BASE_ADDRESS;
|
|
|
|
+
|
|
|
|
+ new_res->name = "hyperv mmio";
|
|
|
|
+ new_res->flags = IORESOURCE_MEM;
|
|
|
|
+ new_res->start = start;
|
|
|
|
+ new_res->end = end;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ if (!*old_res) {
|
|
|
|
+ *old_res = new_res;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((*old_res)->end < new_res->start) {
|
|
|
|
+ new_res->sibling = *old_res;
|
|
|
|
+ if (prev_res)
|
|
|
|
+ (*prev_res)->sibling = new_res;
|
|
|
|
+ *old_res = new_res;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ prev_res = old_res;
|
|
|
|
+ old_res = &(*old_res)->sibling;
|
|
|
|
+
|
|
|
|
+ } while (1);
|
|
|
|
|
|
return AE_OK;
|
|
return AE_OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int vmbus_acpi_remove(struct acpi_device *device)
|
|
|
|
+{
|
|
|
|
+ struct resource *cur_res;
|
|
|
|
+ struct resource *next_res;
|
|
|
|
+
|
|
|
|
+ if (hyperv_mmio) {
|
|
|
|
+ for (cur_res = hyperv_mmio; cur_res; cur_res = next_res) {
|
|
|
|
+ next_res = cur_res->sibling;
|
|
|
|
+ kfree(cur_res);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int vmbus_acpi_add(struct acpi_device *device)
|
|
static int vmbus_acpi_add(struct acpi_device *device)
|
|
{
|
|
{
|
|
acpi_status result;
|
|
acpi_status result;
|
|
int ret_val = -ENODEV;
|
|
int ret_val = -ENODEV;
|
|
|
|
+ struct acpi_device *ancestor;
|
|
|
|
|
|
hv_acpi_dev = device;
|
|
hv_acpi_dev = device;
|
|
|
|
|
|
@@ -1046,35 +1118,27 @@ static int vmbus_acpi_add(struct acpi_device *device)
|
|
if (ACPI_FAILURE(result))
|
|
if (ACPI_FAILURE(result))
|
|
goto acpi_walk_err;
|
|
goto acpi_walk_err;
|
|
/*
|
|
/*
|
|
- * The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that
|
|
|
|
- * has the mmio ranges. Get that.
|
|
|
|
|
|
+ * Some ancestor of the vmbus acpi device (Gen1 or 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);
|
|
|
|
|
|
+ for (ancestor = device->parent; ancestor; ancestor = ancestor->parent) {
|
|
|
|
+ result = acpi_walk_resources(ancestor->handle, METHOD_NAME__CRS,
|
|
|
|
+ vmbus_walk_resources, NULL);
|
|
|
|
|
|
if (ACPI_FAILURE(result))
|
|
if (ACPI_FAILURE(result))
|
|
- goto acpi_walk_err;
|
|
|
|
- if (hyperv_mmio.start && hyperv_mmio.end)
|
|
|
|
- request_resource(&iomem_resource, &hyperv_mmio);
|
|
|
|
|
|
+ continue;
|
|
|
|
+ if (hyperv_mmio)
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
ret_val = 0;
|
|
ret_val = 0;
|
|
|
|
|
|
acpi_walk_err:
|
|
acpi_walk_err:
|
|
complete(&probe_event);
|
|
complete(&probe_event);
|
|
|
|
+ if (ret_val)
|
|
|
|
+ vmbus_acpi_remove(device);
|
|
return ret_val;
|
|
return ret_val;
|
|
}
|
|
}
|
|
|
|
|
|
-static int vmbus_acpi_remove(struct acpi_device *device)
|
|
|
|
-{
|
|
|
|
- int ret = 0;
|
|
|
|
-
|
|
|
|
- if (hyperv_mmio.start && hyperv_mmio.end)
|
|
|
|
- ret = release_resource(&hyperv_mmio);
|
|
|
|
- return ret;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
|
|
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
|
|
{"VMBUS", 0},
|
|
{"VMBUS", 0},
|
|
{"VMBus", 0},
|
|
{"VMBus", 0},
|