@@ -44,10 +44,6 @@ use crate::mm::{FrameAlloc, PageBox, PageRangeAllocator};
4444
4545pub const MAGIC_VALUE : u32 = 0x7472_6976 ;
4646
47- pub const MMIO_START : usize = 0x0000_0000_feb0_0000 ;
48- pub const MMIO_END : usize = 0x0000_0000_feb0_ffff ;
49- const IRQ_NUMBER : u8 = 44 - 32 ;
50-
5147static MMIO_DRIVERS : InitCell < Vec < MmioDriver > > = InitCell :: new ( Vec :: new ( ) ) ;
5248
5349#[ allow( clippy:: enum_variant_names) ]
@@ -113,124 +109,76 @@ unsafe fn check_ptr(ptr: *mut u8) -> Option<VolatileRef<'static, DeviceRegisters
113109 }
114110
115111 info ! ( "Found Virtio {id:?} device: {mmio:p}" ) ;
112+ Some ( mmio)
113+ }
114+
115+ fn detect_device (
116+ virtual_address : VirtAddr ,
117+ current_address : usize ,
118+ ) -> Option < VolatileRef < ' static , DeviceRegisters > > {
119+ trace ! ( "try to detect MMIO device at physical address {current_address:#X}" ) ;
120+
121+ let mut flags = PageTableEntryFlags :: empty ( ) ;
122+ flags. normal ( ) . writable ( ) ;
123+ paging:: map :: < BasePageSize > (
124+ virtual_address,
125+ PhysAddr :: from ( current_address. align_down ( BasePageSize :: SIZE as usize ) ) ,
126+ 1 ,
127+ flags,
128+ ) ;
129+
130+ let addr = virtual_address. as_usize ( ) | ( current_address & ( BasePageSize :: SIZE as usize - 1 ) ) ;
131+ let ptr = ptr:: with_exposed_provenance_mut :: < u8 > ( addr) ;
132+
133+ let mmio = unsafe { check_ptr ( ptr) } ?;
134+
135+ if cfg ! ( debug_assertions) {
136+ let len = usize:: try_from ( BasePageSize :: SIZE ) . unwrap ( ) ;
137+ let start = current_address. align_down ( len) ;
138+ let frame_range = PageRange :: from_start_len ( start, len) . unwrap ( ) ;
139+
140+ FrameAlloc :: allocate_at ( frame_range) . unwrap_err ( ) ;
141+ }
116142
117143 Some ( mmio)
118144}
119145
120146fn check_linux_args (
121147 linux_mmio : & ' static [ String ] ,
122- ) -> Vec < ( VolatileRef < ' static , DeviceRegisters > , u8 ) > {
123- let layout = PageLayout :: from_size ( BasePageSize :: SIZE as usize ) . unwrap ( ) ;
124- let page_range = PageBox :: new ( layout) . unwrap ( ) ;
125- let virtual_address = VirtAddr :: from ( page_range. start ( ) ) ;
126-
127- let mut devices = vec ! [ ] ;
128- for arg in linux_mmio {
129- trace ! ( "check linux parameter: {arg}" ) ;
130-
131- match arg. trim ( ) . trim_matches ( char:: from ( 0 ) ) . strip_prefix ( "4K@" ) {
132- Some ( arg) => {
148+ virtual_address : VirtAddr ,
149+ ) -> impl Iterator < Item = ( VolatileRef < ' static , DeviceRegisters > , u8 ) > {
150+ linux_mmio
151+ . iter ( )
152+ . inspect ( |arg| trace ! ( "check linux parameter: {arg}" ) )
153+ . flat_map ( move |arg| {
154+ if let Some ( arg) = arg. trim ( ) . trim_matches ( char:: from ( 0 ) ) . strip_prefix ( "4K@" ) {
133155 let v: Vec < & str > = arg. trim ( ) . split ( ':' ) . collect ( ) ;
134156 let without_prefix = v[ 0 ] . trim_start_matches ( "0x" ) ;
135157 let current_address = usize:: from_str_radix ( without_prefix, 16 ) . unwrap ( ) ;
136158 let irq: u8 = v[ 1 ] . parse :: < u8 > ( ) . unwrap ( ) ;
137-
138- trace ! ( "try to detect MMIO device at physical address {current_address:#X}" ) ;
139-
140- let mut flags = PageTableEntryFlags :: empty ( ) ;
141- flags. normal ( ) . writable ( ) ;
142- paging:: map :: < BasePageSize > (
143- virtual_address,
144- PhysAddr :: from ( current_address. align_down ( BasePageSize :: SIZE as usize ) ) ,
145- 1 ,
146- flags,
147- ) ;
148-
149- let addr = virtual_address. as_usize ( )
150- | ( current_address & ( BasePageSize :: SIZE as usize - 1 ) ) ;
151- let ptr = ptr:: with_exposed_provenance_mut ( addr) ;
152- let Some ( mmio) = ( unsafe { check_ptr ( ptr) } ) else {
153- continue ;
154- } ;
155-
156- if cfg ! ( debug_assertions) {
157- let len = usize:: try_from ( BasePageSize :: SIZE ) . unwrap ( ) ;
158- let start = current_address. align_down ( len) ;
159- let frame_range = PageRange :: from_start_len ( start, len) . unwrap ( ) ;
160-
161- FrameAlloc :: allocate_at ( frame_range) . unwrap_err ( ) ;
162- }
163-
164- devices. push ( ( mmio, irq) ) ;
165- }
166- _ => {
159+ detect_device ( virtual_address, current_address) . map ( |mmio| ( mmio, irq) )
160+ } else {
167161 warn ! ( "Invalid prefix in {arg}" ) ;
162+ None
168163 }
169- }
170- }
171-
172- devices
173- }
174-
175- fn guess_device ( ) -> Vec < ( VolatileRef < ' static , DeviceRegisters > , u8 ) > {
176- // Trigger page mapping in the first iteration!
177- let mut current_page = 0 ;
178- let layout = PageLayout :: from_size ( BasePageSize :: SIZE as usize ) . unwrap ( ) ;
179- let page_range = PageBox :: new ( layout) . unwrap ( ) ;
180- let virtual_address = VirtAddr :: from ( page_range. start ( ) ) ;
181-
182- // Look for the device-ID in all possible 64-byte aligned addresses within this range.
183- let mut devices = vec ! [ ] ;
184- for current_address in ( MMIO_START ..MMIO_END ) . step_by ( 512 ) {
185- trace ! ( "try to detect MMIO device at physical address {current_address:#X}" ) ;
186- // Have we crossed a page boundary in the last iteration?
187- // info!("before the {}. paging", current_page);
188- if current_address / BasePageSize :: SIZE as usize > current_page {
189- if !devices. is_empty ( ) {
190- return devices;
191- }
192-
193- let mut flags = PageTableEntryFlags :: empty ( ) ;
194- flags. normal ( ) . writable ( ) ;
195- paging:: map :: < BasePageSize > (
196- virtual_address,
197- PhysAddr :: from ( current_address. align_down ( BasePageSize :: SIZE as usize ) ) ,
198- 1 ,
199- flags,
200- ) ;
201-
202- current_page = current_address / BasePageSize :: SIZE as usize ;
203- }
204-
205- let addr =
206- virtual_address. as_usize ( ) | ( current_address & ( BasePageSize :: SIZE as usize - 1 ) ) ;
207- let ptr = ptr:: with_exposed_provenance_mut ( addr) ;
208- let Some ( mmio) = ( unsafe { check_ptr ( ptr) } ) else {
209- continue ;
210- } ;
211-
212- if cfg ! ( debug_assertions) {
213- let len = usize:: try_from ( BasePageSize :: SIZE ) . unwrap ( ) ;
214- let start = current_address. align_down ( len) ;
215- let frame_range = PageRange :: from_start_len ( start, len) . unwrap ( ) ;
216-
217- FrameAlloc :: allocate_at ( frame_range) . unwrap_err ( ) ;
218- }
219-
220- devices. push ( ( mmio, IRQ_NUMBER ) ) ;
221- }
222-
223- devices
164+ } )
224165}
225166
226- fn detect_devices ( ) -> Vec < ( VolatileRef < ' static , DeviceRegisters > , u8 ) > {
227- let linux_mmio = env:: mmio ( ) ;
228-
229- if linux_mmio. is_empty ( ) {
230- guess_device ( )
231- } else {
232- check_linux_args ( linux_mmio)
233- }
167+ fn guess_device (
168+ virtual_address : VirtAddr ,
169+ ) -> impl Iterator < Item = ( VolatileRef < ' static , DeviceRegisters > , u8 ) > {
170+ // From https://gitlab.com/qemu-project/qemu/-/blob/v10.2.2/include/hw/i386/microvm.h#L53.
171+ const VIRTIO_MMIO_BASE : usize = 0xfeb0_0000 ;
172+ // Although these values are not constants in reality, those are the values
173+ // that we have for our configuration at the time of writing, based on
174+ // https://gitlab.com/qemu-project/qemu/-/blob/v10.2.2/hw/i386/microvm.c#L188-204.
175+ const VIRTIO_IRQ_BASE : u8 = 5 ;
176+ const VIRTIO_NUM_TRANSPORTS : u8 = 8 ;
177+
178+ ( 0 ..VIRTIO_NUM_TRANSPORTS ) . flat_map ( move |i| {
179+ detect_device ( virtual_address, VIRTIO_MMIO_BASE + usize:: from ( i) * 512 )
180+ . map ( |mmio| ( mmio, VIRTIO_IRQ_BASE + i) )
181+ } )
234182}
235183
236184#[ cfg( any(
@@ -269,29 +217,43 @@ pub(crate) fn get_vsock_driver() -> Option<&'static InterruptTicketMutex<VirtioV
269217 . find_map ( |drv| drv. get_vsock_driver ( ) )
270218}
271219
220+ fn register_mmio ( mmio : VolatileRef < ' static , DeviceRegisters > , irq : u8 ) {
221+ match mmio_virtio:: init_device ( mmio, irq) {
222+ #[ cfg( feature = "virtio-console" ) ]
223+ Ok ( VirtioDriver :: Console ( drv) ) => {
224+ register_driver ( MmioDriver :: VirtioConsole ( InterruptTicketMutex :: new ( * drv) ) ) ;
225+ }
226+ #[ cfg( feature = "virtio-fs" ) ]
227+ Ok ( VirtioDriver :: Fs ( drv) ) => {
228+ register_driver ( MmioDriver :: VirtioFs ( InterruptTicketMutex :: new ( * drv) ) ) ;
229+ }
230+ #[ cfg( feature = "virtio-net" ) ]
231+ Ok ( VirtioDriver :: Net ( drv) ) => {
232+ * NETWORK_DEVICE . lock ( ) = Some ( * drv) ;
233+ }
234+ #[ cfg( feature = "virtio-vsock" ) ]
235+ Ok ( VirtioDriver :: Vsock ( drv) ) => {
236+ register_driver ( MmioDriver :: VirtioVsock ( InterruptTicketMutex :: new ( * drv) ) ) ;
237+ }
238+ Err ( err) => error ! ( "Could not initialize virtio-mmio device: {err}" ) ,
239+ }
240+ }
241+
272242pub ( crate ) fn init_drivers ( ) {
273243 without_interrupts ( || {
274- let devices = detect_devices ( ) ;
275-
276- for ( mmio, irq) in devices {
277- match mmio_virtio:: init_device ( mmio, irq) {
278- #[ cfg( feature = "virtio-console" ) ]
279- Ok ( VirtioDriver :: Console ( drv) ) => {
280- register_driver ( MmioDriver :: VirtioConsole ( InterruptTicketMutex :: new ( * drv) ) ) ;
281- }
282- #[ cfg( feature = "virtio-fs" ) ]
283- Ok ( VirtioDriver :: Fs ( drv) ) => {
284- register_driver ( MmioDriver :: VirtioFs ( InterruptTicketMutex :: new ( * drv) ) ) ;
285- }
286- #[ cfg( feature = "virtio-net" ) ]
287- Ok ( VirtioDriver :: Net ( drv) ) => {
288- * NETWORK_DEVICE . lock ( ) = Some ( * drv) ;
289- }
290- #[ cfg( feature = "virtio-vsock" ) ]
291- Ok ( VirtioDriver :: Vsock ( drv) ) => {
292- register_driver ( MmioDriver :: VirtioVsock ( InterruptTicketMutex :: new ( * drv) ) ) ;
293- }
294- Err ( err) => error ! ( "Could not initialize virtio-mmio device: {err}" ) ,
244+ let layout = PageLayout :: from_size ( BasePageSize :: SIZE as usize ) . unwrap ( ) ;
245+ let page_range = PageBox :: new ( layout) . unwrap ( ) ;
246+ let virtual_address = VirtAddr :: from ( page_range. start ( ) ) ;
247+
248+ let linux_mmio = env:: mmio ( ) ;
249+
250+ if linux_mmio. is_empty ( ) {
251+ for ( mmio, irq) in guess_device ( virtual_address) {
252+ register_mmio ( mmio, irq) ;
253+ }
254+ } else {
255+ for ( mmio, irq) in check_linux_args ( linux_mmio, virtual_address) {
256+ register_mmio ( mmio, irq) ;
295257 }
296258 }
297259
0 commit comments