blob: 070d1bda87452d4fbb524e1e7fbc42d72cc5589c [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.
#include "src/devices/board/lib/acpi/fidl.h"
#include <fidl/fuchsia.hardware.acpi/cpp/wire.h>
#include <lib/fidl/llcpp/vector_view.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/resource.h>
#include <zxtest/zxtest.h>
#include "fidl/fuchsia.hardware.acpi/cpp/wire_types.h"
#include "lib/fake-resource/resource.h"
#include "src/devices/board/lib/acpi/acpi.h"
#include "src/devices/board/lib/acpi/test/mock-acpi.h"
namespace facpi = fuchsia_hardware_acpi::wire;
using acpi::test::Device;
using EvaluateObjectRequestView =
fidl::WireServer<fuchsia_hardware_acpi::Device>::EvaluateObjectRequestView;
using EvaluateObjectCompleter =
fidl::WireServer<fuchsia_hardware_acpi::Device>::EvaluateObjectCompleter;
using fuchsia_hardware_acpi::wire::EvaluateObjectMode;
namespace {
void CheckEq(ACPI_OBJECT value, ACPI_OBJECT expected) {
ASSERT_EQ(value.Type, expected.Type);
switch (value.Type) {
case ACPI_TYPE_INTEGER:
ASSERT_EQ(value.Integer.Value, expected.Integer.Value);
break;
case ACPI_TYPE_STRING:
ASSERT_EQ(value.String.Length, expected.String.Length);
ASSERT_STREQ(value.String.Pointer, expected.String.Pointer);
break;
case ACPI_TYPE_PACKAGE:
ASSERT_EQ(value.Package.Count, expected.Package.Count);
for (size_t i = 0; i < value.Package.Count; i++) {
ASSERT_NO_FATAL_FAILURE(CheckEq(value.Package.Elements[i], expected.Package.Elements[i]));
}
break;
case ACPI_TYPE_BUFFER:
ASSERT_EQ(value.Buffer.Length, expected.Buffer.Length);
ASSERT_BYTES_EQ(value.Buffer.Pointer, expected.Buffer.Pointer, value.Buffer.Length);
break;
case ACPI_TYPE_POWER:
ASSERT_EQ(value.PowerResource.ResourceOrder, expected.PowerResource.ResourceOrder);
ASSERT_EQ(value.PowerResource.SystemLevel, expected.PowerResource.SystemLevel);
break;
case ACPI_TYPE_PROCESSOR:
ASSERT_EQ(value.Processor.PblkAddress, expected.Processor.PblkAddress);
ASSERT_EQ(value.Processor.PblkLength, expected.Processor.PblkLength);
ASSERT_EQ(value.Processor.ProcId, expected.Processor.ProcId);
break;
case ACPI_TYPE_LOCAL_REFERENCE:
ASSERT_EQ(value.Reference.ActualType, expected.Reference.ActualType);
ASSERT_EQ(value.Reference.Handle, expected.Reference.Handle);
break;
default:
ASSERT_FALSE(true, "Unexpected object type");
}
}
void CheckEq(facpi::Object value, facpi::Object expected) {
using Tag = fuchsia_hardware_acpi::wire::Object::Tag;
ASSERT_EQ(value.Which(), expected.Which());
switch (value.Which()) {
case Tag::kIntegerVal: {
ASSERT_EQ(value.integer_val(), expected.integer_val());
break;
}
case Tag::kStringVal: {
ASSERT_EQ(value.string_val().size(), expected.string_val().size());
ASSERT_BYTES_EQ(value.string_val().data(), expected.string_val().data(),
value.string_val().size());
break;
}
case Tag::kPackageVal: {
auto &val_list = value.package_val().value;
auto &exp_list = expected.package_val().value;
ASSERT_EQ(val_list.count(), exp_list.count());
for (size_t i = 0; i < val_list.count(); i++) {
ASSERT_NO_FATAL_FAILURE(CheckEq(val_list[i], exp_list[i]));
}
break;
}
case Tag::kBufferVal: {
auto &val = value.buffer_val();
auto &exp = expected.buffer_val();
ASSERT_EQ(val.count(), exp.count());
ASSERT_BYTES_EQ(val.data(), exp.data(), val.count());
break;
}
case Tag::kPowerResourceVal: {
auto &val = value.power_resource_val();
auto &exp = expected.power_resource_val();
ASSERT_EQ(val.resource_order, exp.resource_order);
ASSERT_EQ(val.system_level, exp.system_level);
break;
}
case Tag::kProcessorVal: {
auto &val = value.processor_val();
auto &exp = expected.processor_val();
ASSERT_EQ(val.id, exp.id);
ASSERT_EQ(val.pblk_address, exp.pblk_address);
ASSERT_EQ(val.pblk_length, exp.pblk_length);
break;
}
case Tag::kReferenceVal: {
auto &val = value.reference_val();
auto &exp = expected.reference_val();
ASSERT_EQ(val.path.size(), exp.path.size());
ASSERT_BYTES_EQ(val.path.data(), exp.path.data(), val.path.size());
ASSERT_EQ(val.object_type, exp.object_type);
break;
}
case Tag::kUnknown:
ASSERT_TRUE(false);
}
}
acpi::UniquePtr<ACPI_RESOURCE> MakeResources(cpp20::span<ACPI_RESOURCE> resources) {
ACPI_RESOURCE *alloc = static_cast<ACPI_RESOURCE *>(ACPI_ALLOCATE_ZEROED(resources.size_bytes()));
ZX_ASSERT(alloc != nullptr);
memcpy(alloc, resources.data(), resources.size_bytes());
return acpi::UniquePtr<ACPI_RESOURCE>(alloc);
}
} // namespace
class FidlEvaluateObjectTest : public zxtest::Test {
public:
void SetUp() override { acpi_.SetDeviceRoot(std::make_unique<Device>("\\")); }
void InsertDeviceBelow(std::string path, std::unique_ptr<Device> d) {
Device *parent = acpi_.GetDeviceRoot()->FindByPath(path);
ASSERT_NE(parent, nullptr);
parent->AddChild(std::move(d));
}
protected:
acpi::test::MockAcpi acpi_;
};
TEST_F(FidlEvaluateObjectTest, TestCantEvaluateParent) {
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\", std::make_unique<Device>("_SB_")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI0")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_.PCI0", std::make_unique<Device>("I2C0")));
acpi::EvaluateObjectFidlHelper helper(
&acpi_, acpi_.GetDeviceRoot()->FindByPath("\\_SB_.PCI0.I2C0"), "\\_SB_.PCI0",
EvaluateObjectMode::kPlainObject, fidl::VectorView<facpi::Object>(nullptr, 0));
auto result = helper.ValidateAndLookupPath("\\_SB_.PCI0");
ASSERT_EQ(result.status_value(), AE_ACCESS);
}
TEST_F(FidlEvaluateObjectTest, TestCantEvaluateSibling) {
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\", std::make_unique<Device>("_SB_")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI0")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI1")));
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot()->FindByPath("\\_SB_.PCI1"),
"\\_SB_.PCI0", EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
auto result = helper.ValidateAndLookupPath("\\_SB_.PCI0");
ASSERT_EQ(result.status_value(), AE_ACCESS);
}
TEST_F(FidlEvaluateObjectTest, TestCanEvaluateChild) {
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\", std::make_unique<Device>("_SB_")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI0")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_.PCI0", std::make_unique<Device>("I2C0")));
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot()->FindByPath("\\_SB_.PCI0"),
"I2C0", EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
ACPI_HANDLE hnd;
auto result = helper.ValidateAndLookupPath("I2C0", &hnd);
ASSERT_EQ(result.status_value(), AE_OK);
ASSERT_EQ(result.value(), "\\_SB_.PCI0.I2C0");
ASSERT_EQ(hnd, acpi_.GetDeviceRoot()->FindByPath("\\_SB_.PCI0.I2C0"));
}
TEST_F(FidlEvaluateObjectTest, TestDecodeInteger) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
facpi::Object obj;
ACPI_OBJECT out;
fidl::Arena<> alloc;
obj = facpi::Object::WithIntegerVal(alloc, 42);
auto status = helper.DecodeObject(obj, &out);
ASSERT_OK(status.status_value());
ASSERT_NO_FATAL_FAILURE(CheckEq(out, ACPI_OBJECT{
.Integer =
{
.Type = ACPI_TYPE_INTEGER,
.Value = 42,
},
}));
}
TEST_F(FidlEvaluateObjectTest, TestDecodeString) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
facpi::Object obj;
ACPI_OBJECT out;
fidl::Arena<> alloc;
obj = facpi::Object::WithStringVal(alloc, "test string");
auto status = helper.DecodeObject(obj, &out);
ASSERT_OK(status.status_value());
ASSERT_NO_FATAL_FAILURE(CheckEq(out, ACPI_OBJECT{
.String =
{
.Type = ACPI_TYPE_STRING,
.Length = 11,
.Pointer = const_cast<char *>("test string"),
},
}));
}
TEST_F(FidlEvaluateObjectTest, TestDecodeBuffer) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
facpi::Object obj;
ACPI_OBJECT out;
fidl::Arena<> alloc;
static constexpr uint8_t kBuffer[] = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
obj = facpi::Object::WithBufferVal(
alloc,
fidl::VectorView<uint8_t>::FromExternal(const_cast<uint8_t *>(kBuffer), std::size(kBuffer)));
auto status = helper.DecodeObject(obj, &out);
ASSERT_OK(status.status_value());
ASSERT_NO_FATAL_FAILURE(CheckEq(out, ACPI_OBJECT{
.Buffer =
{
.Type = ACPI_TYPE_BUFFER,
.Length = std::size(kBuffer),
.Pointer = const_cast<uint8_t *>(kBuffer),
},
}));
}
TEST_F(FidlEvaluateObjectTest, TestDecodePowerResource) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
facpi::Object obj;
ACPI_OBJECT out;
fidl::Arena<> alloc;
facpi::PowerResource power;
power.resource_order = 9;
power.system_level = 32;
obj = facpi::Object::WithPowerResourceVal(alloc, power);
auto status = helper.DecodeObject(obj, &out);
ASSERT_OK(status.status_value());
ASSERT_NO_FATAL_FAILURE(CheckEq(out, ACPI_OBJECT{
.PowerResource =
{
.Type = ACPI_TYPE_POWER,
.SystemLevel = 32,
.ResourceOrder = 9,
},
}));
}
TEST_F(FidlEvaluateObjectTest, TestDecodeProcessorVal) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
facpi::Object obj;
ACPI_OBJECT out;
fidl::Arena<> alloc;
facpi::Processor processor;
processor.pblk_address = 0xd00dfeed;
processor.pblk_length = 0xabc;
processor.id = 7;
obj = facpi::Object::WithProcessorVal(alloc, processor);
auto status = helper.DecodeObject(obj, &out);
ASSERT_OK(status.status_value());
ASSERT_NO_FATAL_FAILURE(CheckEq(out, ACPI_OBJECT{
.Processor =
{
.Type = ACPI_TYPE_PROCESSOR,
.ProcId = 7,
.PblkAddress = 0xd00dfeed,
.PblkLength = 0xabc,
},
}));
}
TEST_F(FidlEvaluateObjectTest, TestDecodeReference) {
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\", std::make_unique<Device>("_SB_")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI0")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_.PCI0", std::make_unique<Device>("I2C0")));
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot()->FindByPath("\\_SB_"),
"\\_SB_", EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
facpi::Object obj;
ACPI_OBJECT out;
fidl::Arena<> alloc;
facpi::Handle ref;
ref.object_type = facpi::ObjectType::kDevice;
ref.path = "PCI0.I2C0";
obj = facpi::Object::WithReferenceVal(alloc, ref);
auto status = helper.DecodeObject(obj, &out);
ASSERT_OK(status.status_value());
ASSERT_NO_FATAL_FAILURE(
CheckEq(out, ACPI_OBJECT{
.Reference =
{
.Type = ACPI_TYPE_LOCAL_REFERENCE,
.ActualType = ACPI_TYPE_DEVICE,
.Handle = acpi_.GetDeviceRoot()->FindByPath("\\_SB_.PCI0.I2C0"),
},
}));
}
TEST_F(FidlEvaluateObjectTest, TestDecodeParentReferenceFails) {
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\", std::make_unique<Device>("_SB_")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI0")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_.PCI0", std::make_unique<Device>("I2C0")));
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot()->FindByPath("\\_SB_"),
"\\_SB_", EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
facpi::Object obj;
ACPI_OBJECT out;
fidl::Arena<> alloc;
facpi::Handle ref;
ref.object_type = facpi::ObjectType::kDevice;
ref.path = "\\";
obj = facpi::Object::WithReferenceVal(alloc, ref);
auto status = helper.DecodeObject(obj, &out);
ASSERT_EQ(status.status_value(), AE_ACCESS);
}
TEST_F(FidlEvaluateObjectTest, TestDecodePackage) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
std::vector<facpi::Object> elements;
facpi::Object obj;
facpi::ObjectList list;
ACPI_OBJECT out;
fidl::Arena<> alloc;
obj = facpi::Object::WithIntegerVal(alloc, 32);
elements.emplace_back(obj);
obj = facpi::Object::WithStringVal(alloc, "test string");
elements.emplace_back(obj);
list.value = fidl::VectorView<facpi::Object>::FromExternal(elements);
obj = facpi::Object::WithPackageVal(alloc, list);
auto status = helper.DecodeObject(obj, &out);
ASSERT_OK(status.status_value());
constexpr ACPI_OBJECT kObjects[2] = {
{.Integer =
{
.Type = ACPI_TYPE_INTEGER,
.Value = 32,
}},
{.String =
{
.Type = ACPI_TYPE_STRING,
.Length = 11,
.Pointer = const_cast<char *>("test string"),
}},
};
ACPI_OBJECT expected = {
.Package =
{
.Type = ACPI_TYPE_PACKAGE,
.Count = 2,
.Elements = const_cast<acpi_object *>(kObjects),
},
};
ASSERT_NO_FATAL_FAILURE(CheckEq(out, expected));
}
TEST_F(FidlEvaluateObjectTest, TestDecodeParameters) {
fidl::Arena<> alloc;
std::vector<facpi::Object> params;
params.emplace_back(facpi::Object::WithIntegerVal(alloc, 32));
params.emplace_back(facpi::Object::WithStringVal(alloc, "hello"));
constexpr uint8_t kData[] = {1, 2, 3};
params.emplace_back(facpi::Object::WithBufferVal(
alloc,
fidl::VectorView<uint8_t>::FromExternal(const_cast<uint8_t *>(kData), std::size(kData))));
auto view = fidl::VectorView<facpi::Object>::FromExternal(params);
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject, view);
auto result = helper.DecodeParameters(view);
ASSERT_OK(result.zx_status_value());
ACPI_OBJECT expected[] = {
{.Integer = {.Type = ACPI_TYPE_INTEGER, .Value = 32}},
{.String = {.Type = ACPI_TYPE_STRING, .Length = 5, .Pointer = const_cast<char *>("hello")}},
{.Buffer = {.Type = ACPI_TYPE_BUFFER, .Length = 3, .Pointer = const_cast<uint8_t *>(kData)}},
};
auto value = std::move(result.value());
ASSERT_EQ(value.size(), std::size(expected));
for (size_t i = 0; i < std::size(expected); i++) {
ASSERT_NO_FATAL_FAILURE(CheckEq(value[i], expected[i]), "param %zd", i);
}
}
TEST_F(FidlEvaluateObjectTest, TestEncodeInt) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
fidl::Arena<> alloc;
ACPI_OBJECT obj = {.Integer = {.Type = ACPI_TYPE_INTEGER, .Value = 320}};
auto result = helper.EncodeObject(alloc, &obj);
ASSERT_OK(result.zx_status_value());
facpi::Object expected = facpi::Object::WithIntegerVal(alloc, 320);
ASSERT_NO_FATAL_FAILURE(CheckEq(result.value(), expected));
}
TEST_F(FidlEvaluateObjectTest, TestEncodeString) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
fidl::Arena<> alloc;
ACPI_OBJECT obj = {.String = {
.Type = ACPI_TYPE_STRING,
.Length = 3,
.Pointer = const_cast<char *>("abc"),
}};
auto result = helper.EncodeObject(alloc, &obj);
ASSERT_OK(result.zx_status_value());
facpi::Object expected = facpi::Object::WithStringVal(alloc, "abc");
ASSERT_NO_FATAL_FAILURE(CheckEq(result.value(), expected));
}
TEST_F(FidlEvaluateObjectTest, TestEncodeBuffer) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
fidl::Arena<> alloc;
static constexpr uint8_t kBuffer[] = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
ACPI_OBJECT obj = {.Buffer = {
.Type = ACPI_TYPE_BUFFER,
.Length = std::size(kBuffer),
.Pointer = const_cast<uint8_t *>(kBuffer),
}};
auto result = helper.EncodeObject(alloc, &obj);
ASSERT_OK(result.zx_status_value());
facpi::Object expected = facpi::Object::WithBufferVal(
alloc,
fidl::VectorView<uint8_t>::FromExternal(const_cast<uint8_t *>(kBuffer), std::size(kBuffer)));
ASSERT_NO_FATAL_FAILURE(CheckEq(result.value(), expected));
}
TEST_F(FidlEvaluateObjectTest, TestEncodeProcessorVal) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
fidl::Arena<> alloc;
ACPI_OBJECT obj = {
.Processor =
{
.Type = ACPI_TYPE_PROCESSOR,
.ProcId = 7,
.PblkAddress = 0xd00dfeed,
.PblkLength = 0xabc,
},
};
auto result = helper.EncodeObject(alloc, &obj);
ASSERT_OK(result.zx_status_value());
facpi::Processor processor;
processor.pblk_address = 0xd00dfeed;
processor.pblk_length = 0xabc;
processor.id = 7;
facpi::Object expected = facpi::Object::WithProcessorVal(alloc, processor);
ASSERT_NO_FATAL_FAILURE(CheckEq(result.value(), expected));
}
TEST_F(FidlEvaluateObjectTest, TestEncodeReference) {
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\", std::make_unique<Device>("_SB_")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI0")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_.PCI0", std::make_unique<Device>("I2C0")));
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot()->FindByPath("\\_SB_"),
"\\_SB_", EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
fidl::Arena<> alloc;
ACPI_OBJECT obj = {.Reference = {
.Type = ACPI_TYPE_LOCAL_REFERENCE,
.ActualType = ACPI_TYPE_DEVICE,
.Handle = acpi_.GetDeviceRoot()->FindByPath("\\_SB_.PCI0.I2C0"),
}};
facpi::Object expected;
facpi::Handle ref;
ref.object_type = facpi::ObjectType::kDevice;
ref.path = "\\_SB_.PCI0.I2C0";
expected = facpi::Object::WithReferenceVal(alloc, ref);
auto result = helper.EncodeObject(alloc, &obj);
ASSERT_OK(result.zx_status_value());
ASSERT_NO_FATAL_FAILURE(CheckEq(result.value(), expected));
}
TEST_F(FidlEvaluateObjectTest, TestEncodeParentReferenceFails) {
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\", std::make_unique<Device>("_SB_")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_", std::make_unique<Device>("PCI0")));
ASSERT_NO_FATAL_FAILURE(InsertDeviceBelow("\\_SB_.PCI0", std::make_unique<Device>("I2C0")));
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot()->FindByPath("\\_SB_"),
"\\_SB_", EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
ACPI_OBJECT obj = {.Reference = {
.Type = ACPI_TYPE_LOCAL_REFERENCE,
.ActualType = ACPI_TYPE_DEVICE,
.Handle = acpi_.GetDeviceRoot(),
}};
fidl::Arena<> alloc;
auto status = helper.EncodeObject(alloc, &obj);
ASSERT_EQ(status.status_value(), AE_ACCESS);
}
TEST_F(FidlEvaluateObjectTest, TestEncodePackage) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
constexpr ACPI_OBJECT kObjects[2] = {
{.Integer =
{
.Type = ACPI_TYPE_INTEGER,
.Value = 32,
}},
{.String =
{
.Type = ACPI_TYPE_STRING,
.Length = 11,
.Pointer = const_cast<char *>("test string"),
}},
};
ACPI_OBJECT obj = {
.Package =
{
.Type = ACPI_TYPE_PACKAGE,
.Count = 2,
.Elements = const_cast<acpi_object *>(kObjects),
},
};
fidl::Arena<> alloc;
auto result = helper.EncodeObject(alloc, &obj);
ASSERT_OK(result.zx_status_value());
std::vector<facpi::Object> elements;
facpi::ObjectList list;
elements.emplace_back(facpi::Object::WithIntegerVal(alloc, 32));
elements.emplace_back(facpi::Object::WithStringVal(alloc, "test string"));
list.value = fidl::VectorView<facpi::Object>::FromExternal(elements);
auto expected = facpi::Object::WithPackageVal(alloc, list);
ASSERT_NO_FATAL_FAILURE(CheckEq(result.value(), expected));
}
TEST_F(FidlEvaluateObjectTest, TestEncodeReturnValue) {
ACPI_OBJECT obj = {.Integer = {.Type = ACPI_TYPE_INTEGER, .Value = 47}};
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>(nullptr, 0));
fidl::Arena<> alloc;
auto result = helper.EncodeReturnValue(alloc, &obj);
ASSERT_OK(result.zx_status_value());
ASSERT_FALSE(result.value().is_err());
auto &object = result.value().response().result;
// Expect a value of this size to be encoded in-line.
auto expected = facpi::Object::WithIntegerVal(alloc, 47);
ASSERT_NO_FATAL_FAILURE(CheckEq(object.object(), expected));
}
TEST_F(FidlEvaluateObjectTest, TestEncodeMmioResource) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kParseResources,
fidl::VectorView<facpi::Object>(nullptr, 0));
// Set up a list of fake ACPI_RESOURCEs.
std::vector<ACPI_RESOURCE> resources;
resources.emplace_back(ACPI_RESOURCE{
.Type = ACPI_RESOURCE_TYPE_ADDRESS64,
.Length = sizeof(ACPI_RESOURCE),
.Data =
{
.Address64 =
{
.Address =
{
.Minimum = 32,
.AddressLength = zx_system_get_page_size(),
},
},
},
});
resources.emplace_back(ACPI_RESOURCE{
.Type = ACPI_RESOURCE_TYPE_END_TAG,
.Length = sizeof(ACPI_RESOURCE),
});
acpi_.SetResource(MakeResources(resources));
zx_handle_t fake_root_resource;
ASSERT_OK(fake_root_resource_create(&fake_root_resource));
// Make a resource that exactly encompasses the MMIO range we expect, so that we ensure the test
// requests the right range.
zx_handle_t child_resource;
ASSERT_OK(zx_resource_create(fake_root_resource, ZX_RSRC_KIND_MMIO, 0,
2lu * zx_system_get_page_size(), nullptr, 0, &child_resource));
helper.SetMmioResource(child_resource);
fidl::Arena<> arena;
ACPI_OBJECT fake_object = {
.Buffer =
{
.Type = ACPI_TYPE_BUFFER,
.Length = 0,
.Pointer = nullptr,
},
};
auto result = helper.EncodeResourcesReturnValue(arena, &fake_object);
ASSERT_OK(result.zx_status_value());
ASSERT_TRUE(result->is_response());
ASSERT_TRUE(result->response().result.is_resources());
auto &fidl = result->response().result.resources();
ASSERT_EQ(fidl.count(), 1);
ASSERT_TRUE(fidl[0].is_mmio());
ASSERT_EQ(fidl[0].mmio().offset, 32);
ASSERT_EQ(fidl[0].mmio().size, zx_system_get_page_size());
}
TEST_F(FidlEvaluateObjectTest, TestMethodReturnsNothing) {
acpi::EvaluateObjectFidlHelper helper(&acpi_, acpi_.GetDeviceRoot(), "\\",
EvaluateObjectMode::kPlainObject,
fidl::VectorView<facpi::Object>());
fidl::Arena<> arena;
auto result = helper.EncodeReturnValue(arena, nullptr);
ASSERT_EQ(result.status_value(), AE_OK);
ASSERT_TRUE(result->response().result.has_invalid_tag());
}