blob: 0caff2e9ffac5f0b5f017cbbc8e7171dec3741bf [file] [log] [blame]
// Copyright 2018 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 "methods.h"
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <zircon/types.h>
#include <acpica/acpi.h>
#include <acpica/acuuid.h>
#include <fbl/auto_call.h>
#include "errors.h"
#include "util.h"
static zx_status_t uuid_str_to_uint8_buf(const char* uuid_str, uint8_t* uuid) {
if (strlen(uuid_str) != 36) {
return ZX_ERR_WRONG_TYPE;
}
// Converts the format string aabbccdd-eeff-gghh-iijj-kkllmmnnoopp to
// { dd, cc, bb, aa, ff, ee, hh, gg, ii, jj, kk, ll, mm, nn, oo, pp }
// per ACPI Spec 6.1, 19.6.136
int ret =
sscanf(uuid_str,
"%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8
"%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8
"%02" SCNx8 "%02" SCNx8,
&uuid[3], &uuid[2], &uuid[1], &uuid[0], &uuid[5], &uuid[4], &uuid[7], &uuid[6],
&uuid[8], &uuid[9], &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], &uuid[15]);
if (ret != 16) {
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
// Call the ACPI _BNN method to query a PCI Host Bridge's base bus number.
zx_status_t acpi_bbn_call(ACPI_HANDLE dev_obj, uint8_t* out_bbn) {
uint64_t tmp = 0;
ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_BBN", &tmp);
// BBN is returned in the lower 8 bits
*out_bbn = tmp & 0xFF;
return acpi_to_zx_status(status);
}
// Call the ACPI _CRT method to query critical shutdown temperature.
zx_status_t acpi_crt_call(ACPI_HANDLE dev_obj, uint64_t* out) {
ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_CRT", out);
return acpi_to_zx_status(status);
}
enum {
OSC_RET_FAILURE = (1u << 1),
OSC_RET_BAD_UUID = (1u << 2),
OSC_RET_BAD_REV = (1u << 3),
OSC_RET_MASKED = (1u << 4),
};
// Check for the 3 bits that indicate a failure in calling _OSC
static constexpr bool osc_bad_result(uint32_t val) {
return (val & (OSC_RET_FAILURE | OSC_RET_BAD_UUID | OSC_RET_BAD_REV));
}
// Call the ACPI _OSC method to query and negotiate OS capabilities.
zx_status_t acpi_osc_call(ACPI_HANDLE dev_obj, const char* uuid_str, uint64_t revision,
size_t dword_cnt, uint32_t* dwords_in, uint32_t* dwords_out,
bool* bit_masked) {
// The _OSC spec in 6.2.11 requires at least 2 dwords, though some specific invocations such
// as PCIe require 3+.
if (!dwords_in || !dwords_out || dword_cnt < 2) {
return ZX_ERR_INVALID_ARGS;
}
uint8_t uuid[16] = {};
if (uuid_str_to_uint8_buf(uuid_str, uuid) != ZX_OK) {
return ZX_ERR_INVALID_ARGS;
}
ACPI_OBJECT objs[4] = {};
uint32_t dword_length = static_cast<uint32_t>(dword_cnt * sizeof(uint32_t));
// UUID
objs[0].Buffer.Type = ACPI_TYPE_BUFFER;
objs[0].Buffer.Length = ACPI_UUID_SIZE;
objs[0].Buffer.Pointer = (uint8_t*)uuid;
// revision id
objs[1].Integer.Type = ACPI_TYPE_INTEGER;
objs[1].Integer.Value = revision;
// number of dwords in the next arg
objs[2].Integer.Type = ACPI_TYPE_INTEGER;
objs[2].Integer.Value = dword_cnt;
// buffer containing dwords
objs[3].Buffer.Type = ACPI_TYPE_BUFFER;
objs[3].Buffer.Length = dword_length;
objs[3].Buffer.Pointer = (uint8_t*)dwords_in;
ACPI_OBJECT_LIST params = {};
params.Count = countof(objs);
params.Pointer = objs;
// Have ACPI allocate the return buffer for us.
ACPI_BUFFER out = {};
out.Length = ACPI_ALLOCATE_BUFFER;
out.Pointer = NULL;
// Make the call and ensure that both the rpc itself and the status bits returned
// in the first dword all indicate success.
ACPI_STATUS acpi_status = AcpiEvaluateObject(dev_obj, const_cast<char*>("_OSC"), &params, &out);
if (acpi_status != AE_OK) {
printf("error making _OSC call: %d!\n", acpi_status);
return acpi_to_zx_status(acpi_status);
}
// Ensure we free ACPI's memory allocation for the _OSC call.
auto acpi_object_free = fbl::MakeAutoCall([&]() { AcpiOsFree(out.Pointer); });
ACPI_OBJECT* out_obj = static_cast<ACPI_OBJECT*>(out.Pointer);
if (out_obj->Buffer.Length > dword_length) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
memcpy(dwords_out, out_obj->Buffer.Pointer, out_obj->Buffer.Length);
// Inform the caller if a bit was masked off in negotiation of capabilities
*bit_masked = dwords_out[0] & OSC_RET_MASKED;
return osc_bad_result(dwords_out[0]) ? ZX_ERR_INTERNAL : ZX_OK;
}
// Call the ACPI _PSV method to query the temperature OSPM will trigger
// a cooling policy.
zx_status_t acpi_psv_call(ACPI_HANDLE dev_obj, uint64_t* out) {
ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_PSV", out);
return acpi_to_zx_status(status);
}
// Call the ACPI _SEG method to query a PCI Host Bridge's segment group
zx_status_t acpi_seg_call(ACPI_HANDLE dev_obj, uint16_t* out_seg) {
uint64_t out;
ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_SEG", &out);
// Lower 8 bits of _SEG returned integer is the PCI segment group.
*out_seg = static_cast<uint8_t>(out & 0xFF);
return acpi_to_zx_status(status);
}
// Call the ACPI _TMP method to query the temperaure of a thermal zone.
zx_status_t acpi_tmp_call(ACPI_HANDLE dev_obj, uint64_t* out) {
ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_TMP", out);
return acpi_to_zx_status(status);
}