1#include <uacpi/sleep.h>
2#include <uacpi/internal/context.h>
3#include <uacpi/internal/log.h>
4#include <uacpi/internal/io.h>
5#include <uacpi/internal/registers.h>
6#include <uacpi/internal/event.h>
7#include <uacpi/platform/arch_helpers.h>
8
9#ifndef UACPI_REDUCED_HARDWARE
10#define CALL_SLEEP_FN(name, state) \
11 (uacpi_is_hardware_reduced() ? \
12 name##_hw_reduced(state) : name##_hw_full(state))
13#else
14#define CALL_SLEEP_FN(name, state) name##_hw_reduced(state);
15#endif
16
17static uacpi_status eval_wak(uacpi_u8 state);
18static uacpi_status eval_sst(uacpi_u8 value);
19
20#ifndef UACPI_REDUCED_HARDWARE
21uacpi_status uacpi_set_waking_vector(
22 uacpi_phys_addr addr32, uacpi_phys_addr addr64
23)
24{
25 struct acpi_facs *facs = g_uacpi_rt_ctx.facs;
26
27 if (facs == UACPI_NULL)
28 return UACPI_STATUS_OK;
29
30 facs->firmware_waking_vector = addr32;
31
32 // The 64-bit wake vector doesn't exist, we're done
33 if (facs->length < 32)
34 return UACPI_STATUS_OK;
35
36 // Only allow 64-bit wake vector on 1.0 and above FACS
37 if (facs->version >= 1)
38 facs->x_firmware_waking_vector = addr64;
39 else
40 facs->x_firmware_waking_vector = 0;
41
42 return UACPI_STATUS_OK;
43}
44
45static uacpi_status enter_sleep_state_hw_full(uacpi_u8 state)
46{
47 uacpi_status ret;
48 uacpi_u64 wake_status, pm1a, pm1b;
49
50 ret = uacpi_write_register_field(
51 UACPI_REGISTER_FIELD_WAK_STS, ACPI_PM1_STS_CLEAR
52 );
53 if (uacpi_unlikely_error(ret))
54 return ret;
55
56 ret = uacpi_disable_all_gpes();
57 if (uacpi_unlikely_error(ret))
58 return ret;
59
60 ret = uacpi_clear_all_events();
61 if (uacpi_unlikely_error(ret))
62 return ret;
63
64 ret = uacpi_enable_all_wake_gpes();
65 if (uacpi_unlikely_error(ret))
66 return ret;
67
68 ret = uacpi_read_register(UACPI_REGISTER_PM1_CNT, &pm1a);
69 if (uacpi_unlikely_error(ret))
70 return ret;
71
72 pm1a &= ~((uacpi_u64)(ACPI_PM1_CNT_SLP_TYP_MASK | ACPI_PM1_CNT_SLP_EN_MASK));
73 pm1b = pm1a;
74
75 pm1a |= g_uacpi_rt_ctx.last_sleep_typ_a << ACPI_PM1_CNT_SLP_TYP_IDX;
76 pm1b |= g_uacpi_rt_ctx.last_sleep_typ_b << ACPI_PM1_CNT_SLP_TYP_IDX;
77
78 /*
79 * Just like ACPICA, split writing SLP_TYP and SLP_EN to work around
80 * buggy firmware that can't handle both written at the same time.
81 */
82 ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
83 if (uacpi_unlikely_error(ret))
84 return ret;
85
86 pm1a |= ACPI_PM1_CNT_SLP_EN_MASK;
87 pm1b |= ACPI_PM1_CNT_SLP_EN_MASK;
88
89 if (state < UACPI_SLEEP_STATE_S4)
90 UACPI_ARCH_FLUSH_CPU_CACHE();
91
92 ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
93 if (uacpi_unlikely_error(ret))
94 return ret;
95
96 if (state > UACPI_SLEEP_STATE_S3) {
97 /*
98 * We're still here, this is a bug or very slow firmware.
99 * Just try spinning for a bit.
100 */
101 uacpi_u64 stalled_time = 0;
102
103 // 10 seconds max
104 while (stalled_time < (10 * 1000 * 1000)) {
105 uacpi_kernel_stall(usec: 100);
106 stalled_time += 100;
107 }
108
109 // Try one more time
110 ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
111 if (uacpi_unlikely_error(ret))
112 return ret;
113
114 // Nothing we can do here, give up
115 return UACPI_STATUS_HARDWARE_TIMEOUT;
116 }
117
118 do {
119 ret = uacpi_read_register_field(
120 UACPI_REGISTER_FIELD_WAK_STS, &wake_status
121 );
122 if (uacpi_unlikely_error(ret))
123 return ret;
124 } while (wake_status != 1);
125
126 return UACPI_STATUS_OK;
127}
128
129static uacpi_status prepare_for_wake_from_sleep_state_hw_full(uacpi_u8 state)
130{
131 uacpi_status ret;
132 uacpi_u64 pm1a, pm1b;
133 UACPI_UNUSED(state);
134
135 /*
136 * Some hardware apparently relies on S0 values being written to the PM1
137 * control register on wake, so do this here.
138 */
139
140 if (g_uacpi_rt_ctx.s0_sleep_typ_a == UACPI_SLEEP_TYP_INVALID)
141 goto out;
142
143 ret = uacpi_read_register(UACPI_REGISTER_PM1_CNT, &pm1a);
144 if (uacpi_unlikely_error(ret))
145 goto out;
146
147 pm1a &= ~((uacpi_u64)(ACPI_PM1_CNT_SLP_TYP_MASK | ACPI_PM1_CNT_SLP_EN_MASK));
148 pm1b = pm1a;
149
150 pm1a |= g_uacpi_rt_ctx.s0_sleep_typ_a << ACPI_PM1_CNT_SLP_TYP_IDX;
151 pm1b |= g_uacpi_rt_ctx.s0_sleep_typ_b << ACPI_PM1_CNT_SLP_TYP_IDX;
152
153 uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b);
154out:
155 // Errors ignored intentionally, we don't want to abort because of this
156 return UACPI_STATUS_OK;
157}
158
159static uacpi_status wake_from_sleep_state_hw_full(uacpi_u8 state)
160{
161 uacpi_status ret;
162 g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
163 g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
164
165 // Set the status to 2 (waking) while we execute the wake method.
166 eval_sst(value: 2);
167
168 ret = uacpi_disable_all_gpes();
169 if (uacpi_unlikely_error(ret))
170 return ret;
171
172 ret = uacpi_enable_all_runtime_gpes();
173 if (uacpi_unlikely_error(ret))
174 return ret;
175
176 eval_wak(state);
177
178 // Apparently some BIOSes expect us to clear this, so do it
179 uacpi_write_register_field(
180 UACPI_REGISTER_FIELD_WAK_STS, ACPI_PM1_STS_CLEAR
181 );
182
183 // Now that we're awake set the status to 1 (running)
184 eval_sst(value: 1);
185
186 return UACPI_STATUS_OK;
187}
188#endif
189
190static uacpi_status get_slp_type_for_state(
191 uacpi_u8 state, uacpi_u8 *a, uacpi_u8 *b
192)
193{
194 uacpi_char path[] = "_S0";
195 uacpi_status ret;
196 uacpi_object *obj0, *obj1, *ret_obj = UACPI_NULL;
197
198 path[2] += state;
199
200 ret = uacpi_eval_typed(
201 parent: uacpi_namespace_root(), path, UACPI_NULL,
202 UACPI_OBJECT_PACKAGE_BIT, ret: &ret_obj
203 );
204 if (ret != UACPI_STATUS_OK) {
205 if (uacpi_unlikely(ret != UACPI_STATUS_NOT_FOUND)) {
206 uacpi_warn("error while evaluating %s: %s\n", path,
207 uacpi_status_to_string(ret));
208 } else {
209 uacpi_trace("sleep state %d is not supported as %s was not found\n",
210 state, path);
211 }
212 goto out;
213 }
214
215 switch (ret_obj->package->count) {
216 case 0:
217 uacpi_error("empty package while evaluating %s!\n", path);
218 ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
219 goto out;
220
221 case 1:
222 obj0 = ret_obj->package->objects[0];
223 if (uacpi_unlikely(obj0->type != UACPI_OBJECT_INTEGER)) {
224 uacpi_error(
225 "invalid object type at pkg[0] => %s when evaluating %s\n",
226 uacpi_object_type_to_string(obj0->type), path
227 );
228 goto out;
229 }
230
231 *a = obj0->integer;
232 *b = obj0->integer >> 8;
233 break;
234
235 default:
236 obj0 = ret_obj->package->objects[0];
237 obj1 = ret_obj->package->objects[1];
238
239 if (uacpi_unlikely(obj0->type != UACPI_OBJECT_INTEGER ||
240 obj1->type != UACPI_OBJECT_INTEGER)) {
241 uacpi_error(
242 "invalid object type when evaluating %s: "
243 "pkg[0] => %s, pkg[1] => %s\n", path,
244 uacpi_object_type_to_string(obj0->type),
245 uacpi_object_type_to_string(obj1->type)
246 );
247 ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
248 goto out;
249 }
250
251 *a = obj0->integer;
252 *b = obj1->integer;
253 break;
254 }
255
256out:
257 if (ret != UACPI_STATUS_OK) {
258 *a = UACPI_SLEEP_TYP_INVALID;
259 *b = UACPI_SLEEP_TYP_INVALID;
260 }
261
262 uacpi_object_unref(obj: ret_obj);
263 return ret;
264}
265
266static uacpi_status eval_sleep_helper(
267 uacpi_namespace_node *parent, const uacpi_char *path, uacpi_u8 value
268)
269{
270 uacpi_object *arg;
271 uacpi_args args;
272 uacpi_status ret;
273
274 arg = uacpi_create_object(type: UACPI_OBJECT_INTEGER);
275 if (uacpi_unlikely(arg == UACPI_NULL))
276 return UACPI_STATUS_OUT_OF_MEMORY;
277
278 arg->integer = value;
279 args.objects = &arg;
280 args.count = 1;
281
282 ret = uacpi_eval(parent, path, args: &args, UACPI_NULL);
283 switch (ret) {
284 case UACPI_STATUS_OK:
285 break;
286 case UACPI_STATUS_NOT_FOUND:
287 ret = UACPI_STATUS_OK;
288 break;
289 default:
290 uacpi_error("error while evaluating %s: %s\n",
291 path, uacpi_status_to_string(ret));
292 break;
293 }
294
295 uacpi_object_unref(obj: arg);
296 return ret;
297}
298
299static uacpi_status eval_pts(uacpi_u8 state)
300{
301 return eval_sleep_helper(parent: uacpi_namespace_root(), path: "_PTS", value: state);
302}
303
304static uacpi_status eval_wak(uacpi_u8 state)
305{
306 return eval_sleep_helper(parent: uacpi_namespace_root(), path: "_WAK", value: state);
307}
308
309static uacpi_status eval_sst(uacpi_u8 value)
310{
311 return eval_sleep_helper(
312 parent: uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_SI),
313 path: "_SST", value
314 );
315}
316
317static uacpi_status eval_sst_for_state(enum uacpi_sleep_state state)
318{
319 uacpi_u8 arg;
320
321 /*
322 * This optional object is a control method that OSPM invokes to set the
323 * system status indicator as desired.
324 * Arguments:(1)
325 * Arg0 - An Integer containing the system status indicator identifier:
326 * 0 - No system state indication. Indicator off
327 * 1 - Working
328 * 2 - Waking
329 * 3 - Sleeping. Used to indicate system state S1, S2, or S3
330 * 4 - Sleeping with context saved to non-volatile storage
331 */
332 switch (state) {
333 case UACPI_SLEEP_STATE_S0:
334 arg = 1;
335 break;
336 case UACPI_SLEEP_STATE_S1:
337 case UACPI_SLEEP_STATE_S2:
338 case UACPI_SLEEP_STATE_S3:
339 arg = 3;
340 break;
341 case UACPI_SLEEP_STATE_S4:
342 arg = 4;
343 break;
344 case UACPI_SLEEP_STATE_S5:
345 arg = 0;
346 break;
347 default:
348 return UACPI_STATUS_INVALID_ARGUMENT;
349 }
350
351 return eval_sst(value: arg);
352}
353
354uacpi_status uacpi_prepare_for_sleep_state(enum uacpi_sleep_state state_enum)
355{
356 uacpi_u8 state = state_enum;
357 uacpi_status ret;
358
359 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
360
361 if (uacpi_unlikely(state > UACPI_SLEEP_STATE_S5))
362 return UACPI_STATUS_INVALID_ARGUMENT;
363
364 ret = get_slp_type_for_state(
365 state,
366 a: &g_uacpi_rt_ctx.last_sleep_typ_a,
367 b: &g_uacpi_rt_ctx.last_sleep_typ_b
368 );
369 if (ret != UACPI_STATUS_OK)
370 return ret;
371
372 ret = get_slp_type_for_state(
373 state: 0,
374 a: &g_uacpi_rt_ctx.s0_sleep_typ_a,
375 b: &g_uacpi_rt_ctx.s0_sleep_typ_b
376 );
377
378 ret = eval_pts(state);
379 if (uacpi_unlikely_error(ret))
380 return ret;
381
382 eval_sst_for_state(state);
383 return UACPI_STATUS_OK;
384}
385
386static uacpi_u8 make_hw_reduced_sleep_control(uacpi_u8 slp_typ)
387{
388 uacpi_u8 value;
389
390 value = (slp_typ << ACPI_SLP_CNT_SLP_TYP_IDX);
391 value &= ACPI_SLP_CNT_SLP_TYP_MASK;
392 value |= ACPI_SLP_CNT_SLP_EN_MASK;
393
394 return value;
395}
396
397static uacpi_status enter_sleep_state_hw_reduced(uacpi_u8 state)
398{
399 uacpi_status ret;
400 uacpi_u8 sleep_control;
401 uacpi_u64 wake_status;
402 struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
403
404 if (!fadt->sleep_control_reg.address || !fadt->sleep_status_reg.address)
405 return UACPI_STATUS_NOT_FOUND;
406
407 ret = uacpi_write_register_field(
408 UACPI_REGISTER_FIELD_HWR_WAK_STS,
409 ACPI_SLP_STS_CLEAR
410 );
411 if (uacpi_unlikely_error(ret))
412 return ret;
413
414 sleep_control = make_hw_reduced_sleep_control(
415 slp_typ: g_uacpi_rt_ctx.last_sleep_typ_a
416 );
417
418 if (state < UACPI_SLEEP_STATE_S4)
419 UACPI_ARCH_FLUSH_CPU_CACHE();
420
421 /*
422 * To put the system into a sleep state, software will write the HW-reduced
423 * Sleep Type value (obtained from the \_Sx object in the DSDT) and the
424 * SLP_EN bit to the sleep control register.
425 */
426 ret = uacpi_write_register(UACPI_REGISTER_SLP_CNT, sleep_control);
427 if (uacpi_unlikely_error(ret))
428 return ret;
429
430 /*
431 * The OSPM then polls the WAK_STS bit of the SLEEP_STATUS_REG waiting for
432 * it to be one (1), indicating that the system has been transitioned
433 * back to the Working state.
434 */
435 do {
436 ret = uacpi_read_register_field(
437 UACPI_REGISTER_FIELD_HWR_WAK_STS, &wake_status
438 );
439 if (uacpi_unlikely_error(ret))
440 return ret;
441 } while (wake_status != 1);
442
443 return UACPI_STATUS_OK;
444}
445
446static uacpi_status prepare_for_wake_from_sleep_state_hw_reduced(uacpi_u8 state)
447{
448 uacpi_u8 sleep_control;
449 UACPI_UNUSED(state);
450
451 if (g_uacpi_rt_ctx.s0_sleep_typ_a == UACPI_SLEEP_TYP_INVALID)
452 goto out;
453
454 sleep_control = make_hw_reduced_sleep_control(
455 slp_typ: g_uacpi_rt_ctx.s0_sleep_typ_a
456 );
457 uacpi_write_register(UACPI_REGISTER_SLP_CNT, sleep_control);
458
459out:
460 return UACPI_STATUS_OK;
461}
462
463static uacpi_status wake_from_sleep_state_hw_reduced(uacpi_u8 state)
464{
465 g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID;
466 g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID;
467
468 // Set the status to 2 (waking) while we execute the wake method.
469 eval_sst(value: 2);
470
471 eval_wak(state);
472
473 // Apparently some BIOSes expect us to clear this, so do it
474 uacpi_write_register_field(
475 UACPI_REGISTER_FIELD_HWR_WAK_STS, ACPI_SLP_STS_CLEAR
476 );
477
478 // Now that we're awake set the status to 1 (running)
479 eval_sst(value: 1);
480
481 return UACPI_STATUS_OK;
482}
483
484uacpi_status uacpi_enter_sleep_state(enum uacpi_sleep_state state_enum)
485{
486 uacpi_u8 state = state_enum;
487
488 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
489
490 if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX))
491 return UACPI_STATUS_INVALID_ARGUMENT;
492
493 if (uacpi_unlikely(g_uacpi_rt_ctx.last_sleep_typ_a > ACPI_SLP_TYP_MAX ||
494 g_uacpi_rt_ctx.last_sleep_typ_b > ACPI_SLP_TYP_MAX)) {
495 uacpi_error("invalid SLP_TYP values: 0x%02X:0x%02X\n",
496 g_uacpi_rt_ctx.last_sleep_typ_a,
497 g_uacpi_rt_ctx.last_sleep_typ_b);
498 return UACPI_STATUS_AML_BAD_ENCODING;
499 }
500
501 return CALL_SLEEP_FN(enter_sleep_state, state);
502}
503
504uacpi_status uacpi_prepare_for_wake_from_sleep_state(
505 uacpi_sleep_state state_enum
506)
507{
508 uacpi_u8 state = state_enum;
509
510 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
511
512 if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX))
513 return UACPI_STATUS_INVALID_ARGUMENT;
514
515 return CALL_SLEEP_FN(prepare_for_wake_from_sleep_state, state);
516}
517
518uacpi_status uacpi_wake_from_sleep_state(
519 uacpi_sleep_state state_enum
520)
521{
522 uacpi_u8 state = state_enum;
523
524 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED);
525
526 if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX))
527 return UACPI_STATUS_INVALID_ARGUMENT;
528
529 return CALL_SLEEP_FN(wake_from_sleep_state, state);
530}
531
532uacpi_status uacpi_reboot(void)
533{
534 uacpi_status ret;
535 struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt;
536 struct acpi_gas *reset_reg = &fadt->reset_reg;
537
538 /*
539 * Allow restarting earlier than namespace load so that the kernel can
540 * use this in case of some initialization error.
541 */
542 UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED);
543
544 if (!(fadt->flags & ACPI_RESET_REG_SUP) || !reset_reg->address)
545 return UACPI_STATUS_NOT_FOUND;
546
547 switch (reset_reg->address_space_id) {
548 case UACPI_ADDRESS_SPACE_SYSTEM_IO:
549 /*
550 * For SystemIO we don't do any checking, and we ignore bit width
551 * because that's what NT does.
552 */
553 ret = uacpi_kernel_raw_io_write(
554 address: reset_reg->address, byte_width: 1, in_value: fadt->reset_value
555 );
556 break;
557 case UACPI_ADDRESS_SPACE_SYSTEM_MEMORY:
558 ret = uacpi_write_register(UACPI_REGISTER_RESET, fadt->reset_value);
559 break;
560 case UACPI_ADDRESS_SPACE_PCI_CONFIG: {
561 // Bus is assumed to be 0 here
562 uacpi_pci_address address = {
563 .segment = 0,
564 .bus = 0,
565 .device = (reset_reg->address >> 32) & 0xFF,
566 .function = (reset_reg->address >> 16) & 0xFF,
567 };
568
569 ret = uacpi_kernel_pci_write(
570 address: &address, offset: reset_reg->address & 0xFFFF, byte_width: 1, value: fadt->reset_value
571 );
572 break;
573 }
574 default:
575 uacpi_warn(
576 "unable to perform a reset: unsupported address space '%s' (%d)\n",
577 uacpi_address_space_to_string(reset_reg->address_space_id),
578 reset_reg->address_space_id
579 );
580 ret = UACPI_STATUS_UNIMPLEMENTED;
581 }
582
583 if (ret == UACPI_STATUS_OK) {
584 /*
585 * This should've worked but we're still here.
586 * Spin for a bit then give up.
587 */
588 uacpi_u64 stalled_time = 0;
589
590 while (stalled_time < (1000 * 1000)) {
591 uacpi_kernel_stall(usec: 100);
592 stalled_time += 100;
593 }
594
595 uacpi_error("reset timeout\n");
596 return UACPI_STATUS_HARDWARE_TIMEOUT;
597 }
598
599 return ret;
600}
601