Skip to content

[VFIO] Chapter 2, Section 3: BAR areas and mappings - #6114

Open
ShadowCurse wants to merge 4 commits into
firecracker-microvm:feature/vfiofrom
ShadowCurse:vfio_part_2_3
Open

[VFIO] Chapter 2, Section 3: BAR areas and mappings#6114
ShadowCurse wants to merge 4 commits into
firecracker-microvm:feature/vfiofrom
ShadowCurse:vfio_part_2_3

Conversation

@ShadowCurse

Copy link
Copy Markdown
Contributor

Changes

Another set of commits taken from #6055 PR. Here we add logic for calculating mappable areas of BARs (taking into account the holes left by the MSIX tables) and actually doing the actual map operations.

License Acceptance

By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache 2.0 license. For more information on following Developer
Certificate of Origin and signing off your commits, please check
CONTRIBUTING.md.

PR Checklist

  • I have read and understand CONTRIBUTING.md.
  • I have run tools/devtool checkbuild --all to verify that the PR passes
    build checks on all supported architectures.
  • I have run tools/devtool checkstyle to verify that the PR passes the
    automated style checks.
  • I have described what is done in these changes, why they are needed, and
    how they are solving the problem in a clear and encompassing way.
  • I have updated any relevant documentation (both in code and in the docs)
    in the PR.
  • I have mentioned all user-facing changes in CHANGELOG.md.
  • If a specific issue led to this PR, this PR closes the issue.
  • When making API changes, I have followed the
    Runbook for Firecracker API changes.
  • I have tested all new and changed functionalities in unit tests and/or
    integration tests.
  • I have linked an issue to every new TODO.

  • This functionality cannot be added in rust-vmm.

@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.11842% with 103 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.77%. Comparing base (8c8fc63) to head (476cade).

Files with missing lines Patch % Lines
src/vmm/src/vfio.rs 64.23% 103 Missing ⚠️
Additional details and impacted files
@@               Coverage Diff                @@
##           feature/vfio    #6114      +/-   ##
================================================
- Coverage         82.85%   82.77%   -0.09%     
================================================
  Files               280      280              
  Lines             31115    31419     +304     
================================================
+ Hits              25781    26006     +225     
- Misses             5334     5413      +79     
Flag Coverage Δ
5.10-m5n.metal 83.00% <66.11%> (-0.10%) ⬇️
5.10-m6a.metal 82.36% <66.11%> (-0.10%) ⬇️
5.10-m6g.metal 79.87% <66.11%> (-0.07%) ⬇️
5.10-m6i.metal 83.00% <66.11%> (-0.10%) ⬇️
5.10-m7a.metal-48xl 82.35% <66.11%> (-0.11%) ⬇️
5.10-m7g.metal 79.87% <66.11%> (-0.07%) ⬇️
5.10-m7i.metal-24xl 82.97% <66.11%> (-0.11%) ⬇️
5.10-m7i.metal-48xl 82.97% <66.11%> (-0.11%) ⬇️
5.10-m8g.metal-24xl 79.87% <66.11%> (-0.07%) ⬇️
5.10-m8g.metal-48xl 79.87% <66.11%> (-0.07%) ⬇️
5.10-m8i.metal-48xl 82.97% <66.11%> (-0.11%) ⬇️
5.10-m8i.metal-96xl 82.97% <66.11%> (-0.10%) ⬇️
6.1-m5n.metal 83.02% <66.11%> (-0.10%) ⬇️
6.1-m6a.metal 82.39% <66.11%> (-0.10%) ⬇️
6.1-m6g.metal 79.87% <66.11%> (-0.07%) ⬇️
6.1-m6i.metal 83.02% <66.11%> (-0.10%) ⬇️
6.1-m7a.metal-48xl 82.38% <66.11%> (-0.10%) ⬇️
6.1-m7g.metal 79.87% <66.11%> (-0.07%) ⬇️
6.1-m7i.metal-24xl 83.03% <66.11%> (-0.10%) ⬇️
6.1-m7i.metal-48xl 83.03% <66.11%> (-0.11%) ⬇️
6.1-m8g.metal-24xl 79.87% <66.11%> (-0.07%) ⬇️
6.1-m8g.metal-48xl 79.87% <66.11%> (-0.07%) ⬇️
6.1-m8i.metal-48xl 83.03% <66.11%> (-0.11%) ⬇️
6.1-m8i.metal-96xl 83.03% <66.11%> (-0.11%) ⬇️
6.18-m5n.metal 83.02% <66.11%> (-0.10%) ⬇️
6.18-m6a.metal 82.39% <66.11%> (-0.09%) ⬇️
6.18-m6g.metal 79.87% <66.11%> (-0.07%) ⬇️
6.18-m6i.metal 83.02% <66.11%> (-0.11%) ⬇️
6.18-m7a.metal-48xl 82.38% <66.11%> (-0.10%) ⬇️
6.18-m7g.metal 79.87% <66.11%> (-0.07%) ⬇️
6.18-m7i.metal-24xl 83.03% <66.11%> (-0.11%) ⬇️
6.18-m7i.metal-48xl 83.03% <66.11%> (-0.11%) ⬇️
6.18-m8g.metal-24xl 79.87% <66.11%> (-0.07%) ⬇️
6.18-m8g.metal-48xl 79.87% <66.11%> (-0.07%) ⬇️
6.18-m8i.metal-48xl 83.03% <66.11%> (-0.10%) ⬇️
6.18-m8i.metal-96xl 83.03% <66.11%> (-0.10%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ShadowCurse
ShadowCurse force-pushed the vfio_part_2_3 branch 3 times, most recently from 7cfcfd7 to 3acd6b7 Compare August 18, 2026 09:34
Make `arrayvec` non optional as it will be used in the future VFIO
commits.

Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
@ShadowCurse
ShadowCurse marked this pull request as ready for review August 19, 2026 10:41
Add new utility functions for dealing with alignment of addresses.
These will be used in the following commits.

Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
@ShadowCurse ShadowCurse self-assigned this Aug 19, 2026
@ShadowCurse ShadowCurse added the Status: Awaiting review Indicates that a pull request is ready to be reviewed label Aug 19, 2026
Comment thread src/vmm/src/utils/mod.rs
Comment thread src/vmm/src/vfio.rs Outdated
}
}

/// Information about the location of the hole in the bar

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain for future readers what a hole in the bar is?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would VfioBarEmulatedRegion be a more descriptive name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switched to VfioBarEmulatedArea

Comment thread src/vmm/src/vfio.rs Outdated
region_offset: u64,
/// Offset within BAR
offset: u64,
/// Size

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please get rid of these useless comments? We can disable the directive that requires them in this module.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed redundant ones

Comment thread src/vmm/src/vfio.rs Outdated
Comment thread src/vmm/src/vfio.rs
);
} else {
let can_mmap = region_info.flags & VFIO_REGION_INFO_FLAG_MMAP != 0;
if can_mmap || sparse_mmap_cap.is_some() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not extract all this logic into a separate function? It's hard to follow the code when functions are so long.

Comment thread src/vmm/src/vfio.rs Outdated
/// KVM slot assigned to the mapping
slot: u32,
/// Guest physical address
iova: u64,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says GPA and the name says IOVA. How can it be IOVA if we don't map the BARs into the IOMMU?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed to gpa

Comment thread src/vmm/src/vfio.rs Outdated
return Err(VfioError::Mmap);
}

let iova = area.bar_gpa + area.offset;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How's that an IOVA?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed

Comment thread src/vmm/src/vfio.rs Outdated
struct BarArea {
/// BAR gpa
bar_gpa: u64,
/// Offset into VFIO region

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pointing to the beginning of the BAR?
And perhaps vfio_region_offset for the variable?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just add the two when creating the object and store a single offset.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed that code a bit and merged 2 offsets

Comment thread src/vmm/src/vfio.rs Outdated
};

if hva_ptr == libc::MAP_FAILED {
return Err(VfioError::Mmap);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we return the errno?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread src/vmm/src/vfio.rs Outdated
Comment on lines +866 to +867
"Error on unmapping host memory on VFIO device creation failure: {r:?}. \
Continuing with other regions removal."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be useful to log errno here as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread src/vmm/src/vfio.rs Outdated
Comment on lines +903 to +904
"Error on unmapping host memory for BAR in a VFIO device: {r:?}. Continuing \
with other regions removal."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto as above

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread src/vmm/src/vfio.rs Outdated
// Merge into it so we don't register the same MMIO range
// twice.
if let Some(last) = bar_hole_infos.last_mut()
&& last.gpa == pba_gpa

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible for these to span multiple pages? if so, this check is insufficient and there may be an overlap in the mappings. When we compute the areas below, the code already takes care of this, we could adopt the same logic here of deferring the addition only after collecting table and pba and sorting them.

iow something like

                  // Both structures are rounded out to whole host pages, so their page
                  // ranges can overlap even though the structures themselves cannot.
                  // Emit their union: one hole per contiguous run, tagged with what it
                  // covers.
                  let mut holes = [
                      (
                          msix_table_offset,
                          msix_table_size,
                          VfioBarHoleUsageFlags::TABLE,
                      ),
                      (msix_pba_offset, msix_pba_size, VfioBarHoleUsageFlags::PBA),
                  ];
                  holes.sort_unstable_by_key(|(offset, _, _)| *offset);
                  for (offset, size, usage) in holes {
                      if size == 0 {
                          continue;
                      }
                      let gpa = bar_gpa + offset;
                      match bar_hole_infos.last_mut() {
                          Some(last) if gpa < last.gpa + last.size => {
                              last.usage |= usage;
                              last.size = (last.gpa + last.size).max(gpa + size) - last.gpa;
                          }
                          _ => bar_hole_infos.push(VfioBarHole { gpa, size, usage }),
                      }
                  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a separate vfio_add_emulated_area for this. It should handle any overlap

Comment thread src/vmm/src/vfio.rs Outdated
region_size,
));
}
areas.push(BarArea {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we carve the msix/pba holes as well? is it possible for them to land here? iirc the CH code does that but i'm not sure if it's needed or if we can trust the vfio driver to always give us valid mmap-able areas.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expanded the checks to verify these areas do not overlap with msix table/pba

Comment thread src/vmm/src/vfio.rs Outdated
let mut first_gap_size = msix_table_size;
let mut second_gap_offset = msix_pba_offset;
let mut second_gap_size = msix_pba_size;
if second_gap_offset < first_gap_offset {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a unit test covering the swap?

Comment thread src/vmm/src/vfio.rs Outdated

/// Internal type to store areas needed to be mmaped for the device
#[derive(Debug, Clone, Copy)]
struct BarArea {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: a more descriptive name would help. This is only the mmappable area of a BAR, right? MappableBarArea?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment thread src/vmm/src/vfio.rs Outdated
Comment on lines +750 to +798
let mut first_gap_offset = msix_table_offset;
let mut first_gap_size = msix_table_size;
let mut second_gap_offset = msix_pba_offset;
let mut second_gap_size = msix_pba_size;
if second_gap_offset < first_gap_offset {
second_gap_offset = msix_table_offset;
second_gap_size = msix_table_size;
first_gap_offset = msix_pba_offset;
first_gap_size = msix_pba_size;
}
let mut offset = 0;
if first_gap_size != 0 {
let area_size = first_gap_offset - offset;
if area_size != 0 {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: area_size,
prot,
});
}
offset = first_gap_offset + first_gap_size;
}
if second_gap_size != 0 {
if offset < second_gap_offset {
let area_size = second_gap_offset - offset;
if area_size != 0 {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: area_size,
prot,
});
}
}
offset = offset.max(second_gap_offset + second_gap_size);
}
let area_size = region_size - offset;
if area_size != 0 {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: area_size,
prot,
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more readable as a loop. I had to stare at it for a while to understand what was going on.

Suggested change
let mut first_gap_offset = msix_table_offset;
let mut first_gap_size = msix_table_size;
let mut second_gap_offset = msix_pba_offset;
let mut second_gap_size = msix_pba_size;
if second_gap_offset < first_gap_offset {
second_gap_offset = msix_table_offset;
second_gap_size = msix_table_size;
first_gap_offset = msix_pba_offset;
first_gap_size = msix_pba_size;
}
let mut offset = 0;
if first_gap_size != 0 {
let area_size = first_gap_offset - offset;
if area_size != 0 {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: area_size,
prot,
});
}
offset = first_gap_offset + first_gap_size;
}
if second_gap_size != 0 {
if offset < second_gap_offset {
let area_size = second_gap_offset - offset;
if area_size != 0 {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: area_size,
prot,
});
}
}
offset = offset.max(second_gap_offset + second_gap_size);
}
let area_size = region_size - offset;
if area_size != 0 {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: area_size,
prot,
});
}
// Map the region minus the MSI-X holes: walk them in
// ascending order and map whatever precedes each one.
let mut holes = [
(msix_table_offset, msix_table_size),
(msix_pba_offset, msix_pba_size),
];
holes.sort_unstable_by_key(|(offset, _)| *offset);
let mut offset = 0;
for (hole_offset, hole_size) in holes {
if hole_size == 0 {
continue;
}
if offset < hole_offset {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: hole_offset - offset,
prot,
});
}
offset = offset.max(hole_offset + hole_size);
}
if offset < region_size {
areas.push(BarArea {
bar_gpa,
region_offset: region_info.offset,
offset,
size: region_size - offset,
prot,
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, applied the suggestion

Comment thread src/vmm/src/vfio.rs Outdated
let mut bar_idx: u8 = 0;
while bar_idx < NUM_BAR_REGS {
let bar_gpa = bars.get_bar_addr(bar_idx);
if bar_gpa != 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we first check if the bar is used and then get the address like in the loop above (line 531)? get_bar_addr has mutlitple preconditions which make it panic and which are not verified here

Suggested change
if bar_gpa != 0 {
if bars.bars[bar_idx as usize].used() {
let bar_gpa = bars.get_bar_addr(bar_idx);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Calculate areas of BARs which either should be mmapped and provided to
the guest or fully emulated.

Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
After calculating the DMA areas of BARs, map them into the guest. This
involves `mmap`ing the device BAR into Firecracker virtual space, and
creating KVM slots for them.

Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Status: Awaiting review Indicates that a pull request is ready to be reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants