blob: 5253f78a6088be6a31114f5a8fffd814c0468139 [file] [log] [blame]
// 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.
#include "src/devices/board/drivers/x86/acpi/fidl.h"
#include <fuchsia/hardware/acpi/llcpp/fidl.h>
#include <lib/ddk/debug.h>
#include "lib/fidl/llcpp/message.h"
namespace acpi {
namespace {
ACPI_OBJECT_TYPE FidlTypeToAcpiType(fuchsia_hardware_acpi::wire::ObjectType type) {
using ObjectType = fuchsia_hardware_acpi::wire::ObjectType;
switch (type) {
case ObjectType::kAny:
return ACPI_TYPE_ANY;
case ObjectType::kBuffer:
return ACPI_TYPE_BUFFER;
case ObjectType::kBufferField:
return ACPI_TYPE_BUFFER_FIELD;
case ObjectType::kDebugObject:
return ACPI_TYPE_DEBUG_OBJECT;
case ObjectType::kDevice:
return ACPI_TYPE_DEVICE;
case ObjectType::kEvent:
return ACPI_TYPE_EVENT;
case ObjectType::kFieldUnit:
return ACPI_TYPE_FIELD_UNIT;
case ObjectType::kInteger:
return ACPI_TYPE_INTEGER;
case ObjectType::kMethod:
return ACPI_TYPE_METHOD;
case ObjectType::kMutex:
return ACPI_TYPE_MUTEX;
case ObjectType::kOperationRegion:
return ACPI_TYPE_REGION;
case ObjectType::kPackage:
return ACPI_TYPE_PACKAGE;
case ObjectType::kPowerResource:
return ACPI_TYPE_POWER;
case ObjectType::kString:
return ACPI_TYPE_STRING;
case ObjectType::kThermalZone:
return ACPI_TYPE_THERMAL;
}
zxlogf(ERROR, "Unknown ACPI object type %d", int(type));
return ACPI_TYPE_ANY;
}
fuchsia_hardware_acpi::wire::ObjectType AcpiTypeToFidlType(ACPI_OBJECT_TYPE type) {
using ObjectType = fuchsia_hardware_acpi::wire::ObjectType;
switch (type) {
case ACPI_TYPE_ANY:
return ObjectType::kAny;
case ACPI_TYPE_BUFFER:
return ObjectType::kBuffer;
case ACPI_TYPE_BUFFER_FIELD:
return ObjectType::kBufferField;
case ACPI_TYPE_DEBUG_OBJECT:
return ObjectType::kDebugObject;
case ACPI_TYPE_DEVICE:
return ObjectType::kDevice;
case ACPI_TYPE_EVENT:
return ObjectType::kEvent;
case ACPI_TYPE_FIELD_UNIT:
return ObjectType::kFieldUnit;
case ACPI_TYPE_INTEGER:
return ObjectType::kInteger;
case ACPI_TYPE_METHOD:
return ObjectType::kMethod;
case ACPI_TYPE_MUTEX:
return ObjectType::kMutex;
case ACPI_TYPE_REGION:
return ObjectType::kOperationRegion;
case ACPI_TYPE_PACKAGE:
return ObjectType::kPackage;
case ACPI_TYPE_POWER:
return ObjectType::kPowerResource;
case ACPI_TYPE_STRING:
return ObjectType::kString;
case ACPI_TYPE_THERMAL:
return ObjectType::kThermalZone;
}
zxlogf(ERROR, "Unknown ACPI object type %d", type);
return ObjectType::Unknown();
}
} // namespace
EvaluateObjectFidlHelper EvaluateObjectFidlHelper::FromRequest(acpi::Acpi* acpi, ACPI_HANDLE device,
EvaluateObjectRequestView& request) {
std::string path(request->path.data(), request->path.size());
return EvaluateObjectFidlHelper(acpi, device, std::move(path), request->parameters);
}
acpi::status<fuchsia_hardware_acpi::wire::DeviceEvaluateObjectResult>
EvaluateObjectFidlHelper::Evaluate(fidl::AnyAllocator& alloc) {
auto path = ValidateAndLookupPath(request_path_.data());
if (path.is_error()) {
return path.take_error();
}
auto result = DecodeParameters(request_params_);
if (result.is_error()) {
return result.take_error();
}
auto value = acpi_->EvaluateObject(nullptr, path->data(), result.value());
if (value.is_error()) {
return value.take_error();
}
auto fidl_val = EncodeReturnValue(alloc, value.value().get());
if (fidl_val.is_error()) {
return fidl_val.take_error();
}
return acpi::ok(fidl_val.value());
}
acpi::status<std::string> EvaluateObjectFidlHelper::ValidateAndLookupPath(const char* request_path,
ACPI_HANDLE* hnd) {
auto result = acpi_->GetHandle(device_handle_, request_path);
if (result.is_error()) {
return result.take_error();
}
auto my_path = acpi_->GetPath(device_handle_);
if (my_path.is_error()) {
return my_path.take_error();
}
ACPI_HANDLE target = result.value();
auto abs_path = acpi_->GetPath(target);
if (abs_path.is_error()) {
return abs_path.take_error();
}
if (!strncmp(my_path->data(), abs_path->data(), my_path->size())) {
if (hnd) {
*hnd = target;
}
return acpi::ok(std::move(abs_path.value()));
}
return acpi::error(AE_ACCESS);
}
acpi::status<std::vector<ACPI_OBJECT>> EvaluateObjectFidlHelper::DecodeParameters(
fidl::VectorView<fuchsia_hardware_acpi::wire::Object>& request_params) {
std::vector<ACPI_OBJECT> result(request_params.count());
size_t i = 0;
for (auto& param : request_params) {
auto status = DecodeObject(param, &result[i]);
if (status.is_error()) {
return status.take_error();
}
i++;
}
return acpi::ok(std::move(result));
}
acpi::status<fuchsia_hardware_acpi::wire::DeviceEvaluateObjectResult>
EvaluateObjectFidlHelper::EncodeReturnValue(fidl::AnyAllocator& alloc, ACPI_OBJECT* value) {
auto result = EncodeObject(alloc, value);
if (result.is_error()) {
return result.take_error();
}
// TODO(fxbug.dev/79172): put the data in a VMO if it's too big.
fuchsia_hardware_acpi::wire::EncodedObject encoded;
encoded.set_object(alloc, result.value());
fuchsia_hardware_acpi::wire::DeviceEvaluateObjectResponse response;
response.result = encoded;
fuchsia_hardware_acpi::wire::DeviceEvaluateObjectResult ret;
ret.set_response(alloc, response);
return acpi::ok(ret);
}
acpi::status<fuchsia_hardware_acpi::wire::Object> EvaluateObjectFidlHelper::EncodeObject(
fidl::AnyAllocator& alloc, ACPI_OBJECT* value) {
fuchsia_hardware_acpi::wire::Object result;
switch (value->Type) {
case ACPI_TYPE_INTEGER: {
result.set_integer_val(alloc, value->Integer.Value);
break;
}
case ACPI_TYPE_STRING: {
fidl::StringView sv;
sv.Set(alloc, cpp17::string_view(value->String.Pointer, value->String.Length));
result.set_string_val(alloc, sv);
break;
}
case ACPI_TYPE_PACKAGE: {
fidl::VectorView<fuchsia_hardware_acpi::wire::Object> view;
view.Allocate(alloc, value->Package.Count);
for (size_t i = 0; i < value->Package.Count; i++) {
auto ret = EncodeObject(alloc, &value->Package.Elements[i]);
if (ret.is_error()) {
return ret.take_error();
}
view[i] = ret.value();
}
fuchsia_hardware_acpi::wire::ObjectList list;
list.value = view;
result.set_package_val(alloc, list);
break;
}
case ACPI_TYPE_BUFFER: {
fidl::VectorView<uint8_t> data;
data.Allocate(alloc, value->Buffer.Length);
memcpy(data.mutable_data(), value->Buffer.Pointer, value->Buffer.Length);
result.set_buffer_val(alloc, data);
break;
}
case ACPI_TYPE_POWER: {
fuchsia_hardware_acpi::wire::PowerResource power;
power.resource_order = value->PowerResource.ResourceOrder;
power.system_level = value->PowerResource.SystemLevel;
result.set_power_resource_val(alloc, power);
break;
}
case ACPI_TYPE_PROCESSOR: {
fuchsia_hardware_acpi::wire::Processor processor;
processor.id = value->Processor.ProcId;
processor.pblk_address = value->Processor.PblkAddress;
processor.pblk_length = value->Processor.PblkLength;
result.set_processor_val(alloc, processor);
break;
}
case ACPI_TYPE_LOCAL_REFERENCE: {
auto handle_path = acpi_->GetPath(value->Reference.Handle);
if (handle_path.is_error()) {
return handle_path.take_error();
}
auto my_path = acpi_->GetPath(device_handle_);
if (my_path.is_error()) {
return my_path.take_error();
}
if (strncmp(my_path->data(), handle_path->data(), my_path->size()) != 0) {
zxlogf(WARNING, "EvaluateObject returned a reference to an external object: %s",
handle_path->data());
return acpi::error(AE_ACCESS);
}
fuchsia_hardware_acpi::wire::Handle hnd;
hnd.object_type = AcpiTypeToFidlType(value->Reference.ActualType);
fidl::StringView sv;
sv.Set(alloc, cpp17::string_view(handle_path.value()));
hnd.path = sv;
result.set_reference_val(alloc, hnd);
break;
}
default:
zxlogf(ERROR, "Unexpected return type from EvaluateObject: %d", value->Type);
return acpi::error(AE_NOT_IMPLEMENTED);
}
return acpi::ok(result);
}
acpi::status<> EvaluateObjectFidlHelper::DecodeObject(
const fuchsia_hardware_acpi::wire::Object& obj, ACPI_OBJECT* out) {
using Tag = fuchsia_hardware_acpi::wire::Object::Tag;
switch (obj.which()) {
case Tag::kIntegerVal: {
out->Integer.Type = ACPI_TYPE_INTEGER;
out->Integer.Value = obj.integer_val();
break;
}
case Tag::kStringVal: {
// ACPI strings need to be null terminated. FIDL strings aren't, so we have to make copies
// of them.
allocated_strings_.emplace_front(
std::string(obj.string_val().data(), obj.string_val().size()));
out->String.Type = ACPI_TYPE_STRING;
out->String.Length = obj.string_val().size();
out->String.Pointer = allocated_strings_.front().data();
break;
}
case Tag::kPackageVal: {
auto& list = obj.package_val().value;
std::vector<ACPI_OBJECT> package(list.count());
for (size_t i = 0; i < list.count(); i++) {
auto status = DecodeObject(list[i], &package[i]);
if (status.is_error()) {
return status.take_error();
}
}
allocated_packages_.emplace_front(std::move(package));
out->Package.Type = ACPI_TYPE_PACKAGE;
out->Package.Count = list.count();
out->Package.Elements = allocated_packages_.front().data();
break;
}
case Tag::kBufferVal: {
auto& buffer = obj.buffer_val();
out->Buffer.Type = ACPI_TYPE_BUFFER;
out->Buffer.Length = buffer.count();
out->Buffer.Pointer = buffer.mutable_data();
break;
}
case Tag::kPowerResourceVal: {
auto& power = obj.power_resource_val();
out->PowerResource.Type = ACPI_TYPE_POWER;
out->PowerResource.ResourceOrder = power.resource_order;
out->PowerResource.SystemLevel = power.system_level;
break;
}
case Tag::kProcessorVal: {
auto& processor = obj.processor_val();
out->Processor.Type = ACPI_TYPE_PROCESSOR;
out->Processor.PblkAddress = processor.pblk_address;
out->Processor.PblkLength = processor.pblk_length;
out->Processor.ProcId = processor.id;
break;
}
case Tag::kReferenceVal: {
auto& ref = obj.reference_val();
std::string path(ref.path.data(), ref.path.size());
ACPI_HANDLE hnd = nullptr;
auto result = ValidateAndLookupPath(path.data(), &hnd);
if (result.is_error()) {
return result.take_error();
}
out->Reference.Type = ACPI_TYPE_LOCAL_REFERENCE;
out->Reference.ActualType = FidlTypeToAcpiType(ref.object_type);
out->Reference.Handle = hnd;
break;
}
case Tag::kUnknown:
return acpi::error(AE_NOT_IMPLEMENTED);
}
return acpi::ok();
}
} // namespace acpi