xref: /linux/drivers/acpi/acpica/utosi.c (revision 6f7e6393d1ce636bb7ec77a7fe7b77458fddf701)
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 - 2025, 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 	{"Windows 2022", NULL, 0, ACPI_OSI_WIN_11_22H2},	/* Windows 11 version 22H2 - Added 04/2024 */
79 
80 	/* Feature Group Strings */
81 
82 	{"Extended Address Space Descriptor", NULL, ACPI_OSI_FEATURE, 0},
83 
84 	/*
85 	 * All "optional" feature group strings (features that are implemented
86 	 * by the host) should be dynamically modified to VALID by the host via
87 	 * acpi_install_interface or acpi_update_interfaces. Such optional feature
88 	 * group strings are set as INVALID by default here.
89 	 */
90 
91 	{"Module Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
92 	{"Processor Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
93 	{"3.0 Thermal Model", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
94 	{"3.0 _SCP Extensions", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
95 	{"Processor Aggregator Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0},
96 
97 	/* See https://learn.microsoft.com/en-us/windows-hardware/drivers/display/automatic-display-switch */
98 
99 	{"DisplayMux", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}
100 };
101 
102 /*******************************************************************************
103  *
104  * FUNCTION:    acpi_ut_initialize_interfaces
105  *
106  * PARAMETERS:  None
107  *
108  * RETURN:      Status
109  *
110  * DESCRIPTION: Initialize the global _OSI supported interfaces list
111  *
112  ******************************************************************************/
113 
114 acpi_status acpi_ut_initialize_interfaces(void)
115 {
116 	acpi_status status;
117 	u32 i;
118 
119 	status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
120 	if (ACPI_FAILURE(status)) {
121 		return (status);
122 	}
123 
124 	acpi_gbl_supported_interfaces = acpi_default_supported_interfaces;
125 
126 	/* Link the static list of supported interfaces */
127 
128 	for (i = 0;
129 	     i < (ACPI_ARRAY_LENGTH(acpi_default_supported_interfaces) - 1);
130 	     i++) {
131 		acpi_default_supported_interfaces[i].next =
132 		    &acpi_default_supported_interfaces[(acpi_size)i + 1];
133 	}
134 
135 	acpi_os_release_mutex(acpi_gbl_osi_mutex);
136 	return (AE_OK);
137 }
138 
139 /*******************************************************************************
140  *
141  * FUNCTION:    acpi_ut_interface_terminate
142  *
143  * PARAMETERS:  None
144  *
145  * RETURN:      Status
146  *
147  * DESCRIPTION: Delete all interfaces in the global list. Sets
148  *              acpi_gbl_supported_interfaces to NULL.
149  *
150  ******************************************************************************/
151 
152 acpi_status acpi_ut_interface_terminate(void)
153 {
154 	acpi_status status;
155 	struct acpi_interface_info *next_interface;
156 
157 	status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
158 	if (ACPI_FAILURE(status)) {
159 		return (status);
160 	}
161 
162 	next_interface = acpi_gbl_supported_interfaces;
163 	while (next_interface) {
164 		acpi_gbl_supported_interfaces = next_interface->next;
165 
166 		if (next_interface->flags & ACPI_OSI_DYNAMIC) {
167 
168 			/* Only interfaces added at runtime can be freed */
169 
170 			ACPI_FREE(next_interface->name);
171 			ACPI_FREE(next_interface);
172 		} else {
173 			/* Interface is in static list. Reset it to invalid or valid. */
174 
175 			if (next_interface->flags & ACPI_OSI_DEFAULT_INVALID) {
176 				next_interface->flags |= ACPI_OSI_INVALID;
177 			} else {
178 				next_interface->flags &= ~ACPI_OSI_INVALID;
179 			}
180 		}
181 
182 		next_interface = acpi_gbl_supported_interfaces;
183 	}
184 
185 	acpi_os_release_mutex(acpi_gbl_osi_mutex);
186 	return (AE_OK);
187 }
188 
189 /*******************************************************************************
190  *
191  * FUNCTION:    acpi_ut_install_interface
192  *
193  * PARAMETERS:  interface_name      - The interface to install
194  *
195  * RETURN:      Status
196  *
197  * DESCRIPTION: Install the interface into the global interface list.
198  *              Caller MUST hold acpi_gbl_osi_mutex
199  *
200  ******************************************************************************/
201 
202 acpi_status acpi_ut_install_interface(acpi_string interface_name)
203 {
204 	struct acpi_interface_info *interface_info;
205 
206 	/* Allocate info block and space for the name string */
207 
208 	interface_info =
209 	    ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_interface_info));
210 	if (!interface_info) {
211 		return (AE_NO_MEMORY);
212 	}
213 
214 	interface_info->name = ACPI_ALLOCATE_ZEROED(strlen(interface_name) + 1);
215 	if (!interface_info->name) {
216 		ACPI_FREE(interface_info);
217 		return (AE_NO_MEMORY);
218 	}
219 
220 	/* Initialize new info and insert at the head of the global list */
221 
222 	strcpy(interface_info->name, interface_name);
223 	interface_info->flags = ACPI_OSI_DYNAMIC;
224 	interface_info->next = acpi_gbl_supported_interfaces;
225 
226 	acpi_gbl_supported_interfaces = interface_info;
227 	return (AE_OK);
228 }
229 
230 /*******************************************************************************
231  *
232  * FUNCTION:    acpi_ut_remove_interface
233  *
234  * PARAMETERS:  interface_name      - The interface to remove
235  *
236  * RETURN:      Status
237  *
238  * DESCRIPTION: Remove the interface from the global interface list.
239  *              Caller MUST hold acpi_gbl_osi_mutex
240  *
241  ******************************************************************************/
242 
243 acpi_status acpi_ut_remove_interface(acpi_string interface_name)
244 {
245 	struct acpi_interface_info *previous_interface;
246 	struct acpi_interface_info *next_interface;
247 
248 	previous_interface = next_interface = acpi_gbl_supported_interfaces;
249 	while (next_interface) {
250 		if (!strcmp(interface_name, next_interface->name)) {
251 			/*
252 			 * Found: name is in either the static list
253 			 * or was added at runtime
254 			 */
255 			if (next_interface->flags & ACPI_OSI_DYNAMIC) {
256 
257 				/* Interface was added dynamically, remove and free it */
258 
259 				if (previous_interface == next_interface) {
260 					acpi_gbl_supported_interfaces =
261 					    next_interface->next;
262 				} else {
263 					previous_interface->next =
264 					    next_interface->next;
265 				}
266 
267 				ACPI_FREE(next_interface->name);
268 				ACPI_FREE(next_interface);
269 			} else {
270 				/*
271 				 * Interface is in static list. If marked invalid, then
272 				 * it does not actually exist. Else, mark it invalid.
273 				 */
274 				if (next_interface->flags & ACPI_OSI_INVALID) {
275 					return (AE_NOT_EXIST);
276 				}
277 
278 				next_interface->flags |= ACPI_OSI_INVALID;
279 			}
280 
281 			return (AE_OK);
282 		}
283 
284 		previous_interface = next_interface;
285 		next_interface = next_interface->next;
286 	}
287 
288 	/* Interface was not found */
289 
290 	return (AE_NOT_EXIST);
291 }
292 
293 /*******************************************************************************
294  *
295  * FUNCTION:    acpi_ut_update_interfaces
296  *
297  * PARAMETERS:  action              - Actions to be performed during the
298  *                                    update
299  *
300  * RETURN:      Status
301  *
302  * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor
303  *              strings or/and feature group strings.
304  *              Caller MUST hold acpi_gbl_osi_mutex
305  *
306  ******************************************************************************/
307 
308 acpi_status acpi_ut_update_interfaces(u8 action)
309 {
310 	struct acpi_interface_info *next_interface;
311 
312 	next_interface = acpi_gbl_supported_interfaces;
313 	while (next_interface) {
314 		if (((next_interface->flags & ACPI_OSI_FEATURE) &&
315 		     (action & ACPI_FEATURE_STRINGS)) ||
316 		    (!(next_interface->flags & ACPI_OSI_FEATURE) &&
317 		     (action & ACPI_VENDOR_STRINGS))) {
318 			if (action & ACPI_DISABLE_INTERFACES) {
319 
320 				/* Mark the interfaces as invalid */
321 
322 				next_interface->flags |= ACPI_OSI_INVALID;
323 			} else {
324 				/* Mark the interfaces as valid */
325 
326 				next_interface->flags &= ~ACPI_OSI_INVALID;
327 			}
328 		}
329 
330 		next_interface = next_interface->next;
331 	}
332 
333 	return (AE_OK);
334 }
335 
336 /*******************************************************************************
337  *
338  * FUNCTION:    acpi_ut_get_interface
339  *
340  * PARAMETERS:  interface_name      - The interface to find
341  *
342  * RETURN:      struct acpi_interface_info if found. NULL if not found.
343  *
344  * DESCRIPTION: Search for the specified interface name in the global list.
345  *              Caller MUST hold acpi_gbl_osi_mutex
346  *
347  ******************************************************************************/
348 
349 struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name)
350 {
351 	struct acpi_interface_info *next_interface;
352 
353 	next_interface = acpi_gbl_supported_interfaces;
354 	while (next_interface) {
355 		if (!strcmp(interface_name, next_interface->name)) {
356 			return (next_interface);
357 		}
358 
359 		next_interface = next_interface->next;
360 	}
361 
362 	return (NULL);
363 }
364 
365 /*******************************************************************************
366  *
367  * FUNCTION:    acpi_ut_osi_implementation
368  *
369  * PARAMETERS:  walk_state          - Current walk state
370  *
371  * RETURN:      Status
372  *              Integer: TRUE (0) if input string is matched
373  *                       FALSE (-1) if string is not matched
374  *
375  * DESCRIPTION: Implementation of the _OSI predefined control method. When
376  *              an invocation of _OSI is encountered in the system AML,
377  *              control is transferred to this function.
378  *
379  * (August 2016)
380  * Note:  _OSI is now defined to return "Ones" to indicate a match, for
381  * compatibility with other ACPI implementations. On a 32-bit DSDT, Ones
382  * is 0xFFFFFFFF. On a 64-bit DSDT, Ones is 0xFFFFFFFFFFFFFFFF
383  * (ACPI_UINT64_MAX).
384  *
385  * This function always returns ACPI_UINT64_MAX for TRUE, and later code
386  * will truncate this to 32 bits if necessary.
387  *
388  ******************************************************************************/
389 
390 acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
391 {
392 	union acpi_operand_object *string_desc;
393 	union acpi_operand_object *return_desc;
394 	struct acpi_interface_info *interface_info;
395 	acpi_interface_handler interface_handler;
396 	acpi_status status;
397 	u64 return_value;
398 
399 	ACPI_FUNCTION_TRACE(ut_osi_implementation);
400 
401 	/* Validate the string input argument (from the AML caller) */
402 
403 	string_desc = walk_state->arguments[0].object;
404 	if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) {
405 		return_ACPI_STATUS(AE_TYPE);
406 	}
407 
408 	/* Create a return object */
409 
410 	return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
411 	if (!return_desc) {
412 		return_ACPI_STATUS(AE_NO_MEMORY);
413 	}
414 
415 	/* Default return value is 0, NOT SUPPORTED */
416 
417 	return_value = 0;
418 	status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER);
419 	if (ACPI_FAILURE(status)) {
420 		acpi_ut_remove_reference(return_desc);
421 		return_ACPI_STATUS(status);
422 	}
423 
424 	/* Lookup the interface in the global _OSI list */
425 
426 	interface_info = acpi_ut_get_interface(string_desc->string.pointer);
427 	if (interface_info && !(interface_info->flags & ACPI_OSI_INVALID)) {
428 		/*
429 		 * The interface is supported.
430 		 * Update the osi_data if necessary. We keep track of the latest
431 		 * version of Windows that has been requested by the BIOS.
432 		 */
433 		if (interface_info->value > acpi_gbl_osi_data) {
434 			acpi_gbl_osi_data = interface_info->value;
435 		}
436 
437 		return_value = ACPI_UINT64_MAX;
438 	}
439 
440 	acpi_os_release_mutex(acpi_gbl_osi_mutex);
441 
442 	/*
443 	 * Invoke an optional _OSI interface handler. The host OS may wish
444 	 * to do some interface-specific handling. For example, warn about
445 	 * certain interfaces or override the true/false support value.
446 	 */
447 	interface_handler = acpi_gbl_interface_handler;
448 	if (interface_handler) {
449 		if (interface_handler
450 		    (string_desc->string.pointer, (u32)return_value)) {
451 			return_value = ACPI_UINT64_MAX;
452 		}
453 	}
454 
455 	ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
456 			      "ACPI: BIOS _OSI(\"%s\") is %ssupported\n",
457 			      string_desc->string.pointer,
458 			      return_value == 0 ? "not " : ""));
459 
460 	/* Complete the return object */
461 
462 	return_desc->integer.value = return_value;
463 	walk_state->return_desc = return_desc;
464 	return_ACPI_STATUS(AE_OK);
465 }
466