]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/platform_control-acpi_sleep.cpp
Update
[l4.git] / kernel / fiasco / src / kern / platform_control-acpi_sleep.cpp
1 IMPLEMENTATION:
2
3 #include <cstdio>
4
5 #include "acpi.h"
6 #include "acpi_fadt.h"
7 #include "context.h"
8 #include "kernel_thread.h"
9 #include "kmem.h"
10 #include "pm.h"
11 #include "timer.h"
12 #include "timer_tick.h"
13
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;
18
19 IMPLEMENT_OVERRIDE
20 void
21 Platform_control::init(Cpu_number cpu)
22 {
23   if (cpu != Cpu_number::boot_cpu())
24     return;
25
26   Acpi_fadt const *fadt = Acpi::find<Acpi_fadt const *>("FACP");
27   if (!fadt)
28     {
29       printf("ACPI: cannot find FADT, so suspend support disabled\n");
30       return;
31     }
32
33   facs = Acpi::map_table_head<Acpi_facs>(fadt->facs_addr);
34   printf("ACPI: FACS phys=%x virt=%p\n", fadt->facs_addr, facs);
35
36   if (!facs)
37     {
38       printf("ACPI: cannot map FACS, so suspend support disabled\n");
39       return;
40     }
41
42   if (!Acpi::check_signature(facs->signature, "FACS"))
43     {
44       printf("ACPI: FACS signature invalid, so suspend support disabled\n");
45       return;
46     }
47
48   printf("ACPI: HW sig=%x\n", facs->hw_signature);
49
50   extern char _tramp_acpi_wakeup[];
51   phys_wake_vector = (Address)_tramp_acpi_wakeup;
52   if (phys_wake_vector >= 1UL << 20)
53     {
54       printf("ACPI: invalid wake vector (1MB): %lx\n", phys_wake_vector);
55       return;
56     }
57
58   extern volatile Address _realmode_startup_pdbr;
59   _realmode_startup_pdbr = Kmem::get_realmode_startup_pdbr();
60   facs->fw_wake_vector = phys_wake_vector;
61
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;
66
67   _system_suspend_enabled = true;
68 }
69
70
71
72 /* implemented in ia32/tramp-acpi.S */
73 extern "C" FIASCO_FASTCALL
74 int acpi_save_cpu_and_suspend(Unsigned32 sleep_type,
75                               Unsigned32 pm1_cntl,
76                               Unsigned32 pm1_sts);
77
78
79 IMPLEMENTATION [mp]:
80
81 #include "cpu_call.h"
82
83 static Cpu_mask _cpus_to_suspend;
84
85 static void
86 suspend_ap_cpus()
87 {
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());
91
92   Cpu_call::cpu_call_many(_cpus_to_suspend, [](Cpu_number cpu)
93     {
94       Context::spill_current_fpu(cpu);
95       current()->kernel_context_drq([](Context::Drq *, Context *, void *)
96         {
97           Cpu_number cpun = current_cpu();
98           Cpu &cpu = Cpu::cpus.current();
99           Pm_object::run_on_suspend_hooks(cpun);
100           cpu.pm_suspend();
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();
106         }, 0);
107       return false;
108     }, true);
109
110   while (!_cpus_to_suspend.empty())
111     {
112       Proc::pause();
113       asm volatile ("" : "=m" (_cpus_to_suspend));
114     }
115 }
116
117 IMPLEMENTATION [!mp]:
118
119 static void suspend_ap_cpus() {}
120
121
122 IMPLEMENTATION:
123
124 #include "cpu_call.h"
125
126 /**
127  * \brief Initiate a full system suspend to RAM.
128  * \pre must run on the boot CPU
129  */
130 static Context::Drq::Result
131 do_system_suspend(Context::Drq *, Context *, void *data)
132 {
133   assert (current_cpu() == Cpu_number::boot_cpu());
134   Context::spill_current_fpu(current_cpu());
135   suspend_ap_cpus();
136
137   facs->fw_wake_vector = phys_wake_vector;
138   if (facs->len > 32 && facs->version >= 1)
139     facs->x_fw_wake_vector = 0;
140
141   Mword sleep_type = *(Mword *)data;
142   *reinterpret_cast<Mword*>(data) = 0;
143
144   Pm_object::run_on_suspend_hooks(current_cpu());
145
146   Cpu::cpus.current().pm_suspend();
147
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;
152
153   Cpu::cpus.current().pm_resume();
154
155   Pm_object::run_on_resume_hooks(current_cpu());
156
157   Fpu::init(current_cpu(), true);
158
159   Timer::init(current_cpu());
160   Timer_tick::enable(current_cpu());
161   Kernel_thread::boot_app_cpus();
162
163   return Context::Drq::no_answer_resched();
164 }
165
166 IMPLEMENT_OVERRIDE
167 static int
168 Platform_control::system_suspend(Mword extra)
169 {
170   auto guard = lock_guard(cpu_lock);
171
172   if (!_system_suspend_enabled)
173     return -L4_err::ENodev;
174
175   Cpu_mask cpus;
176   cpus.set(Cpu_number::boot_cpu());
177
178   Cpu_call::cpu_call_many(cpus, [&extra](Cpu_number)
179     {
180       current()->kernel_context_drq(do_system_suspend, &extra);
181       return false;
182     }, true);
183
184   return extra;
185 }