8 #include "kernel_thread.h"
12 #include "timer_tick.h"
14 static bool _system_suspend_enabled;
15 static Unsigned32 _pm1a, _pm1b, _pm1a_sts, _pm1b_sts;
16 static Address phys_wake_vector;
17 static Acpi_facs *facs;
21 Platform_control::init(Cpu_number cpu)
23 if (cpu != Cpu_number::boot_cpu())
26 Acpi_fadt const *fadt = Acpi::find<Acpi_fadt const *>("FACP");
29 printf("ACPI: cannot find FADT, so suspend support disabled\n");
33 facs = Acpi::map_table_head<Acpi_facs>(fadt->facs_addr);
34 printf("ACPI: FACS phys=%x virt=%p\n", fadt->facs_addr, facs);
38 printf("ACPI: cannot map FACS, so suspend support disabled\n");
42 if (!Acpi::check_signature(facs->signature, "FACS"))
44 printf("ACPI: FACS signature invalid, so suspend support disabled\n");
48 printf("ACPI: HW sig=%x\n", facs->hw_signature);
50 extern char _tramp_acpi_wakeup[];
51 phys_wake_vector = (Address)_tramp_acpi_wakeup;
52 if (phys_wake_vector >= 1UL << 20)
54 printf("ACPI: invalid wake vector (1MB): %lx\n", phys_wake_vector);
58 extern volatile Address _realmode_startup_pdbr;
59 _realmode_startup_pdbr = Kmem::get_realmode_startup_pdbr();
60 facs->fw_wake_vector = phys_wake_vector;
62 _pm1a = fadt->pm1a_cntl_blk;
63 _pm1b = fadt->pm1b_cntl_blk;
64 _pm1a_sts = fadt->pm1a_evt_blk;
65 _pm1b_sts = fadt->pm1b_evt_blk;
67 _system_suspend_enabled = true;
72 /* implemented in ia32/tramp-acpi.S */
73 extern "C" FIASCO_FASTCALL
74 int acpi_save_cpu_and_suspend(Unsigned32 sleep_type,
83 static Cpu_mask _cpus_to_suspend;
88 // NOTE: This code must not be migrated and is not reentrant!
89 _cpus_to_suspend = Cpu::online_mask();
90 _cpus_to_suspend.clear(Cpu_number::boot_cpu());
92 Cpu_call::cpu_call_many(_cpus_to_suspend, [](Cpu_number cpu)
94 Context::spill_current_fpu(cpu);
95 current()->kernel_context_drq([](Context::Drq *, Context *, void *)
97 Cpu_number cpun = current_cpu();
98 Cpu &cpu = Cpu::cpus.current();
99 Pm_object::run_on_suspend_hooks(cpun);
101 check (Context::take_cpu_offline(cpun, true));
102 Platform_control::prepare_cpu_suspend(cpun);
103 _cpus_to_suspend.atomic_clear(current_cpu());
104 Platform_control::cpu_suspend(cpun);
105 return Context::Drq::no_answer_resched();
110 while (!_cpus_to_suspend.empty())
113 asm volatile ("" : "=m" (_cpus_to_suspend));
117 IMPLEMENTATION [!mp]:
119 static void suspend_ap_cpus() {}
124 #include "cpu_call.h"
127 * \brief Initiate a full system suspend to RAM.
128 * \pre must run on the boot CPU
130 static Context::Drq::Result
131 do_system_suspend(Context::Drq *, Context *, void *data)
133 assert (current_cpu() == Cpu_number::boot_cpu());
134 Context::spill_current_fpu(current_cpu());
137 facs->fw_wake_vector = phys_wake_vector;
138 if (facs->len > 32 && facs->version >= 1)
139 facs->x_fw_wake_vector = 0;
141 Mword sleep_type = *(Mword *)data;
142 *reinterpret_cast<Mword*>(data) = 0;
144 Pm_object::run_on_suspend_hooks(current_cpu());
146 Cpu::cpus.current().pm_suspend();
148 if (acpi_save_cpu_and_suspend(sleep_type,
149 (_pm1b << 16) | _pm1a,
150 (_pm1b_sts << 16) | _pm1a_sts))
151 *reinterpret_cast<Mword *>(data) = -L4_err::EInval;
153 Cpu::cpus.current().pm_resume();
155 Pm_object::run_on_resume_hooks(current_cpu());
157 Fpu::init(current_cpu(), true);
159 Timer::init(current_cpu());
160 Timer_tick::enable(current_cpu());
161 Kernel_thread::boot_app_cpus();
163 return Context::Drq::no_answer_resched();
168 Platform_control::system_suspend(Mword extra)
170 auto guard = lock_guard(cpu_lock);
172 if (!_system_suspend_enabled)
173 return -L4_err::ENodev;
176 cpus.set(Cpu_number::boot_cpu());
178 Cpu_call::cpu_call_many(cpus, [&extra](Cpu_number)
180 current()->kernel_context_drq(do_system_suspend, &extra);