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"), &params, &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