in vmbus_drv.c [2159:2262]
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;
struct resource r;
switch (res->type) {
/*
* "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;
case ACPI_RESOURCE_TYPE_ADDRESS64:
start = res->data.address64.address.minimum;
end = res->data.address64.address.maximum;
break;
/*
* The IRQ information is needed only on ARM64, which Hyper-V
* sets up in the extended format. IRQ information is present
* on x86/x64 in the non-extended format but it is not used by
* Linux. So don't bother checking for the non-extended format.
*/
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
if (!acpi_dev_resource_interrupt(res, 0, &r)) {
pr_err("Unable to parse Hyper-V ACPI interrupt\n");
return AE_ERROR;
}
/* ARM64 INTID for VMbus */
vmbus_interrupt = res->data.extended_irq.interrupts[0];
/* Linux IRQ number */
vmbus_irq = r.start;
return AE_OK;
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;
/*
* If two ranges are adjacent, merge them.
*/
do {
if (!*old_res) {
*old_res = new_res;
break;
}
if (((*old_res)->end + 1) == new_res->start) {
(*old_res)->end = new_res->end;
kfree(new_res);
break;
}
if ((*old_res)->start == new_res->end + 1) {
(*old_res)->start = new_res->start;
kfree(new_res);
break;
}
if ((*old_res)->start > new_res->end) {
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;
}