forked from xinyu391/zircon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmethods.cpp
162 lines (137 loc) · 5.72 KB
/
methods.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// 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 <acpica/acpi.h>
#include <acpica/acuuid.h>
#include <fbl/auto_call.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <zircon/types.h>
#include "errors.h"
#include "methods.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;
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"), ¶ms, &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, uint8_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);
}