Skip to content

Commit 3fd2cd0

Browse files
authored
Merge pull request #2319 from cagatay-y/refactor-mmio
refactor(mmio): deduplicate code and avoid allocation
2 parents 38bd7fd + 6faef5b commit 3fd2cd0

1 file changed

Lines changed: 91 additions & 129 deletions

File tree

src/arch/x86_64/kernel/mmio.rs

Lines changed: 91 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ use crate::mm::{FrameAlloc, PageBox, PageRangeAllocator};
4444

4545
pub 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-
5147
static 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

120146
fn 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+
272242
pub(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

Comments
 (0)