@@ -19,6 +19,91 @@ use crate::{
1919use alloc:: { alloc:: Global , vec:: Vec } ;
2020use core:: { alloc:: Allocator , mem, ptr} ;
2121
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+
22107#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
23108pub enum ProcessorState {
24109 /// A processor in this state is unusable, and you must not attempt to bring it up.
@@ -80,103 +165,3 @@ impl PmTimer {
80165 }
81166 }
82167}
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