@@ -19,6 +19,91 @@ use crate::{
19
19
use alloc:: { alloc:: Global , vec:: Vec } ;
20
20
use core:: { alloc:: Allocator , mem, ptr} ;
21
21
22
+ /// `AcpiPlatform` is a higher-level view of the ACPI tables that makes it easier to perform common
23
+ /// tasks with ACPI. It requires allocator support.
24
+ pub struct AcpiPlatform < H : AcpiHandler , A : Allocator = Global > {
25
+ pub tables : AcpiTables < H > ,
26
+ pub power_profile : PowerProfile ,
27
+ pub interrupt_model : InterruptModel < A > ,
28
+ /// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
29
+ /// interrupt model. That information is stored here, if present.
30
+ pub processor_info : Option < ProcessorInfo < A > > ,
31
+ pub pm_timer : Option < PmTimer > ,
32
+ }
33
+
34
+ impl < H : AcpiHandler > AcpiPlatform < H , Global > {
35
+ pub fn new ( tables : AcpiTables < H > ) -> Result < Self , AcpiError > {
36
+ Self :: new_in ( tables, alloc:: alloc:: Global )
37
+ }
38
+ }
39
+
40
+ impl < H : AcpiHandler , A : Allocator + Clone > AcpiPlatform < H , A > {
41
+ pub fn new_in ( tables : AcpiTables < H > , allocator : A ) -> Result < Self , AcpiError > {
42
+ let Some ( fadt) = tables. find_table :: < Fadt > ( ) else { Err ( AcpiError :: TableNotFound ( Signature :: FADT ) ) ? } ;
43
+ let power_profile = fadt. power_profile ( ) ;
44
+
45
+ let ( interrupt_model, processor_info) = InterruptModel :: new_in ( & tables, allocator) ?;
46
+ let pm_timer = PmTimer :: new ( & fadt) ?;
47
+
48
+ Ok ( AcpiPlatform { tables, power_profile, interrupt_model, processor_info, pm_timer} )
49
+ }
50
+
51
+ /// Wake up all Application Processors (APs) using the Multiprocessor Wakeup Mailbox Mechanism.
52
+ /// This may not be available on the platform you're running on.
53
+ ///
54
+ /// On Intel processors, this will start the AP in long-mode, with interrupts disabled and a
55
+ /// single page with the supplied waking vector identity-mapped (it is therefore advisable to
56
+ /// align your waking vector to start at a page boundary and fit within one page).
57
+ pub unsafe fn wake_aps (
58
+ & self ,
59
+ apic_id : u32 ,
60
+ wakeup_vector : u64 ,
61
+ timeout_loops : u64 ,
62
+ handler : H ,
63
+ ) -> Result < ( ) , AcpiError > {
64
+ let Some ( madt) = self . tables . find_table :: < Madt > ( ) else { Err ( AcpiError :: TableNotFound ( Signature :: MADT ) ) ? } ;
65
+ let mailbox_addr = madt. get ( ) . get_mpwk_mailbox_addr ( ) ?;
66
+ let mut mpwk_mapping = unsafe {
67
+ handler. map_physical_region :: < MultiprocessorWakeupMailbox > (
68
+ mailbox_addr as usize ,
69
+ mem:: size_of :: < MultiprocessorWakeupMailbox > ( ) ,
70
+ )
71
+ } ;
72
+
73
+ // Reset command
74
+ unsafe {
75
+ ptr:: write_volatile ( & mut mpwk_mapping. command , MpProtectedModeWakeupCommand :: Noop as u16 ) ;
76
+ }
77
+
78
+ // Fill the mailbox
79
+ mpwk_mapping. apic_id = apic_id;
80
+ mpwk_mapping. wakeup_vector = wakeup_vector;
81
+ unsafe {
82
+ ptr:: write_volatile ( & mut mpwk_mapping. command , MpProtectedModeWakeupCommand :: Wakeup as u16 ) ;
83
+ }
84
+
85
+ // Wait to join
86
+ // TODO: if we merge the handlers into one, we could use `stall` here.
87
+ let mut loops = 0 ;
88
+ let mut command = MpProtectedModeWakeupCommand :: Wakeup ;
89
+ while command != MpProtectedModeWakeupCommand :: Noop {
90
+ if loops >= timeout_loops {
91
+ return Err ( AcpiError :: InvalidMadt ( MadtError :: WakeupApsTimeout ) ) ;
92
+ }
93
+ // SAFETY: The caller must ensure that the provided `handler` correctly handles these
94
+ // operations and that the specified `mailbox_addr` is valid.
95
+ unsafe {
96
+ command = ptr:: read_volatile ( & mpwk_mapping. command ) . into ( ) ;
97
+ }
98
+ core:: hint:: spin_loop ( ) ;
99
+ loops += 1 ;
100
+ }
101
+ drop ( mpwk_mapping) ;
102
+
103
+ Ok ( ( ) )
104
+ }
105
+ }
106
+
22
107
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
23
108
pub enum ProcessorState {
24
109
/// A processor in this state is unusable, and you must not attempt to bring it up.
@@ -80,103 +165,3 @@ impl PmTimer {
80
165
}
81
166
}
82
167
}
83
-
84
- /// `PlatformInfo` allows the collection of some basic information about the platform from some of the fixed-size
85
- /// tables in a nice way. It requires access to the `FADT` and `MADT`. It is the easiest way to get information
86
- /// about the processors and interrupt controllers on a platform.
87
- #[ derive( Debug , Clone ) ]
88
- pub struct PlatformInfo < A : Allocator = Global > {
89
- pub power_profile : PowerProfile ,
90
- pub interrupt_model : InterruptModel < A > ,
91
- /// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
92
- /// interrupt model. That information is stored here, if present.
93
- pub processor_info : Option < ProcessorInfo < A > > ,
94
- pub pm_timer : Option < PmTimer > ,
95
- /*
96
- * TODO: we could provide a nice view of the hardware register blocks in the FADT here.
97
- */
98
- }
99
-
100
- impl PlatformInfo < Global > {
101
- pub fn new < H > ( tables : & AcpiTables < H > ) -> Result < Self , AcpiError >
102
- where
103
- H : AcpiHandler ,
104
- {
105
- Self :: new_in ( tables, alloc:: alloc:: Global )
106
- }
107
- }
108
-
109
- impl < A : Allocator + Clone > PlatformInfo < A > {
110
- pub fn new_in < H > ( tables : & AcpiTables < H > , allocator : A ) -> Result < Self , AcpiError >
111
- where
112
- H : AcpiHandler ,
113
- {
114
- let Some ( fadt) = tables. find_table :: < Fadt > ( ) else { Err ( AcpiError :: TableNotFound ( Signature :: FADT ) ) ? } ;
115
- let power_profile = fadt. power_profile ( ) ;
116
-
117
- let ( interrupt_model, processor_info) = InterruptModel :: new_in ( & tables, allocator) ?;
118
- let pm_timer = PmTimer :: new ( & fadt) ?;
119
-
120
- Ok ( PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer } )
121
- }
122
- }
123
-
124
- /// Wakes up Application Processors (APs) using the Multiprocessor Wakeup Mailbox mechanism.
125
- ///
126
- /// For Intel processors, the execution environment is:
127
- /// - Interrupts must be disabled.
128
- /// - RFLAGES.IF set to 0.
129
- /// - Long mode enabled.
130
- /// - Paging mode is enabled and physical memory for waking vector is identity mapped (virtual address equals physical address).
131
- /// - Waking vector must be contained within one physical page.
132
- /// - Selectors are set to flat and otherwise not used.
133
- pub unsafe fn wakeup_aps < H > (
134
- tables : & AcpiTables < H > ,
135
- handler : H ,
136
- apic_id : u32 ,
137
- wakeup_vector : u64 ,
138
- timeout_loops : u64 ,
139
- ) -> Result < ( ) , AcpiError >
140
- where
141
- H : AcpiHandler ,
142
- {
143
- let Some ( madt) = tables. find_table :: < Madt > ( ) else { Err ( AcpiError :: TableNotFound ( Signature :: MADT ) ) ? } ;
144
- let mailbox_addr = madt. get ( ) . get_mpwk_mailbox_addr ( ) ?;
145
- let mut mpwk_mapping = unsafe {
146
- handler. map_physical_region :: < MultiprocessorWakeupMailbox > (
147
- mailbox_addr as usize ,
148
- mem:: size_of :: < MultiprocessorWakeupMailbox > ( ) ,
149
- )
150
- } ;
151
-
152
- // Reset command
153
- unsafe {
154
- ptr:: write_volatile ( & mut mpwk_mapping. command , MpProtectedModeWakeupCommand :: Noop as u16 ) ;
155
- }
156
-
157
- // Fill the mailbox
158
- mpwk_mapping. apic_id = apic_id;
159
- mpwk_mapping. wakeup_vector = wakeup_vector;
160
- unsafe {
161
- ptr:: write_volatile ( & mut mpwk_mapping. command , MpProtectedModeWakeupCommand :: Wakeup as u16 ) ;
162
- }
163
-
164
- // Wait to join
165
- let mut loops = 0 ;
166
- let mut command = MpProtectedModeWakeupCommand :: Wakeup ;
167
- while command != MpProtectedModeWakeupCommand :: Noop {
168
- if loops >= timeout_loops {
169
- return Err ( AcpiError :: InvalidMadt ( MadtError :: WakeupApsTimeout ) ) ;
170
- }
171
- // SAFETY: The caller must ensure that the provided `handler` correctly handles these
172
- // operations and that the specified `mailbox_addr` is valid.
173
- unsafe {
174
- command = ptr:: read_volatile ( & mpwk_mapping. command ) . into ( ) ;
175
- }
176
- core:: hint:: spin_loop ( ) ;
177
- loops += 1 ;
178
- }
179
- drop ( mpwk_mapping) ;
180
-
181
- Ok ( ( ) )
182
- }
0 commit comments