1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 #include <acpica/acpi.h>
6 #include <acpica/acuuid.h>
7 #include <fbl/auto_call.h>
8 #include <inttypes.h>
9 #include <stdint.h>
10 #include <string.h>
11 #include <zircon/types.h>
12
13 #include "errors.h"
14 #include "methods.h"
15 #include "util.h"
16
uuid_str_to_uint8_buf(const char * uuid_str,uint8_t * uuid)17 static zx_status_t uuid_str_to_uint8_buf(const char* uuid_str, uint8_t* uuid) {
18 if (strlen(uuid_str) != 36) {
19 return ZX_ERR_WRONG_TYPE;
20 }
21
22 // Converts the format string aabbccdd-eeff-gghh-iijj-kkllmmnnoopp to
23 // { dd, cc, bb, aa, ff, ee, hh, gg, ii, jj, kk, ll, mm, nn, oo, pp }
24 // per ACPI Spec 6.1, 19.6.136
25 int ret = sscanf(uuid_str,
26 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8
27 "-%02" SCNx8 "%02" SCNx8
28 "-%02" SCNx8 "%02" SCNx8
29 "-%02" SCNx8 "%02" SCNx8
30 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8,
31 &uuid[3], &uuid[2], &uuid[1], &uuid[0],
32 &uuid[5], &uuid[4],
33 &uuid[7], &uuid[6],
34 &uuid[8], &uuid[9],
35 &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], &uuid[15]);
36 if (ret != 16) {
37 return ZX_ERR_INTERNAL;
38 }
39
40 return ZX_OK;
41 }
42
43
44 // Call the ACPI _BNN method to query a PCI Host Bridge's base bus number.
acpi_bbn_call(ACPI_HANDLE dev_obj,uint8_t * out_bbn)45 zx_status_t acpi_bbn_call(ACPI_HANDLE dev_obj, uint8_t* out_bbn) {
46 uint64_t tmp;
47 ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_BBN", &tmp);
48 // BBN is returned in the lower 8 bits
49 *out_bbn = tmp & 0xFF;
50 return acpi_to_zx_status(status);
51 }
52
53 // Call the ACPI _CRT method to query critical shutdown temperature.
acpi_crt_call(ACPI_HANDLE dev_obj,uint64_t * out)54 zx_status_t acpi_crt_call(ACPI_HANDLE dev_obj, uint64_t* out) {
55 ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_CRT", out);
56 return acpi_to_zx_status(status);
57 }
58
59 enum {
60 OSC_RET_FAILURE = (1u << 1),
61 OSC_RET_BAD_UUID = (1u << 2),
62 OSC_RET_BAD_REV = (1u << 3),
63 OSC_RET_MASKED = (1u << 4),
64 };
65
66 // Check for the 3 bits that indicate a failure in calling _OSC
osc_bad_result(uint32_t val)67 static constexpr bool osc_bad_result(uint32_t val) {
68 return (val & (OSC_RET_FAILURE | OSC_RET_BAD_UUID | OSC_RET_BAD_REV));
69 }
70
71 // Call the ACPI _OSC method to query and negotiate OS capabilities.
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)72 zx_status_t acpi_osc_call(ACPI_HANDLE dev_obj,
73 const char* uuid_str,
74 uint64_t revision,
75 size_t dword_cnt,
76 uint32_t* dwords_in,
77 uint32_t* dwords_out,
78 bool* bit_masked) {
79 // The _OSC spec in 6.2.11 requires at least 2 dwords, though some specific invocations such
80 // as PCIe require 3+.
81 if (!dwords_in || !dwords_out || dword_cnt < 2) {
82 return ZX_ERR_INVALID_ARGS;
83 }
84
85 uint8_t uuid[16] = {};
86 if (uuid_str_to_uint8_buf(uuid_str, uuid) != ZX_OK) {
87 return ZX_ERR_INVALID_ARGS;
88 }
89
90 ACPI_OBJECT objs[4] = {};
91 uint32_t dword_length = static_cast<uint32_t>(dword_cnt * sizeof(uint32_t));
92
93 // UUID
94 objs[0].Buffer.Type = ACPI_TYPE_BUFFER;
95 objs[0].Buffer.Length = ACPI_UUID_SIZE;
96 objs[0].Buffer.Pointer = (uint8_t*)uuid;
97
98 // revision id
99 objs[1].Integer.Type = ACPI_TYPE_INTEGER;
100 objs[1].Integer.Value = revision;
101
102 // number of dwords in the next arg
103 objs[2].Integer.Type = ACPI_TYPE_INTEGER;
104 objs[2].Integer.Value = dword_cnt;
105
106 // buffer containing dwords
107 objs[3].Buffer.Type = ACPI_TYPE_BUFFER;
108 objs[3].Buffer.Length = dword_length;
109 objs[3].Buffer.Pointer = (uint8_t*)dwords_in;
110
111 ACPI_OBJECT_LIST params = {};
112 params.Count = countof(objs);
113 params.Pointer = objs;
114
115 // Have ACPI allocate the return buffer for us.
116 ACPI_BUFFER out = {};
117 out.Length = ACPI_ALLOCATE_BUFFER;
118 out.Pointer = NULL;
119
120 // Make the call and ensure that both the rpc itself and the status bits returned
121 // in the first dword all indicate success.
122 ACPI_STATUS acpi_status = AcpiEvaluateObject(dev_obj, const_cast<char*>("_OSC"), ¶ms, &out);
123 if (acpi_status != AE_OK) {
124 printf("error making _OSC call: %d!\n", acpi_status);
125 return acpi_to_zx_status(acpi_status);
126 }
127
128 // Ensure we free ACPI's memory allocation for the _OSC call.
129 auto acpi_object_free = fbl::MakeAutoCall([&]() { AcpiOsFree(out.Pointer); });
130 ACPI_OBJECT* out_obj = static_cast<ACPI_OBJECT*>(out.Pointer);
131 if (out_obj->Buffer.Length > dword_length) {
132 return ZX_ERR_BUFFER_TOO_SMALL;
133 }
134
135 memcpy(dwords_out, out_obj->Buffer.Pointer, out_obj->Buffer.Length);
136 // Inform the caller if a bit was masked off in negotiation of capabilities
137 *bit_masked = dwords_out[0] & OSC_RET_MASKED;
138
139 return osc_bad_result(dwords_out[0]) ? ZX_ERR_INTERNAL : ZX_OK;
140 }
141
142 // Call the ACPI _PSV method to query the temperature OSPM will trigger
143 // a cooling policy.
acpi_psv_call(ACPI_HANDLE dev_obj,uint64_t * out)144 zx_status_t acpi_psv_call(ACPI_HANDLE dev_obj, uint64_t* out) {
145 ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_PSV", out);
146 return acpi_to_zx_status(status);
147 }
148
149 // Call the ACPI _SEG method to query a PCI Host Bridge's segment group
acpi_seg_call(ACPI_HANDLE dev_obj,uint16_t * out_seg)150 zx_status_t acpi_seg_call(ACPI_HANDLE dev_obj, uint16_t* out_seg) {
151 uint64_t out;
152 ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_SEG", &out);
153 // Lower 8 bits of _SEG returned integer is the PCI segment group.
154 *out_seg = static_cast<uint8_t>(out & 0xFF);
155 return acpi_to_zx_status(status);
156 }
157
158 // Call the ACPI _TMP method to query the temperaure of a thermal zone.
acpi_tmp_call(ACPI_HANDLE dev_obj,uint64_t * out)159 zx_status_t acpi_tmp_call(ACPI_HANDLE dev_obj, uint64_t* out) {
160 ACPI_STATUS status = acpi_evaluate_integer(dev_obj, "_TMP", out);
161 return acpi_to_zx_status(status);
162 }
163