1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdbool.h>
11
12 #include "vmmapi.h"
13 #include "acpi.h"
14
get_vcpu_pm_info(struct vmctx * ctx,int vcpu_id,uint64_t pm_type,uint64_t * pm_info)15 static inline int get_vcpu_pm_info(struct vmctx *ctx, int vcpu_id,
16 uint64_t pm_type, uint64_t *pm_info)
17 {
18 *pm_info = ((ctx->vmid << PMCMD_VMID_SHIFT) & PMCMD_VMID_MASK)
19 | ((vcpu_id << PMCMD_VCPUID_SHIFT) & PMCMD_VCPUID_MASK)
20 | (pm_type & PMCMD_TYPE_MASK);
21
22 return vm_get_cpu_state(ctx, pm_info);
23 }
24
get_vcpu_px_cnt(struct vmctx * ctx,int vcpu_id,uint8_t * px_cnt)25 static inline int get_vcpu_px_cnt(struct vmctx *ctx, int vcpu_id, uint8_t *px_cnt)
26 {
27 uint64_t px_cnt_u64;
28 int ret;
29
30 ret = get_vcpu_pm_info(ctx, vcpu_id, ACRN_PMCMD_GET_PX_CNT, &px_cnt_u64);
31 *px_cnt = (uint8_t)px_cnt_u64;
32
33 return ret;
34 }
35
get_vcpu_cx_cnt(struct vmctx * ctx,int vcpu_id)36 uint8_t get_vcpu_cx_cnt(struct vmctx *ctx, int vcpu_id)
37 {
38 uint64_t cx_cnt;
39
40 if (get_vcpu_pm_info(ctx, vcpu_id, ACRN_PMCMD_GET_CX_CNT, &cx_cnt)) {
41 return 0;
42 }
43
44 return (uint8_t)cx_cnt;
45 }
46
get_vcpu_px_data(struct vmctx * ctx,int vcpu_id,int px_num,struct acrn_pstate_data * vcpu_px_data)47 static int get_vcpu_px_data(struct vmctx *ctx, int vcpu_id,
48 int px_num, struct acrn_pstate_data *vcpu_px_data)
49 {
50 uint64_t *pm_ioctl_buf;
51 enum acrn_pm_cmd_type cmd_type = ACRN_PMCMD_GET_PX_DATA;
52
53 pm_ioctl_buf = malloc(sizeof(struct acrn_pstate_data));
54 if (!pm_ioctl_buf) {
55 return -1;
56 }
57
58 *pm_ioctl_buf = ((ctx->vmid << PMCMD_VMID_SHIFT) & PMCMD_VMID_MASK)
59 | ((vcpu_id << PMCMD_VCPUID_SHIFT) & PMCMD_VCPUID_MASK)
60 | ((px_num << PMCMD_STATE_NUM_SHIFT) & PMCMD_STATE_NUM_MASK)
61 | cmd_type;
62
63 /* get and validate px data */
64 if (vm_get_cpu_state(ctx, pm_ioctl_buf)) {
65 free(pm_ioctl_buf);
66 return -1;
67 }
68
69 memcpy(vcpu_px_data, pm_ioctl_buf,
70 sizeof(struct acrn_pstate_data));
71
72 free(pm_ioctl_buf);
73 return 0;
74 }
75
get_vcpu_cx_data(struct vmctx * ctx,int vcpu_id,int cx_num,struct acrn_cstate_data * vcpu_cx_data)76 int get_vcpu_cx_data(struct vmctx *ctx, int vcpu_id,
77 int cx_num, struct acrn_cstate_data *vcpu_cx_data)
78 {
79 uint64_t *pm_ioctl_buf;
80 enum acrn_pm_cmd_type cmd_type = ACRN_PMCMD_GET_CX_DATA;
81
82 pm_ioctl_buf = malloc(sizeof(struct acrn_cstate_data));
83 if (!pm_ioctl_buf) {
84 return -1;
85 }
86
87 *pm_ioctl_buf = ((ctx->vmid << PMCMD_VMID_SHIFT) & PMCMD_VMID_MASK)
88 | ((vcpu_id << PMCMD_VCPUID_SHIFT) & PMCMD_VCPUID_MASK)
89 | ((cx_num << PMCMD_STATE_NUM_SHIFT) & PMCMD_STATE_NUM_MASK)
90 | cmd_type;
91
92 /* get and validate cx data */
93 if (vm_get_cpu_state(ctx, pm_ioctl_buf)) {
94 free(pm_ioctl_buf);
95 return -1;
96 }
97
98 memcpy(vcpu_cx_data, pm_ioctl_buf,
99 sizeof(struct acrn_cstate_data));
100
101 free(pm_ioctl_buf);
102 return 0;
103 }
104
105 char *_asi_table[7] = { "SystemMemory",
106 "SystemIO",
107 "PCI_Config",
108 "EmbeddedControl",
109 "SMBus",
110 "PCC",
111 "FFixedHW"};
112
get_asi_string(uint8_t space_id)113 static char *get_asi_string(uint8_t space_id)
114 {
115 switch (space_id) {
116 case SPACE_SYSTEM_MEMORY:
117 return _asi_table[0];
118
119 case SPACE_SYSTEM_IO:
120 return _asi_table[1];
121
122 case SPACE_PCI_CONFIG:
123 return _asi_table[2];
124
125 case SPACE_Embedded_Control:
126 return _asi_table[3];
127
128 case SPACE_SMBUS:
129 return _asi_table[4];
130
131 case SPACE_PLATFORM_COMM:
132 return _asi_table[5];
133
134 case SPACE_FFixedHW:
135 return _asi_table[6];
136
137 default:
138 return NULL;
139 }
140 }
141
142 /* _CST: C-States
143 */
dsdt_write_cst(struct vmctx * ctx,int vcpu_id)144 void dsdt_write_cst(struct vmctx *ctx, int vcpu_id)
145 {
146 int i;
147 uint8_t vcpu_cx_cnt;
148 char *cx_asi;
149 struct acrn_acpi_generic_address cx_reg;
150 struct acrn_cstate_data *vcpu_cx_data;
151
152 vcpu_cx_cnt = get_vcpu_cx_cnt(ctx, vcpu_id);
153 if (!vcpu_cx_cnt) {
154 return;
155 }
156
157 /* vcpu_cx_data start from C1, cx_cnt is total Cx entry num. */
158 vcpu_cx_data = malloc(vcpu_cx_cnt * sizeof(struct acrn_cstate_data));
159 if (!vcpu_cx_data) {
160 return;
161 }
162
163 /* copy and validate cx data first */
164 for (i = 1; i <= vcpu_cx_cnt; i++) {
165 if (get_vcpu_cx_data(ctx, vcpu_id, i, vcpu_cx_data + i - 1)) {
166 /* something must be wrong, so skip the write. */
167 free(vcpu_cx_data);
168 return;
169 }
170 }
171
172 dsdt_line("");
173 dsdt_line(" Method (_CST, 0, NotSerialized)");
174 dsdt_line(" {");
175 dsdt_line(" Return (Package (0x%02X)", vcpu_cx_cnt + 1);
176 dsdt_line(" {");
177
178 dsdt_line(" 0x%02X,", vcpu_cx_cnt);
179
180 for (i = 0; i < vcpu_cx_cnt; i++) {
181
182 dsdt_line(" Package (0x04)");
183 dsdt_line(" {");
184
185 cx_reg = (vcpu_cx_data + i)->cx_reg;
186 cx_asi = get_asi_string(cx_reg.space_id);
187
188 dsdt_line(" ResourceTemplate ()");
189 dsdt_line(" {");
190 dsdt_line(" Register (%s,", cx_asi);
191 dsdt_line(" 0x%02x,", cx_reg.bit_width);
192 dsdt_line(" 0x%02x,", cx_reg.bit_offset);
193 dsdt_line(" 0x%016lx,", cx_reg.address);
194 dsdt_line(" 0x%02x,", cx_reg.access_size);
195 dsdt_line(" )");
196 dsdt_line(" },");
197
198 dsdt_line(" 0x%04X,", (vcpu_cx_data + i)->type);
199 dsdt_line(" 0x%04X,", (vcpu_cx_data + i)->latency);
200 dsdt_line(" 0x%04X", (vcpu_cx_data + i)->power);
201
202 if (i == (vcpu_cx_cnt - 1)) {
203 dsdt_line(" }");
204 } else {
205 dsdt_line(" },");
206 }
207
208 }
209
210 dsdt_line(" })");
211 dsdt_line(" }");
212
213 free(vcpu_cx_data);
214 }
215
216 /* _PPC: Performance Present Capabilities
217 * hard code _PPC to 0, all states are available.
218 */
dsdt_write_ppc(void)219 static void dsdt_write_ppc(void)
220 {
221 dsdt_line(" Name (_PPC, Zero)");
222 }
223
224 /* _PCT: Performance Control
225 * Both Performance Control and Status Register are set to FFixedHW
226 */
dsdt_write_pct(void)227 static void dsdt_write_pct(void)
228 {
229 dsdt_line(" Method (_PCT, 0, NotSerialized)");
230 dsdt_line(" {");
231 dsdt_line(" Return (Package (0x02)");
232 dsdt_line(" {");
233 dsdt_line(" ResourceTemplate ()");
234 dsdt_line(" {");
235 dsdt_line(" Register (FFixedHW,");
236 dsdt_line(" 0x00,");
237 dsdt_line(" 0x00,");
238 dsdt_line(" 0x0000000000000000,");
239 dsdt_line(" ,)");
240 dsdt_line(" },");
241 dsdt_line("");
242 dsdt_line(" ResourceTemplate ()");
243 dsdt_line(" {");
244 dsdt_line(" Register (FFixedHW,");
245 dsdt_line(" 0x00,");
246 dsdt_line(" 0x00,");
247 dsdt_line(" 0x0000000000000000,");
248 dsdt_line(" ,)");
249 dsdt_line(" }");
250 dsdt_line(" })");
251 dsdt_line(" }");
252
253 }
254
255 /* _PSS: Performance Supported States
256 */
dsdt_write_pss(struct vmctx * ctx,int vcpu_id)257 static int dsdt_write_pss(struct vmctx *ctx, int vcpu_id)
258 {
259 uint8_t vcpu_px_cnt;
260 int i;
261 struct acrn_pstate_data *vcpu_px_data;
262 int ret;
263
264 ret = get_vcpu_px_cnt(ctx, vcpu_id, &vcpu_px_cnt);
265 /* vcpu_px_cnt = 0 Indicates vcpu supports continuous pstate.
266 * Then we should write _CPC instate of _PSS
267 */
268 if (ret || !vcpu_px_cnt) {
269 return -1;
270 }
271
272 vcpu_px_data = malloc(vcpu_px_cnt * sizeof(struct acrn_pstate_data));
273 if (!vcpu_px_data) {
274 return -1;
275 }
276
277 /* copy and validate px data first */
278 for (i = 0; i < vcpu_px_cnt; i++) {
279 if (get_vcpu_px_data(ctx, vcpu_id, i, vcpu_px_data + i)) {
280 /* something must be wrong, so skip the write. */
281 free(vcpu_px_data);
282 return -1;
283 }
284 }
285
286 dsdt_line("");
287 dsdt_line(" Method (_PSS, 0, NotSerialized)");
288 dsdt_line(" {");
289 dsdt_line(" Return (Package (0x%02X)", vcpu_px_cnt);
290 dsdt_line(" {");
291
292 for (i = 0; i < vcpu_px_cnt; i++) {
293
294 dsdt_line(" Package (0x%02X)", 6);
295 dsdt_line(" {");
296 dsdt_line(" 0x%08X,",
297 (vcpu_px_data + i)->core_frequency);
298 dsdt_line(" 0x%08X,",
299 (vcpu_px_data + i)->power);
300 dsdt_line(" 0x%08X,",
301 (vcpu_px_data + i)->transition_latency);
302 dsdt_line(" 0x%08X,",
303 (vcpu_px_data + i)->bus_master_latency);
304 dsdt_line(" 0x%08X,",
305 (vcpu_px_data + i)->control);
306 dsdt_line(" 0x%08X",
307 (vcpu_px_data + i)->status);
308
309 if (i == (vcpu_px_cnt - 1)) {
310 dsdt_line(" }");
311 } else {
312 dsdt_line(" },");
313 }
314 }
315 dsdt_line(" })");
316 dsdt_line(" }");
317
318 free(vcpu_px_data);
319
320 return 0;
321 }
322
323 /* _CPC: Continuous Performance Control
324 * Hard code a V3 CPC table, describing HWP register interface.
325 */
dsdt_write_cpc(void)326 static void dsdt_write_cpc(void)
327 {
328 dsdt_line("");
329 dsdt_line(" Method (_CPC, 0, NotSerialized)");
330 dsdt_line(" {");
331 dsdt_line(" Return (Package (0x17)");
332 dsdt_line(" {");
333 dsdt_line(" 0x17,");
334 dsdt_line(" 0x03,");
335 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x00, 0x0000000000000771, 0x04, )},");
336 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x08, 0x00000000000000CE, 0x04, )},");
337 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x10, 0x0000000000000771, 0x04, )},");
338 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x18, 0x0000000000000771, 0x04, )},");
339 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x08, 0x0000000000000771, 0x04, )},");
340 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x10, 0x0000000000000774, 0x04, )},");
341 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x00, 0x0000000000000774, 0x04, )},");
342 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x08, 0x0000000000000774, 0x04, )},");
343 dsdt_line(" ResourceTemplate() {Register(SystemMemory, 0x00, 0x00, 0x0000000000000000, , )},");
344 dsdt_line(" ResourceTemplate() {Register(SystemMemory, 0x00, 0x00, 0x0000000000000000, , )},");
345 dsdt_line(" ResourceTemplate() {Register(SystemMemory, 0x00, 0x00, 0x0000000000000000, , )},");
346 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x40, 0x00, 0x00000000000000E7, 0x04, )},");
347 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x40, 0x00, 0x00000000000000E8, 0x04, )},");
348 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x02, 0x01, 0x0000000000000777, 0x04, )},");
349 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x01, 0x00, 0x0000000000000770, 0x04, )},");
350 dsdt_line(" One,");
351 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x0A, 0x20, 0x0000000000000774, 0x04, )},");
352 dsdt_line(" ResourceTemplate() {Register(FFixedHW, 0x08, 0x18, 0x0000000000000774, 0x04, )},");
353 dsdt_line(" Zero,");
354 dsdt_line(" Zero,");
355 dsdt_line(" Zero");
356 dsdt_line(" })");
357 dsdt_line(" }");
358 }
359
pm_write_dsdt(struct vmctx * ctx,int ncpu)360 void pm_write_dsdt(struct vmctx *ctx, int ncpu)
361 {
362 int i;
363 int ret;
364 bool is_cpc = false;
365 uint8_t px_cnt;
366
367 /* Scope (_PR) */
368 dsdt_line("");
369 dsdt_line(" Scope (_SB)");
370 dsdt_line(" {");
371 for (i = 0; i < ncpu; i++) {
372 dsdt_line(" Device (PR%02d)", i);
373 dsdt_line(" {");
374 dsdt_line(" Name (_HID, \"ACPI0007\")");
375 dsdt_line(" Name (_UID, 0x%02X)", i);
376 dsdt_line(" }");
377 }
378 dsdt_line(" }");
379 dsdt_line("");
380
381 /* Scope (_PR.CPU(N)) */
382 for (i = 0; i < ncpu; i++) {
383 dsdt_line(" Scope (_SB.PR%02d)", i);
384 dsdt_line(" {");
385 dsdt_line("");
386
387 ret = dsdt_write_pss(ctx, i);
388 dsdt_write_cst(ctx, i);
389
390 /* if the vm can support px, hv will return the right px/cx cnt.
391 then hard code _PPC and _PCT for all vpu */
392 if (ret == 0) {
393 if (i == 0) {
394 dsdt_write_ppc();
395 dsdt_write_pct();
396 } else {
397 dsdt_line(" Method (_PPC, 0, NotSerialized)");
398 dsdt_line(" {");
399 dsdt_line(" Return (^^PR00._PPC)");
400 dsdt_line(" }");
401 dsdt_line("");
402 dsdt_line(" Method (_PCT, 0, NotSerialized)");
403 dsdt_line(" {");
404 dsdt_line(" Return (^^PR00._PCT)");
405 dsdt_line(" }");
406 dsdt_line("");
407 }
408 }
409
410 ret = get_vcpu_px_cnt(ctx, i, &px_cnt);
411 if (ret == 0 && px_cnt == 0) {
412 /* px_cnt = 0 Indicates vcpu supports continuous pstate.
413 * Then we can write _CPC
414 */
415 is_cpc = true;
416 }
417
418 if (is_cpc) {
419 if (i == 0) {
420 dsdt_write_cpc();
421 } else {
422 dsdt_line(" Method (_CPC, 0, NotSerialized)");
423 dsdt_line(" {");
424 dsdt_line(" Return (^^PR00._CPC)");
425 dsdt_line(" }");
426 dsdt_line("");
427 }
428 }
429
430 dsdt_line(" }");
431 }
432 }
433
434 /* _OSC: Operating System Capabilities
435 * Currently only support CPPC v2 capability.
436 * CPPC v2 capability: revision 2 of the _CPC object.
437 * If all vcpus don't support _CPC object, no need to add _OSC in DSDT.
438 */
osc_write_ospm_dsdt(struct vmctx * ctx,int ncpu)439 void osc_write_ospm_dsdt(struct vmctx *ctx, int ncpu)
440 {
441 int ret;
442 bool support_cpc = false;
443 uint8_t px_cnt;
444
445 /* check px_cnt on vBSP */
446 ret = get_vcpu_px_cnt(ctx, 0, &px_cnt);
447 if (ret == 0 && px_cnt == 0) {
448 /* px_cnt = 0 Indicates vcpu supports continuous pstate.
449 */
450 support_cpc = true;
451 }
452 if (support_cpc) {
453 /* Scope (_SB._OSC) */
454 dsdt_line("");
455 dsdt_line(" Scope (_SB)");
456 dsdt_line(" {");
457 dsdt_line(" Method (_OSC, 4, NotSerialized) // _OSC: Operating System Capabilities");
458 dsdt_line(" {");
459 dsdt_line(" CreateDWordField (Arg3, 0x00, STS0)");
460 dsdt_line(" CreateDWordField (Arg3, 0x04, CAP0)");
461 dsdt_line(" If ((Arg0 == ToUUID (\"0811b06e-4a27-44f9-8d60-3cbbc22e7b48\") /* Platform-wide OSPM Capabilities */))");
462 dsdt_line(" {");
463 dsdt_line(" If ((Arg1 == One))");
464 dsdt_line(" {");
465 dsdt_line(" If ((CAP0 & 0x40))");
466 dsdt_line(" {");
467 dsdt_line(" CAP0 &= 0x00000040");
468 dsdt_line(" }");
469 dsdt_line(" Else");
470 dsdt_line(" {");
471 dsdt_line(" STS0 &= 0xFFFFFF00");
472 dsdt_line(" STS0 |= 0x02");
473 dsdt_line(" }");
474 dsdt_line(" }");
475 dsdt_line(" Else");
476 dsdt_line(" {");
477 dsdt_line(" STS0 &= 0xFFFFFF00");
478 dsdt_line(" STS0 |= 0x0A");
479 dsdt_line(" }");
480 dsdt_line(" }");
481 dsdt_line(" Else");
482 dsdt_line(" {");
483 dsdt_line(" STS0 &= 0xFFFFFF00");
484 dsdt_line(" STS0 |= 0x06");
485 dsdt_line(" }");
486 dsdt_line(" Return (Arg3)");
487 dsdt_line(" }");
488 dsdt_line(" }");
489 dsdt_line("");
490 }
491 }
492