Added a class which owns GuestRam and manages runtime memory allocations

This commit is contained in:
Bruce Leidl 2019-09-11 15:49:53 -04:00
parent ffefbd9283
commit 90e1b26a11
8 changed files with 194 additions and 24 deletions

View File

@ -4,7 +4,7 @@ use std::thread;
use std::fs::File; use std::fs::File;
use crate::virtio::{VirtioDeviceOps,VirtioBus,VirtQueue}; use crate::virtio::{VirtioDeviceOps,VirtioBus,VirtQueue};
use crate::memory::GuestRam; use crate::memory::MemoryManager;
use crate::vm::Result; use crate::vm::Result;
@ -25,7 +25,7 @@ impl VirtioRandom {
impl VirtioDeviceOps for VirtioRandom { impl VirtioDeviceOps for VirtioRandom {
fn start(&mut self, _memory: GuestRam, mut queues: Vec<VirtQueue>) { fn start(&mut self, _memory: &MemoryManager, mut queues: Vec<VirtQueue>) {
thread::spawn(move|| { thread::spawn(move|| {
run(queues.pop().unwrap()) run(queues.pop().unwrap())
}); });

View File

@ -4,7 +4,7 @@ use std::thread::spawn;
use termios::*; use termios::*;
use crate::virtio::{VirtioDeviceOps,VirtioBus, VirtQueue}; use crate::virtio::{VirtioDeviceOps,VirtioBus, VirtQueue};
use crate::memory::GuestRam; use crate::memory::MemoryManager;
use crate::vm::Result; use crate::vm::Result;
const VIRTIO_ID_CONSOLE: u16 = 3; const VIRTIO_ID_CONSOLE: u16 = 3;
@ -40,7 +40,7 @@ impl VirtioSerial {
.register() .register()
} }
fn start_console(&self, _memory: GuestRam, q: VirtQueue) { fn start_console(&self, _memory: &MemoryManager, q: VirtQueue) {
spawn(move || { spawn(move || {
loop { loop {
q.wait_ready().unwrap(); q.wait_ready().unwrap();
@ -81,7 +81,7 @@ impl VirtioDeviceOps for VirtioSerial {
} }
fn start(&mut self, memory: GuestRam, mut queues: Vec<VirtQueue>) { fn start(&mut self, memory: &MemoryManager, mut queues: Vec<VirtQueue>) {
let mut term = Terminal::create(queues.remove(0)); let mut term = Terminal::create(queues.remove(0));
self.start_console(memory, queues.remove(0)); self.start_console(memory, queues.remove(0));
@ -208,6 +208,8 @@ impl Terminal {
} else { } else {
abort_cnt += 1; abort_cnt += 1;
} }
} else {
println!("n = {}", n);
} }
if abort_cnt == 3 { if abort_cnt == 3 {

116
rust/src/memory/manager.rs Normal file
View File

@ -0,0 +1,116 @@
use std::collections::HashMap;
use std::os::unix::io::RawFd;
use std::sync::{Arc, RwLock};
use crate::memory::{GuestRam, SystemAllocator, Mapping, Error, Result};
use crate::kvm::Kvm;
use crate::system::BitVec;
#[derive(Clone)]
pub struct MemoryManager {
kvm: Kvm,
ram: GuestRam,
device_memory: Arc<RwLock<DeviceMemory>>,
}
impl MemoryManager {
pub fn new(kvm: Kvm, ram: GuestRam, allocator: SystemAllocator) -> Self {
let device_memory = RwLock::new(DeviceMemory::new(ram.region_count(), allocator)).into();
MemoryManager {
kvm, ram, device_memory,
}
}
pub fn guest_ram(&self) -> &GuestRam {
&self.ram
}
pub fn kvm(&self) -> &Kvm {
&self.kvm
}
pub fn register_device_memory(&self, fd: RawFd, size: usize) -> Result<(u64, u32)> {
let mut devmem = self.device_memory.write().unwrap();
devmem.register(self.kvm(), fd, size)
}
pub fn unregister_device_memory(&self, slot: u32) -> Result<()> {
let mut devmem = self.device_memory.write().unwrap();
devmem.unregister(self.kvm(), slot)
}
}
pub struct MemoryRegistration {
guest_addr: u64,
_mapping: Mapping,
}
impl MemoryRegistration {
fn new(guest_addr: u64, mapping: Mapping)-> Self {
MemoryRegistration { guest_addr, _mapping: mapping }
}
}
struct DeviceMemory {
slots: BitVec,
mappings: HashMap<u32, MemoryRegistration>,
allocator: SystemAllocator,
}
impl DeviceMemory {
fn new(ram_region_count: usize, allocator: SystemAllocator) -> DeviceMemory {
let mut slots = BitVec::new();
for i in 0..ram_region_count {
slots.set_bit(i);
}
DeviceMemory {
slots, mappings: HashMap::new(), allocator
}
}
fn register(&mut self, kvm: &Kvm, fd: RawFd, size: usize) -> Result<(u64, u32)> {
let mapping = Mapping::new_from_fd(fd, size)
.map_err(Error::MappingFailed)?;
let (addr, slot) = self.allocate_addr_and_slot(size)?;
if let Err(e) = kvm.add_memory_region(slot, addr, mapping.address(), size) {
self.free_addr_and_slot(addr, slot);
Err(Error::RegisterMemoryFailed(e))
} else {
self.mappings.insert(slot, MemoryRegistration::new(addr, mapping));
Ok((addr >> 12, slot))
}
}
fn unregister(&mut self, kvm: &Kvm, slot: u32) -> Result<()> {
if let Some(registration) = self.mappings.remove(&slot) {
kvm.remove_memory_region(slot)
.map_err(Error::UnregisterMemoryFailed)?;
self.free_addr_and_slot(registration.guest_addr, slot);
}
Ok(())
}
fn allocate_addr_and_slot(&mut self, size: usize) -> Result<(u64, u32)> {
let addr = self.allocator.allocate_device_memory(size)
.ok_or(Error::DeviceMemoryAllocFailed)?;
Ok((addr, self.allocate_slot()))
}
fn free_addr_and_slot(&mut self, addr: u64, slot: u32) {
self.allocator.free_device_memory(addr);
self.free_slot(slot);
}
fn allocate_slot(&mut self) -> u32 {
let slot = self.slots.first_unset();
self.slots.set_bit(slot);
slot as u32
}
fn free_slot(&mut self, slot: u32) {
self.slots.clear_bit(slot as usize)
}
}

View File

@ -1,13 +1,42 @@
mod ram; mod ram;
mod manager;
mod mmap; mod mmap;
mod address; mod address;
mod allocator;
pub use self::allocator::SystemAllocator;
pub use self::address::AddressRange; pub use self::address::AddressRange;
pub use self::mmap::Mapping; pub use self::mmap::Mapping;
pub use self::ram::GuestRam; pub use self::ram::GuestRam;
pub use self::ram::{PCI_MMIO_RESERVED_BASE,HIMEM_BASE}; pub use self::ram::{PCI_MMIO_RESERVED_BASE,HIMEM_BASE};
pub use manager::MemoryManager;
use crate::vm::Error as VmError;
use std::{result, fmt};
pub const KVM_KERNEL_LOAD_ADDRESS: u64 = 0x1000000; pub const KVM_KERNEL_LOAD_ADDRESS: u64 = 0x1000000;
pub const KERNEL_CMDLINE_ADDRESS: u64 = 0x20000; pub const KERNEL_CMDLINE_ADDRESS: u64 = 0x20000;
pub const KERNEL_ZERO_PAGE: u64 = 0x7000; pub const KERNEL_ZERO_PAGE: u64 = 0x7000;
#[derive(Debug)]
pub enum Error {
DeviceMemoryAllocFailed,
MappingFailed(VmError),
RegisterMemoryFailed(VmError),
UnregisterMemoryFailed(VmError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
DeviceMemoryAllocFailed => write!(f, "failed to allocate memory for device"),
MappingFailed(e) => write!(f, "failed to create memory mapping for device memory: {}", e),
RegisterMemoryFailed(e) => write!(f, "failed to register memory for device memory: {}", e),
UnregisterMemoryFailed(e) => write!(f, "failed to unregister memory for device memory: {}", e),
}
}
}
pub type Result<T> = result::Result<T, Error>;

View File

@ -31,6 +31,10 @@ impl GuestRam {
self.ram_size self.ram_size
} }
pub fn region_count(&self) -> usize {
self.regions.len()
}
pub fn write_bytes(&self, guest_address: u64, bytes: &[u8]) -> Result<()> { pub fn write_bytes(&self, guest_address: u64, bytes: &[u8]) -> Result<()> {
let region = self.find_region(guest_address, bytes.len())?; let region = self.find_region(guest_address, bytes.len())?;
region.write_bytes(guest_address, bytes) region.write_bytes(guest_address, bytes)
@ -41,7 +45,6 @@ impl GuestRam {
region.read_bytes(guest_address, bytes) region.read_bytes(guest_address, bytes)
} }
#[allow(dead_code)]
pub fn slice(&self, guest_address: u64, size: usize) -> Result<&[u8]> { pub fn slice(&self, guest_address: u64, size: usize) -> Result<&[u8]> {
let region = self.find_region(guest_address, size)?; let region = self.find_region(guest_address, size)?;
region.slice(guest_address, size) region.slice(guest_address, size)
@ -56,11 +59,19 @@ impl GuestRam {
let region = self.find_region(guest_address, mem::size_of::<T>())?; let region = self.find_region(guest_address, mem::size_of::<T>())?;
region.write_int(guest_address, val) region.write_int(guest_address, val)
} }
pub fn read_int<T: Serializable>(&self, guest_address: u64) -> Result<T> { pub fn read_int<T: Serializable>(&self, guest_address: u64) -> Result<T> {
let region = self.find_region(guest_address, mem::size_of::<T>())?; let region = self.find_region(guest_address, mem::size_of::<T>())?;
region.read_int(guest_address) region.read_int(guest_address)
} }
#[allow(dead_code)]
pub fn end_addr(&self) -> u64 {
self.regions.iter()
.max_by_key(|r| r.guest_range.end())
.map_or(0, |r| r.guest_range.end())
}
pub fn is_valid_range(&self, guest_address: u64, size: usize) -> bool { pub fn is_valid_range(&self, guest_address: u64, size: usize) -> bool {
self.find_region(guest_address, size).is_ok() self.find_region(guest_address, size).is_ok()
} }
@ -73,7 +84,7 @@ impl GuestRam {
} }
fn add_region(regions: &mut Vec<MemoryRegion>, base: u64, size: usize, kvm: &Kvm) -> Result<()> { fn add_region(regions: &mut Vec<MemoryRegion>, base: u64, size: usize, kvm: &Kvm) -> Result<()> {
let slot = regions.len(); let slot = regions.len() as u32;
let mr = MemoryRegion::new(base, size)?; let mr = MemoryRegion::new(base, size)?;
kvm.add_memory_region(slot, base, mr.mapping.address(), size) kvm.add_memory_region(slot, base, mr.mapping.address(), size)
.map_err(|e| Error::new(ErrorKind::RegisterMemoryFailed, e))?; .map_err(|e| Error::new(ErrorKind::RegisterMemoryFailed, e))?;

View File

@ -1,7 +1,7 @@
use std::sync::{Arc,RwLock}; use std::sync::{Arc,RwLock};
use crate::vm::io::IoDispatcher; use crate::vm::io::IoDispatcher;
use crate::kvm::Kvm; use crate::kvm::Kvm;
use crate::memory::{GuestRam,AddressRange}; use crate::memory::{AddressRange, MemoryManager};
use super::{VirtioDevice,VirtioDeviceOps,PciIrq}; use super::{VirtioDevice,VirtioDeviceOps,PciIrq};
use super::consts::*; use super::consts::*;
use super::pci::PciBus; use super::pci::PciBus;
@ -10,14 +10,14 @@ use crate::vm::Result;
pub struct VirtioBus { pub struct VirtioBus {
kvm: Kvm, kvm: Kvm,
memory: GuestRam, memory: MemoryManager,
io_dispatcher: Arc<IoDispatcher>, io_dispatcher: Arc<IoDispatcher>,
pci_bus: Arc<RwLock<PciBus>>, pci_bus: Arc<RwLock<PciBus>>,
devices: Vec<Arc<RwLock<VirtioDevice>>>, devices: Vec<Arc<RwLock<VirtioDevice>>>,
} }
impl VirtioBus { impl VirtioBus {
pub fn new(memory: GuestRam, io_dispatcher: Arc<IoDispatcher>, kvm: Kvm) -> VirtioBus { pub fn new(memory: MemoryManager, io_dispatcher: Arc<IoDispatcher>, kvm: Kvm) -> VirtioBus {
VirtioBus { VirtioBus {
kvm, kvm,
memory, memory,

View File

@ -1,7 +1,7 @@
use std::sync::{Arc,RwLock}; use std::sync::{Arc,RwLock};
use std::ops::DerefMut; use std::ops::DerefMut;
use crate::memory::{GuestRam,AddressRange}; use crate::memory::{AddressRange, MemoryManager};
use super::bus::VirtioDeviceConfig; use super::bus::VirtioDeviceConfig;
use super::VirtQueue; use super::VirtQueue;
use super::config::VirtQueueConfig; use super::config::VirtQueueConfig;
@ -14,11 +14,11 @@ pub trait VirtioDeviceOps: Send+Sync {
fn enable_features(&mut self, bits: u64) -> bool { let _ = bits; true } fn enable_features(&mut self, bits: u64) -> bool { let _ = bits; true }
fn write_config(&mut self, offset: usize, size: usize, val: u64) { let (_,_,_) = (offset, size, val); } fn write_config(&mut self, offset: usize, size: usize, val: u64) { let (_,_,_) = (offset, size, val); }
fn read_config(&mut self, offset: usize, size: usize) -> u64 { let (_,_) = (offset, size); 0 } fn read_config(&mut self, offset: usize, size: usize) -> u64 { let (_,_) = (offset, size); 0 }
fn start(&mut self, memory: GuestRam, queues: Vec<VirtQueue>); fn start(&mut self, memory: &MemoryManager, queues: Vec<VirtQueue>);
} }
pub struct VirtioDevice { pub struct VirtioDevice {
memory: GuestRam, memory: MemoryManager,
vq_config: VirtQueueConfig, vq_config: VirtQueueConfig,
common_cfg_mmio: AddressRange, common_cfg_mmio: AddressRange,
isr_mmio: AddressRange, isr_mmio: AddressRange,
@ -43,10 +43,10 @@ fn get_hi32(val: u64) -> u32 { (val >> 32) as u32 }
impl VirtioDevice { impl VirtioDevice {
pub fn new(memory: GuestRam, config: &VirtioDeviceConfig) -> Result<Arc<RwLock<VirtioDevice>>> { pub fn new(memory: MemoryManager, config: &VirtioDeviceConfig) -> Result<Arc<RwLock<VirtioDevice>>> {
Ok(Arc::new(RwLock::new(VirtioDevice { Ok(Arc::new(RwLock::new(VirtioDevice {
memory: memory.clone(), memory: memory.clone(),
vq_config: VirtQueueConfig::new(&memory.clone(),&config)?, vq_config: VirtQueueConfig::new(memory.guest_ram(),&config)?,
common_cfg_mmio: config.common_cfg_mmio(), common_cfg_mmio: config.common_cfg_mmio(),
isr_mmio: config.isr_mmio(), isr_mmio: config.isr_mmio(),
notify_mmio: config.notify_mmio(), notify_mmio: config.notify_mmio(),
@ -85,8 +85,8 @@ impl VirtioDevice {
let new_bits = val & !self.status; let new_bits = val & !self.status;
if new_bits & VIRTIO_CONFIG_S_DRIVER_OK != 0 { if new_bits & VIRTIO_CONFIG_S_DRIVER_OK != 0 {
match self.vq_config.create_queues(&self.memory) { match self.vq_config.create_queues(self.memory.guest_ram()) {
Ok(queues) => self.with_ops(|ops| ops.start(self.memory.clone(), queues)), Ok(queues) => self.with_ops(|ops| ops.start(&self.memory, queues)),
Err(e) => { Err(e) => {
println!("creating virtqueues failed {}", e); println!("creating virtqueues failed {}", e);
self.status |= VIRTIO_CONFIG_S_NEEDS_RESET; self.status |= VIRTIO_CONFIG_S_NEEDS_RESET;

View File

@ -8,7 +8,7 @@ use self::io::IoDispatcher;
use crate::virtio::VirtioBus; use crate::virtio::VirtioBus;
use crate::devices; use crate::devices;
use crate::memory::{GuestRam,KVM_KERNEL_LOAD_ADDRESS}; use crate::memory::{GuestRam, KVM_KERNEL_LOAD_ADDRESS, MemoryManager, SystemAllocator, AddressRange};
use crate::kvm::*; use crate::kvm::*;
@ -63,7 +63,7 @@ impl VmConfig {
} }
pub struct Vm { pub struct Vm {
kvm: Kvm, kvm: Kvm,
memory: GuestRam, memory: MemoryManager,
io_dispatcher: Arc<IoDispatcher>, io_dispatcher: Arc<IoDispatcher>,
_virtio: VirtioBus, _virtio: VirtioBus,
} }
@ -80,6 +80,14 @@ static REQUIRED_EXTENSIONS: &[u32] = &[
KVM_CAP_IOEVENTFD, KVM_CAP_IOEVENTFD,
]; ];
fn get_base_dev_pfn(mem_size: u64) -> u64 {
// Put device memory at a 2MB boundary after physical memory or 4gb, whichever is greater.
const MB: u64 = 1024 * 1024;
const GB: u64 = 1024 * MB;
let mem_size_round_2mb = (mem_size + 2 * MB - 1) / (2 * MB) * (2 * MB);
std::cmp::max(mem_size_round_2mb, 4 * GB) / 4096
}
impl Vm { impl Vm {
pub fn open(config: VmConfig) -> Result<Vm> { pub fn open(config: VmConfig) -> Result<Vm> {
let mut kvm = Kvm::open(&REQUIRED_EXTENSIONS)?; let mut kvm = Kvm::open(&REQUIRED_EXTENSIONS)?;
@ -87,16 +95,20 @@ impl Vm {
kvm.set_tss_addr(0xFFFbd000)?; kvm.set_tss_addr(0xFFFbd000)?;
kvm.create_pit2()?; kvm.create_pit2()?;
let memory = GuestRam::new(config.ram_size, &kvm)?; let ram = GuestRam::new(config.ram_size, &kvm)?;
let dev_addr_start = get_base_dev_pfn(config.ram_size as u64) * 4096;
let dev_addr_size = u64::max_value() - dev_addr_start;
let allocator = SystemAllocator::new(AddressRange::new(dev_addr_start,dev_addr_size as usize));
let memory = MemoryManager::new(kvm.clone(), ram, allocator);
kvm.create_irqchip()?; kvm.create_irqchip()?;
let verbose = env::args().any(|arg| arg == "-v"); let verbose = env::args().any(|arg| arg == "-v");
let cmdline = KernelCmdLine::new_default(verbose); let cmdline = KernelCmdLine::new_default(verbose);
cmdline.write_to_memory(&memory)?; cmdline.write_to_memory(memory.guest_ram())?;
let path = PathBuf::from(&config.kernel_path); let path = PathBuf::from(&config.kernel_path);
setup::kernel::load_pm_kernel(&memory, &path, cmdline.address(), cmdline.size())?; setup::kernel::load_pm_kernel(memory.guest_ram(), &path, cmdline.address(), cmdline.size())?;
let io_dispatch = IoDispatcher::new(); let io_dispatch = IoDispatcher::new();
@ -126,8 +138,8 @@ impl Vm {
pub fn start(&self) -> Result<()> { pub fn start(&self) -> Result<()> {
let mut handles = Vec::new(); let mut handles = Vec::new();
for vcpu in self.kvm.get_vcpus() { for vcpu in self.kvm.get_vcpus() {
setup::cpu::setup_protected_mode(&vcpu, KVM_KERNEL_LOAD_ADDRESS + 0x200, &self.memory)?; setup::cpu::setup_protected_mode(&vcpu, KVM_KERNEL_LOAD_ADDRESS + 0x200, self.memory.guest_ram())?;
let mut run_area = KvmRunArea::new(vcpu, self.io_dispatcher.clone())?; let mut run_area = KvmRunArea::new(vcpu, shutdown.clone(), self.io_dispatcher.clone())?;
let h = thread::spawn(move || run_area.run()); let h = thread::spawn(move || run_area.run());
handles.push(h); handles.push(h);
} }