virtio-mmio: discover devices from ACPI

https://www.kernel.org/doc/Documentation/arm64/arm-acpi.txt
https://wiki.linaro.org/LEG/ServerArchitecture/ACPI/LinaroRegistry

Change-Id: I330a497173c0a82b789a43720e3d86e1513a661b
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5506
Reviewed-by: Fredrik Holmqvist <fredrik.holmqvist@gmail.com>
This commit is contained in:
David Karoly 2022-07-29 15:54:19 +02:00 committed by Fredrik Holmqvist
parent 0f1217b8fb
commit 079db739a6
2 changed files with 214 additions and 54 deletions

View File

@ -277,4 +277,48 @@ enum {
};
/* The following definitions are adapted from acpica/include/acrestyp.h */
typedef struct acpi_resource_source
{
uint8 index;
uint16 string_length;
char *string_ptr;
} _PACKED acpi_resource_source;
typedef struct acpi_resource_fixed_memory32 {
uint8 write_protect;
uint32 address;
uint32 address_length;
} _PACKED acpi_resource_fixed_memory32;
typedef struct acpi_resource_extended_irq {
uint8 producer_consumer;
uint8 triggering;
uint8 polarity;
uint8 shareable;
uint8 wake_capable;
uint8 interrupt_count;
acpi_resource_source resource_source;
uint32 interrupts[1];
} _PACKED acpi_resource_extended_irq;
typedef union acpi_resource_data {
acpi_resource_fixed_memory32 fixed_memory32;
acpi_resource_extended_irq extended_irq;
} acpi_resource_data;
typedef struct acpi_resource {
uint32 type;
uint32 length;
acpi_resource_data data;
} _PACKED acpi_resource;
enum {
ACPI_RESOURCE_TYPE_FIXED_MEMORY32 = 10,
ACPI_RESOURCE_TYPE_EXTENDED_IRQ = 15,
};
#endif /* _KERNEL_ACPI_H */

View File

@ -12,11 +12,14 @@
#include <KernelExport.h>
#include <device_manager.h>
#include <drivers/ACPI.h>
#include <drivers/bus/FDT.h>
#include <debug.h>
#include <AutoDeleterDrivers.h>
#include <acpi.h>
#include <virtio.h>
#include <virtio_defs.h>
#include "VirtioDevice.h"
@ -80,7 +83,6 @@ virtio_device_supports_device(device_node* parent)
const char* name;
const char* bus;
const char* compatible;
status_t status = gDeviceManager->get_attr_string(parent,
B_DEVICE_PRETTY_NAME, &name, false);
@ -93,19 +95,68 @@ virtio_device_supports_device(device_node* parent)
if (status < B_OK)
return -1.0f;
status = gDeviceManager->get_attr_string(parent, "fdt/compatible",
&compatible, false);
// detect virtio device from FDT
if (strcmp(bus, "fdt") == 0) {
const char* compatible;
status = gDeviceManager->get_attr_string(parent, "fdt/compatible",
&compatible, false);
if (status < B_OK)
return -1.0f;
if (status < B_OK)
return -1.0f;
if (strcmp(bus, "fdt") != 0)
return 0.0f;
if (strcmp(compatible, "virtio,mmio") != 0)
return 0.0f;
if (strcmp(compatible, "virtio,mmio") != 0)
return 0.0f;
return 1.0f;
}
return 1.0f;
// detect virtio device from ACPI
if (strcmp(bus, "acpi") == 0) {
const char* hid;
status = gDeviceManager->get_attr_string(parent, "acpi/hid",
&hid, false);
if (status < B_OK)
return -1.0f;
if (strcmp(hid, "LNRO0005") != 0)
return 0.0f;
return 1.0f;
}
return 0.0f;
}
struct virtio_memory_range {
uint64 base;
uint64 length;
};
static acpi_status
virtio_crs_find_address(acpi_resource *res, void *context)
{
virtio_memory_range &range = *((virtio_memory_range *)context);
if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
range.base = res->data.fixed_memory32.address;
range.length = res->data.fixed_memory32.address_length;
}
return B_OK;
}
static acpi_status
virtio_crs_find_interrupt(acpi_resource *res, void *context)
{
uint64 &interrupt = *((uint64 *)context);
if (res->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ)
interrupt = res->data.extended_irq.interrupts[0];
return B_OK;
}
@ -114,18 +165,47 @@ virtio_device_register_device(device_node* parent)
{
TRACE("register_device(%p)\n", parent);
fdt_device_module_info *parentModule;
fdt_device* parentDev;
if (gDeviceManager->get_driver(parent, (driver_module_info**)&parentModule,
(void**)&parentDev)) {
ERROR("can't get parent node driver");
return B_ERROR;
const char* bus;
status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
if (status < B_OK)
return -1.0f;
uint64 regs = 0;
uint64 regsLen = 0;
// initialize virtio device from FDT
if (strcmp(bus, "fdt") == 0) {
fdt_device_module_info *parentModule;
fdt_device* parentDev;
if (gDeviceManager->get_driver(parent, (driver_module_info**)&parentModule,
(void**)&parentDev)) {
ERROR("can't get parent node driver");
return B_ERROR;
}
if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
ERROR("no regs");
return B_ERROR;
}
}
uint64 regs, regsLen;
if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
ERROR("no regs");
return B_ERROR;
// initialize virtio device from ACPI
if (strcmp(bus, "acpi") == 0) {
acpi_device_module_info *parentModule;
acpi_device parentDev;
if (gDeviceManager->get_driver(parent, (driver_module_info**)&parentModule,
(void**)&parentDev)) {
ERROR("can't get parent node driver");
return B_ERROR;
}
virtio_memory_range range = { 0, 0 };
parentModule->walk_resources(parentDev, (char *)"_CRS",
virtio_crs_find_address, &range);
regs = range.base;
regsLen = range.length;
}
VirtioRegs *volatile mappedRegs;
@ -172,49 +252,85 @@ virtio_device_init_device(device_node* node, void** cookie)
DeviceNodePutter<&gDeviceManager>
parent(gDeviceManager->get_parent_node(node));
fdt_device_module_info *parentModule;
fdt_device* parentDev;
if (gDeviceManager->get_driver(parent.Get(),
(driver_module_info**)&parentModule, (void**)&parentDev))
panic("can't get parent node driver");
const char* bus;
dprintf(" bus: %p\n", parentModule->get_bus(parentDev));
dprintf(" compatible: %s\n", (const char*)parentModule->get_prop(parentDev,
"compatible", NULL));
status_t status = gDeviceManager->get_attr_string(parent.Get(), B_DEVICE_BUS, &bus, false);
uint64 regs;
uint64 regsLen;
for (uint32 i = 0; parentModule->get_reg(parentDev, i, &regs, &regsLen);
i++) {
dprintf(" reg[%" B_PRIu32 "]: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
i, regs, regsLen);
}
if (status < B_OK)
return -1.0f;
device_node* interruptController;
uint64 interrupt;
for (uint32 i = 0; parentModule->get_interrupt(parentDev,
i, &interruptController, &interrupt); i++) {
uint64 regs = 0;
uint64 regsLen = 0;
uint64 interrupt = 0;
const char* name;
if (interruptController == NULL
|| gDeviceManager->get_attr_string(interruptController, "fdt/name",
&name, false) < B_OK) {
name = NULL;
// initialize virtio device from FDT
if (strcmp(bus, "fdt") == 0) {
fdt_device_module_info *parentModule;
fdt_device* parentDev;
if (gDeviceManager->get_driver(parent.Get(),
(driver_module_info**)&parentModule, (void**)&parentDev))
panic("can't get parent node driver");
dprintf(" bus: %p\n", parentModule->get_bus(parentDev));
dprintf(" compatible: %s\n", (const char*)parentModule->get_prop(parentDev,
"compatible", NULL));
for (uint32 i = 0; parentModule->get_reg(parentDev, i, &regs, &regsLen);
i++) {
dprintf(" reg[%" B_PRIu32 "]: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
i, regs, regsLen);
}
dprintf(" interrupt[%" B_PRIu32 "]: ('%s', 0x%" B_PRIx64 ")\n", i,
name, interrupt);
device_node* interruptController;
for (uint32 i = 0; parentModule->get_interrupt(parentDev,
i, &interruptController, &interrupt); i++) {
const char* name;
if (interruptController == NULL
|| gDeviceManager->get_attr_string(interruptController, "fdt/name",
&name, false) < B_OK) {
name = NULL;
}
dprintf(" interrupt[%" B_PRIu32 "]: ('%s', 0x%" B_PRIx64 ")\n", i,
name, interrupt);
}
if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
dprintf(" no regs\n");
return B_ERROR;
}
if (!parentModule->get_interrupt(parentDev, 0, &interruptController,
&interrupt)) {
dprintf(" no interrupts\n");
return B_ERROR;
}
}
if (!parentModule->get_reg(parentDev, 0, &regs, &regsLen)) {
dprintf(" no regs\n");
return B_ERROR;
}
// initialize virtio device from ACPI
if (strcmp(bus, "acpi") == 0) {
acpi_device_module_info *parentModule;
acpi_device parentDev;
if (gDeviceManager->get_driver(parent.Get(), (driver_module_info**)&parentModule,
(void**)&parentDev)) {
ERROR("can't get parent node driver");
return B_ERROR;
}
if (!parentModule->get_interrupt(parentDev, 0, &interruptController,
&interrupt)) {
dprintf(" no interrupts\n");
return B_ERROR;
virtio_memory_range range = { 0, 0 };
parentModule->walk_resources(parentDev, (char *)"_CRS",
virtio_crs_find_address, &range);
regs = range.base;
regsLen = range.length;
parentModule->walk_resources(parentDev, (char *)"_CRS",
virtio_crs_find_interrupt, &interrupt);
dprintf(" regs: (0x%" B_PRIx64 ", 0x%" B_PRIx64 ")\n",
regs, regsLen);
dprintf(" interrupt: 0x%" B_PRIx64 "\n",
interrupt);
}
ObjectDeleter<VirtioDevice> dev(new(std::nothrow) VirtioDevice());