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