1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Module Name: utosi - Support for the _OSI predefined control method |
5 | * |
6 | * Copyright (C) 2000 - 2023, Intel Corp. |
7 | * |
8 | *****************************************************************************/ |
9 | |
10 | #include <acpi/acpi.h> |
11 | #include "accommon.h" |
12 | |
13 | #define _COMPONENT ACPI_UTILITIES |
14 | ACPI_MODULE_NAME("utosi" ) |
15 | |
16 | /****************************************************************************** |
17 | * |
18 | * ACPICA policy for new _OSI strings: |
19 | * |
20 | * It is the stated policy of ACPICA that new _OSI strings will be integrated |
21 | * into this module as soon as possible after they are defined. It is strongly |
22 | * recommended that all ACPICA hosts mirror this policy and integrate any |
23 | * changes to this module as soon as possible. There are several historical |
24 | * reasons behind this policy: |
25 | * |
26 | * 1) New BIOSs tend to test only the case where the host responds TRUE to |
27 | * the latest version of Windows, which would respond to the latest/newest |
28 | * _OSI string. Not responding TRUE to the latest version of Windows will |
29 | * risk executing untested code paths throughout the DSDT and SSDTs. |
30 | * |
31 | * 2) If a new _OSI string is recognized only after a significant delay, this |
32 | * has the potential to cause problems on existing working machines because |
33 | * of the possibility that a new and different path through the ASL code |
34 | * will be executed. |
35 | * |
36 | * 3) New _OSI strings are tending to come out about once per year. A delay |
37 | * in recognizing a new string for a significant amount of time risks the |
38 | * release of another string which only compounds the initial problem. |
39 | * |
40 | *****************************************************************************/ |
41 | /* |
42 | * Strings supported by the _OSI predefined control method (which is |
43 | * implemented internally within this module.) |
44 | * |
45 | * March 2009: Removed "Linux" as this host no longer wants to respond true |
46 | * for this string. Basically, the only safe OS strings are windows-related |
47 | * and in many or most cases represent the only test path within the |
48 | * BIOS-provided ASL code. |
49 | * |
50 | * The last element of each entry is used to track the newest version of |
51 | * Windows that the BIOS has requested. |
52 | */ |
53 | static struct acpi_interface_info acpi_default_supported_interfaces[] = { |
54 | /* Operating System Vendor Strings */ |
55 | |
56 | {"Windows 2000" , NULL, 0, ACPI_OSI_WIN_2000}, /* Windows 2000 */ |
57 | {"Windows 2001" , NULL, 0, ACPI_OSI_WIN_XP}, /* Windows XP */ |
58 | {"Windows 2001 SP1" , NULL, 0, ACPI_OSI_WIN_XP_SP1}, /* Windows XP SP1 */ |
59 | {"Windows 2001.1" , NULL, 0, ACPI_OSI_WINSRV_2003}, /* Windows Server 2003 */ |
60 | {"Windows 2001 SP2" , NULL, 0, ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */ |
61 | {"Windows 2001.1 SP1" , NULL, 0, ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */ |
62 | {"Windows 2006" , NULL, 0, ACPI_OSI_WIN_VISTA}, /* Windows vista - Added 03/2006 */ |
63 | {"Windows 2006.1" , NULL, 0, ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */ |
64 | {"Windows 2006 SP1" , NULL, 0, ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */ |
65 | {"Windows 2006 SP2" , NULL, 0, ACPI_OSI_WIN_VISTA_SP2}, /* Windows Vista SP2 - Added 09/2010 */ |
66 | {"Windows 2009" , NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */ |
67 | {"Windows 2012" , NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8 and Server 2012 - Added 08/2012 */ |
68 | {"Windows 2013" , NULL, 0, ACPI_OSI_WIN_8_1}, /* Windows 8.1 and Server 2012 R2 - Added 01/2014 */ |
69 | {"Windows 2015" , NULL, 0, ACPI_OSI_WIN_10}, /* Windows 10 - Added 03/2015 */ |
70 | {"Windows 2016" , NULL, 0, ACPI_OSI_WIN_10_RS1}, /* Windows 10 version 1607 - Added 12/2017 */ |
71 | {"Windows 2017" , NULL, 0, ACPI_OSI_WIN_10_RS2}, /* Windows 10 version 1703 - Added 12/2017 */ |
72 | {"Windows 2017.2" , NULL, 0, ACPI_OSI_WIN_10_RS3}, /* Windows 10 version 1709 - Added 02/2018 */ |
73 | {"Windows 2018" , NULL, 0, ACPI_OSI_WIN_10_RS4}, /* Windows 10 version 1803 - Added 11/2018 */ |
74 | {"Windows 2018.2" , NULL, 0, ACPI_OSI_WIN_10_RS5}, /* Windows 10 version 1809 - Added 11/2018 */ |
75 | {"Windows 2019" , NULL, 0, ACPI_OSI_WIN_10_19H1}, /* Windows 10 version 1903 - Added 08/2019 */ |
76 | {"Windows 2020" , NULL, 0, ACPI_OSI_WIN_10_20H1}, /* Windows 10 version 2004 - Added 08/2021 */ |
77 | {"Windows 2021" , NULL, 0, ACPI_OSI_WIN_11}, /* Windows 11 - Added 01/2022 */ |
78 | |
79 | /* Feature Group Strings */ |
80 | |
81 | {"Extended Address Space Descriptor" , NULL, ACPI_OSI_FEATURE, 0}, |
82 | |
83 | /* |
84 | * All "optional" feature group strings (features that are implemented |
85 | * by the host) should be dynamically modified to VALID by the host via |
86 | * acpi_install_interface or acpi_update_interfaces. Such optional feature |
87 | * group strings are set as INVALID by default here. |
88 | */ |
89 | |
90 | {"Module Device" , NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, |
91 | {"Processor Device" , NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, |
92 | {"3.0 Thermal Model" , NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, |
93 | {"3.0 _SCP Extensions" , NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, |
94 | {"Processor Aggregator Device" , NULL, ACPI_OSI_OPTIONAL_FEATURE, 0} |
95 | }; |
96 | |
97 | /******************************************************************************* |
98 | * |
99 | * FUNCTION: acpi_ut_initialize_interfaces |
100 | * |
101 | * PARAMETERS: None |
102 | * |
103 | * RETURN: Status |
104 | * |
105 | * DESCRIPTION: Initialize the global _OSI supported interfaces list |
106 | * |
107 | ******************************************************************************/ |
108 | |
109 | acpi_status acpi_ut_initialize_interfaces(void) |
110 | { |
111 | acpi_status status; |
112 | u32 i; |
113 | |
114 | status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); |
115 | if (ACPI_FAILURE(status)) { |
116 | return (status); |
117 | } |
118 | |
119 | acpi_gbl_supported_interfaces = acpi_default_supported_interfaces; |
120 | |
121 | /* Link the static list of supported interfaces */ |
122 | |
123 | for (i = 0; |
124 | i < (ACPI_ARRAY_LENGTH(acpi_default_supported_interfaces) - 1); |
125 | i++) { |
126 | acpi_default_supported_interfaces[i].next = |
127 | &acpi_default_supported_interfaces[(acpi_size)i + 1]; |
128 | } |
129 | |
130 | acpi_os_release_mutex(acpi_gbl_osi_mutex); |
131 | return (AE_OK); |
132 | } |
133 | |
134 | /******************************************************************************* |
135 | * |
136 | * FUNCTION: acpi_ut_interface_terminate |
137 | * |
138 | * PARAMETERS: None |
139 | * |
140 | * RETURN: Status |
141 | * |
142 | * DESCRIPTION: Delete all interfaces in the global list. Sets |
143 | * acpi_gbl_supported_interfaces to NULL. |
144 | * |
145 | ******************************************************************************/ |
146 | |
147 | acpi_status acpi_ut_interface_terminate(void) |
148 | { |
149 | acpi_status status; |
150 | struct acpi_interface_info *next_interface; |
151 | |
152 | status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); |
153 | if (ACPI_FAILURE(status)) { |
154 | return (status); |
155 | } |
156 | |
157 | next_interface = acpi_gbl_supported_interfaces; |
158 | while (next_interface) { |
159 | acpi_gbl_supported_interfaces = next_interface->next; |
160 | |
161 | if (next_interface->flags & ACPI_OSI_DYNAMIC) { |
162 | |
163 | /* Only interfaces added at runtime can be freed */ |
164 | |
165 | ACPI_FREE(next_interface->name); |
166 | ACPI_FREE(next_interface); |
167 | } else { |
168 | /* Interface is in static list. Reset it to invalid or valid. */ |
169 | |
170 | if (next_interface->flags & ACPI_OSI_DEFAULT_INVALID) { |
171 | next_interface->flags |= ACPI_OSI_INVALID; |
172 | } else { |
173 | next_interface->flags &= ~ACPI_OSI_INVALID; |
174 | } |
175 | } |
176 | |
177 | next_interface = acpi_gbl_supported_interfaces; |
178 | } |
179 | |
180 | acpi_os_release_mutex(acpi_gbl_osi_mutex); |
181 | return (AE_OK); |
182 | } |
183 | |
184 | /******************************************************************************* |
185 | * |
186 | * FUNCTION: acpi_ut_install_interface |
187 | * |
188 | * PARAMETERS: interface_name - The interface to install |
189 | * |
190 | * RETURN: Status |
191 | * |
192 | * DESCRIPTION: Install the interface into the global interface list. |
193 | * Caller MUST hold acpi_gbl_osi_mutex |
194 | * |
195 | ******************************************************************************/ |
196 | |
197 | acpi_status acpi_ut_install_interface(acpi_string interface_name) |
198 | { |
199 | struct acpi_interface_info *interface_info; |
200 | |
201 | /* Allocate info block and space for the name string */ |
202 | |
203 | interface_info = |
204 | ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_interface_info)); |
205 | if (!interface_info) { |
206 | return (AE_NO_MEMORY); |
207 | } |
208 | |
209 | interface_info->name = ACPI_ALLOCATE_ZEROED(strlen(interface_name) + 1); |
210 | if (!interface_info->name) { |
211 | ACPI_FREE(interface_info); |
212 | return (AE_NO_MEMORY); |
213 | } |
214 | |
215 | /* Initialize new info and insert at the head of the global list */ |
216 | |
217 | strcpy(p: interface_info->name, q: interface_name); |
218 | interface_info->flags = ACPI_OSI_DYNAMIC; |
219 | interface_info->next = acpi_gbl_supported_interfaces; |
220 | |
221 | acpi_gbl_supported_interfaces = interface_info; |
222 | return (AE_OK); |
223 | } |
224 | |
225 | /******************************************************************************* |
226 | * |
227 | * FUNCTION: acpi_ut_remove_interface |
228 | * |
229 | * PARAMETERS: interface_name - The interface to remove |
230 | * |
231 | * RETURN: Status |
232 | * |
233 | * DESCRIPTION: Remove the interface from the global interface list. |
234 | * Caller MUST hold acpi_gbl_osi_mutex |
235 | * |
236 | ******************************************************************************/ |
237 | |
238 | acpi_status acpi_ut_remove_interface(acpi_string interface_name) |
239 | { |
240 | struct acpi_interface_info *previous_interface; |
241 | struct acpi_interface_info *next_interface; |
242 | |
243 | previous_interface = next_interface = acpi_gbl_supported_interfaces; |
244 | while (next_interface) { |
245 | if (!strcmp(interface_name, next_interface->name)) { |
246 | /* |
247 | * Found: name is in either the static list |
248 | * or was added at runtime |
249 | */ |
250 | if (next_interface->flags & ACPI_OSI_DYNAMIC) { |
251 | |
252 | /* Interface was added dynamically, remove and free it */ |
253 | |
254 | if (previous_interface == next_interface) { |
255 | acpi_gbl_supported_interfaces = |
256 | next_interface->next; |
257 | } else { |
258 | previous_interface->next = |
259 | next_interface->next; |
260 | } |
261 | |
262 | ACPI_FREE(next_interface->name); |
263 | ACPI_FREE(next_interface); |
264 | } else { |
265 | /* |
266 | * Interface is in static list. If marked invalid, then |
267 | * it does not actually exist. Else, mark it invalid. |
268 | */ |
269 | if (next_interface->flags & ACPI_OSI_INVALID) { |
270 | return (AE_NOT_EXIST); |
271 | } |
272 | |
273 | next_interface->flags |= ACPI_OSI_INVALID; |
274 | } |
275 | |
276 | return (AE_OK); |
277 | } |
278 | |
279 | previous_interface = next_interface; |
280 | next_interface = next_interface->next; |
281 | } |
282 | |
283 | /* Interface was not found */ |
284 | |
285 | return (AE_NOT_EXIST); |
286 | } |
287 | |
288 | /******************************************************************************* |
289 | * |
290 | * FUNCTION: acpi_ut_update_interfaces |
291 | * |
292 | * PARAMETERS: action - Actions to be performed during the |
293 | * update |
294 | * |
295 | * RETURN: Status |
296 | * |
297 | * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor |
298 | * strings or/and feature group strings. |
299 | * Caller MUST hold acpi_gbl_osi_mutex |
300 | * |
301 | ******************************************************************************/ |
302 | |
303 | acpi_status acpi_ut_update_interfaces(u8 action) |
304 | { |
305 | struct acpi_interface_info *next_interface; |
306 | |
307 | next_interface = acpi_gbl_supported_interfaces; |
308 | while (next_interface) { |
309 | if (((next_interface->flags & ACPI_OSI_FEATURE) && |
310 | (action & ACPI_FEATURE_STRINGS)) || |
311 | (!(next_interface->flags & ACPI_OSI_FEATURE) && |
312 | (action & ACPI_VENDOR_STRINGS))) { |
313 | if (action & ACPI_DISABLE_INTERFACES) { |
314 | |
315 | /* Mark the interfaces as invalid */ |
316 | |
317 | next_interface->flags |= ACPI_OSI_INVALID; |
318 | } else { |
319 | /* Mark the interfaces as valid */ |
320 | |
321 | next_interface->flags &= ~ACPI_OSI_INVALID; |
322 | } |
323 | } |
324 | |
325 | next_interface = next_interface->next; |
326 | } |
327 | |
328 | return (AE_OK); |
329 | } |
330 | |
331 | /******************************************************************************* |
332 | * |
333 | * FUNCTION: acpi_ut_get_interface |
334 | * |
335 | * PARAMETERS: interface_name - The interface to find |
336 | * |
337 | * RETURN: struct acpi_interface_info if found. NULL if not found. |
338 | * |
339 | * DESCRIPTION: Search for the specified interface name in the global list. |
340 | * Caller MUST hold acpi_gbl_osi_mutex |
341 | * |
342 | ******************************************************************************/ |
343 | |
344 | struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name) |
345 | { |
346 | struct acpi_interface_info *next_interface; |
347 | |
348 | next_interface = acpi_gbl_supported_interfaces; |
349 | while (next_interface) { |
350 | if (!strcmp(interface_name, next_interface->name)) { |
351 | return (next_interface); |
352 | } |
353 | |
354 | next_interface = next_interface->next; |
355 | } |
356 | |
357 | return (NULL); |
358 | } |
359 | |
360 | /******************************************************************************* |
361 | * |
362 | * FUNCTION: acpi_ut_osi_implementation |
363 | * |
364 | * PARAMETERS: walk_state - Current walk state |
365 | * |
366 | * RETURN: Status |
367 | * Integer: TRUE (0) if input string is matched |
368 | * FALSE (-1) if string is not matched |
369 | * |
370 | * DESCRIPTION: Implementation of the _OSI predefined control method. When |
371 | * an invocation of _OSI is encountered in the system AML, |
372 | * control is transferred to this function. |
373 | * |
374 | * (August 2016) |
375 | * Note: _OSI is now defined to return "Ones" to indicate a match, for |
376 | * compatibility with other ACPI implementations. On a 32-bit DSDT, Ones |
377 | * is 0xFFFFFFFF. On a 64-bit DSDT, Ones is 0xFFFFFFFFFFFFFFFF |
378 | * (ACPI_UINT64_MAX). |
379 | * |
380 | * This function always returns ACPI_UINT64_MAX for TRUE, and later code |
381 | * will truncate this to 32 bits if necessary. |
382 | * |
383 | ******************************************************************************/ |
384 | |
385 | acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) |
386 | { |
387 | union acpi_operand_object *string_desc; |
388 | union acpi_operand_object *return_desc; |
389 | struct acpi_interface_info *interface_info; |
390 | acpi_interface_handler interface_handler; |
391 | acpi_status status; |
392 | u64 return_value; |
393 | |
394 | ACPI_FUNCTION_TRACE(ut_osi_implementation); |
395 | |
396 | /* Validate the string input argument (from the AML caller) */ |
397 | |
398 | string_desc = walk_state->arguments[0].object; |
399 | if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) { |
400 | return_ACPI_STATUS(AE_TYPE); |
401 | } |
402 | |
403 | /* Create a return object */ |
404 | |
405 | return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); |
406 | if (!return_desc) { |
407 | return_ACPI_STATUS(AE_NO_MEMORY); |
408 | } |
409 | |
410 | /* Default return value is 0, NOT SUPPORTED */ |
411 | |
412 | return_value = 0; |
413 | status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); |
414 | if (ACPI_FAILURE(status)) { |
415 | acpi_ut_remove_reference(object: return_desc); |
416 | return_ACPI_STATUS(status); |
417 | } |
418 | |
419 | /* Lookup the interface in the global _OSI list */ |
420 | |
421 | interface_info = acpi_ut_get_interface(interface_name: string_desc->string.pointer); |
422 | if (interface_info && !(interface_info->flags & ACPI_OSI_INVALID)) { |
423 | /* |
424 | * The interface is supported. |
425 | * Update the osi_data if necessary. We keep track of the latest |
426 | * version of Windows that has been requested by the BIOS. |
427 | */ |
428 | if (interface_info->value > acpi_gbl_osi_data) { |
429 | acpi_gbl_osi_data = interface_info->value; |
430 | } |
431 | |
432 | return_value = ACPI_UINT64_MAX; |
433 | } |
434 | |
435 | acpi_os_release_mutex(acpi_gbl_osi_mutex); |
436 | |
437 | /* |
438 | * Invoke an optional _OSI interface handler. The host OS may wish |
439 | * to do some interface-specific handling. For example, warn about |
440 | * certain interfaces or override the true/false support value. |
441 | */ |
442 | interface_handler = acpi_gbl_interface_handler; |
443 | if (interface_handler) { |
444 | if (interface_handler |
445 | (string_desc->string.pointer, (u32)return_value)) { |
446 | return_value = ACPI_UINT64_MAX; |
447 | } |
448 | } |
449 | |
450 | ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, |
451 | "ACPI: BIOS _OSI(\"%s\") is %ssupported\n" , |
452 | string_desc->string.pointer, |
453 | return_value == 0 ? "not " : "" )); |
454 | |
455 | /* Complete the return object */ |
456 | |
457 | return_desc->integer.value = return_value; |
458 | walk_state->return_desc = return_desc; |
459 | return_ACPI_STATUS(AE_OK); |
460 | } |
461 | |