blob: a6d9661464935444f9f6b28986aad855e7c524c2 [file]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_BOARD_LIB_ACPI_DEVICE_H_
#define SRC_DEVICES_BOARD_LIB_ACPI_DEVICE_H_
#include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
#include <fuchsia/hardware/pciroot/cpp/banjo.h>
#include <lib/ddk/binding.h>
#include <lib/fpromise/promise.h>
#include <lib/svc/outgoing.h>
#include <mutex>
#include <utility>
#include <acpica/acpi.h>
#include <ddktl/device.h>
#include <fbl/mutex.h>
#include "src/devices/board/lib/acpi/event.h"
#include "src/devices/board/lib/acpi/manager.h"
#include "src/devices/board/lib/acpi/resources.h"
namespace acpi {
const char* BusTypeToString(BusType t);
struct DevicePioResource {
explicit DevicePioResource(const resource_io& io)
: base_address{io.minimum}, alignment{io.alignment}, address_length{io.address_length} {}
uint32_t base_address;
uint32_t alignment;
uint32_t address_length;
};
struct DeviceMmioResource {
DeviceMmioResource(bool writeable, uint32_t base_address, uint32_t alignment,
uint32_t address_length)
: writeable{writeable},
base_address{base_address},
alignment{alignment},
address_length{address_length} {}
explicit DeviceMmioResource(const resource_memory_t& mem)
: DeviceMmioResource{mem.writeable, mem.minimum, mem.alignment, mem.address_length} {}
bool writeable;
uint32_t base_address;
uint32_t alignment;
uint32_t address_length;
};
struct DeviceIrqResource {
DeviceIrqResource(const resource_irq irq, int pin_index)
: trigger{irq.trigger},
polarity{irq.polarity},
sharable{irq.sharable},
wake_capable{irq.wake_capable},
pin{static_cast<uint8_t>(irq.pins[pin_index])} {}
uint8_t trigger;
#define ACPI_IRQ_TRIGGER_LEVEL 0
#define ACPI_IRQ_TRIGGER_EDGE 1
uint8_t polarity;
#define ACPI_IRQ_ACTIVE_HIGH 0
#define ACPI_IRQ_ACTIVE_LOW 1
#define ACPI_IRQ_ACTIVE_BOTH 2
uint8_t sharable;
#define ACPI_IRQ_EXCLUSIVE 0
#define ACPI_IRQ_SHARED 1
uint8_t wake_capable;
uint8_t pin;
};
struct DeviceArgs {
zx_device_t* parent_;
acpi::Manager* manager_;
ACPI_HANDLE handle_;
// Bus metadata
std::vector<uint8_t> metadata_;
BusType bus_type_ = BusType::kUnknown;
uint32_t bus_id_ = UINT32_MAX;
// PCI metadata
std::vector<pci_bdf_t> bdfs_;
DeviceArgs(zx_device_t* parent, acpi::Manager* manager, ACPI_HANDLE handle)
: parent_(parent), manager_(manager), handle_(handle) {}
DeviceArgs(DeviceArgs&) = delete;
DeviceArgs& SetBusMetadata(std::vector<uint8_t> metadata, BusType bus_type, uint32_t bus_id) {
metadata_ = std::move(metadata);
bus_type_ = bus_type;
bus_id_ = bus_id;
return *this;
}
DeviceArgs& SetPciMetadata(std::vector<pci_bdf_t> bdfs) {
bdfs_ = std::move(bdfs);
return *this;
}
};
class Device;
using DeviceType = ddk::Device<::acpi::Device, ddk::Initializable, ddk::Unbindable,
ddk::Messageable<fuchsia_hardware_acpi::Device>::Mixin>;
class Device : public DeviceType,
public fidl::WireAsyncEventHandler<fuchsia_hardware_acpi::NotifyHandler> {
public:
explicit Device(DeviceArgs&& args) : Device(args) {}
explicit Device(DeviceArgs& args)
: DeviceType{args.parent_},
manager_{args.manager_},
acpi_{manager_->acpi()},
acpi_handle_{args.handle_},
metadata_{std::move(args.metadata_)},
bus_type_{args.bus_type_},
bus_id_{args.bus_id_},
pci_bdfs_{std::move(args.bdfs_)} {}
// DDK mix-in impls.
void DdkRelease() { delete this; }
void DdkInit(ddk::InitTxn txn);
void DdkUnbind(ddk::UnbindTxn txn);
ACPI_HANDLE acpi_handle() const { return acpi_handle_; }
zx_device_t** mutable_zxdev() { return &zxdev_; }
void AcpiConnectServer(zx::channel server);
// Wrapper around |DdkAdd| which handles setting up FIDL outgoing directory.
zx::status<> AddDevice(const char* name, cpp20::span<zx_device_prop_t> props,
cpp20::span<zx_device_str_prop_t> str_props, uint32_t flags);
// FIDL impls
void GetBusId(GetBusIdRequestView request, GetBusIdCompleter::Sync& completer) override;
void EvaluateObject(EvaluateObjectRequestView request,
EvaluateObjectCompleter::Sync& completer) override;
void MapInterrupt(MapInterruptRequestView request,
MapInterruptCompleter::Sync& completer) override;
void GetPio(GetPioRequestView request, GetPioCompleter::Sync& completer) override;
void GetMmio(GetMmioRequestView request, GetMmioCompleter::Sync& completer) override;
void GetBti(GetBtiRequestView request, GetBtiCompleter::Sync& completer) override;
void InstallNotifyHandler(InstallNotifyHandlerRequestView request,
InstallNotifyHandlerCompleter::Sync& completer) override;
void AcquireGlobalLock(AcquireGlobalLockRequestView request,
AcquireGlobalLockCompleter::Sync& completer) override;
void InstallAddressSpaceHandler(InstallAddressSpaceHandlerRequestView request,
InstallAddressSpaceHandlerCompleter::Sync& completer) override;
std::vector<pci_bdf_t>& pci_bdfs() { return pci_bdfs_; }
void RemoveNotifyHandler();
private:
struct HandlerCtx {
Device* device;
uint32_t space_type;
};
static ACPI_STATUS AddressSpaceHandler(uint32_t function, ACPI_PHYSICAL_ADDRESS physical_address,
uint32_t bit_width, UINT64* value, void* handler_ctx,
void* region_ctx);
static void DeviceObjectNotificationHandler(ACPI_HANDLE object, uint32_t value, void* context);
zx_status_t ReportCurrentResources() __TA_REQUIRES(lock_);
ACPI_STATUS AddResource(ACPI_RESOURCE*) __TA_REQUIRES(lock_);
// Set up FIDL outgoing directory and start serving fuchsia.hardware.acpi.Device.
zx::status<zx::channel> PrepareOutgoing();
acpi::Manager* manager_;
acpi::Acpi* acpi_;
// Handle to the corresponding ACPI node
ACPI_HANDLE acpi_handle_;
// BTI ID for dummy IOMMU.
uint32_t bti_id_ = manager_->GetNextBtiId();
mutable std::mutex lock_;
bool got_resources_ = false;
// Port, memory, and interrupt resources from _CRS respectively
std::vector<DevicePioResource> pio_resources_ __TA_GUARDED(lock_);
std::vector<DeviceMmioResource> mmio_resources_ __TA_GUARDED(lock_);
std::vector<DeviceIrqResource> irqs_ __TA_GUARDED(lock_);
bool can_use_global_lock_ = false;
// FIDL-encoded child metadata.
std::vector<uint8_t> metadata_;
BusType bus_type_ = BusType::kUnknown;
uint32_t bus_id_ = UINT32_MAX;
// TODO(fxbug.dev/32978): remove once kernel PCI is no longer used.
std::vector<pci_bdf_t> pci_bdfs_;
// ACPI events.
std::optional<fidl::WireSharedClient<fuchsia_hardware_acpi::NotifyHandler>> notify_handler_;
std::atomic_size_t pending_notify_count_ = 0;
std::optional<fpromise::promise<void>> notify_teardown_finished_;
std::atomic_bool notify_handler_active_ = false;
uint32_t notify_handler_type_;
bool notify_count_warned_ = false;
// ACPI address space handling.
std::mutex address_handler_lock_;
std::unordered_map<uint32_t, fidl::WireSharedClient<fuchsia_hardware_acpi::AddressSpaceHandler>>
address_handlers_ __TA_GUARDED(address_handler_lock_);
std::vector<fpromise::promise<void>> address_handler_teardown_finished_
__TA_GUARDED(address_handler_lock_);
std::optional<svc::Outgoing> outgoing_;
};
} // namespace acpi
#endif // SRC_DEVICES_BOARD_LIB_ACPI_DEVICE_H_