Added a class which owns GuestRam and manages runtime memory allocations
This commit is contained in:
parent
ffefbd9283
commit
90e1b26a11
@ -4,7 +4,7 @@ use std::thread;
|
||||
use std::fs::File;
|
||||
|
||||
use crate::virtio::{VirtioDeviceOps,VirtioBus,VirtQueue};
|
||||
use crate::memory::GuestRam;
|
||||
use crate::memory::MemoryManager;
|
||||
use crate::vm::Result;
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ impl 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|| {
|
||||
run(queues.pop().unwrap())
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ use std::thread::spawn;
|
||||
use termios::*;
|
||||
|
||||
use crate::virtio::{VirtioDeviceOps,VirtioBus, VirtQueue};
|
||||
use crate::memory::GuestRam;
|
||||
use crate::memory::MemoryManager;
|
||||
use crate::vm::Result;
|
||||
|
||||
const VIRTIO_ID_CONSOLE: u16 = 3;
|
||||
@ -40,7 +40,7 @@ impl VirtioSerial {
|
||||
.register()
|
||||
}
|
||||
|
||||
fn start_console(&self, _memory: GuestRam, q: VirtQueue) {
|
||||
fn start_console(&self, _memory: &MemoryManager, q: VirtQueue) {
|
||||
spawn(move || {
|
||||
loop {
|
||||
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));
|
||||
self.start_console(memory, queues.remove(0));
|
||||
|
||||
@ -208,6 +208,8 @@ impl Terminal {
|
||||
} else {
|
||||
abort_cnt += 1;
|
||||
}
|
||||
} else {
|
||||
println!("n = {}", n);
|
||||
}
|
||||
|
||||
if abort_cnt == 3 {
|
||||
|
116
rust/src/memory/manager.rs
Normal file
116
rust/src/memory/manager.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -1,13 +1,42 @@
|
||||
mod ram;
|
||||
mod manager;
|
||||
mod mmap;
|
||||
mod address;
|
||||
mod allocator;
|
||||
|
||||
pub use self::allocator::SystemAllocator;
|
||||
pub use self::address::AddressRange;
|
||||
pub use self::mmap::Mapping;
|
||||
pub use self::ram::GuestRam;
|
||||
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 KERNEL_CMDLINE_ADDRESS: u64 = 0x20000;
|
||||
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>;
|
||||
|
||||
|
||||
|
@ -31,6 +31,10 @@ impl GuestRam {
|
||||
self.ram_size
|
||||
}
|
||||
|
||||
pub fn region_count(&self) -> usize {
|
||||
self.regions.len()
|
||||
}
|
||||
|
||||
pub fn write_bytes(&self, guest_address: u64, bytes: &[u8]) -> Result<()> {
|
||||
let region = self.find_region(guest_address, bytes.len())?;
|
||||
region.write_bytes(guest_address, bytes)
|
||||
@ -41,7 +45,6 @@ impl GuestRam {
|
||||
region.read_bytes(guest_address, bytes)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn slice(&self, guest_address: u64, size: usize) -> Result<&[u8]> {
|
||||
let region = self.find_region(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>())?;
|
||||
region.write_int(guest_address, val)
|
||||
}
|
||||
|
||||
pub fn read_int<T: Serializable>(&self, guest_address: u64) -> Result<T> {
|
||||
let region = self.find_region(guest_address, mem::size_of::<T>())?;
|
||||
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 {
|
||||
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<()> {
|
||||
let slot = regions.len();
|
||||
let slot = regions.len() as u32;
|
||||
let mr = MemoryRegion::new(base, size)?;
|
||||
kvm.add_memory_region(slot, base, mr.mapping.address(), size)
|
||||
.map_err(|e| Error::new(ErrorKind::RegisterMemoryFailed, e))?;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::sync::{Arc,RwLock};
|
||||
use crate::vm::io::IoDispatcher;
|
||||
use crate::kvm::Kvm;
|
||||
use crate::memory::{GuestRam,AddressRange};
|
||||
use crate::memory::{AddressRange, MemoryManager};
|
||||
use super::{VirtioDevice,VirtioDeviceOps,PciIrq};
|
||||
use super::consts::*;
|
||||
use super::pci::PciBus;
|
||||
@ -10,14 +10,14 @@ use crate::vm::Result;
|
||||
|
||||
pub struct VirtioBus {
|
||||
kvm: Kvm,
|
||||
memory: GuestRam,
|
||||
memory: MemoryManager,
|
||||
io_dispatcher: Arc<IoDispatcher>,
|
||||
pci_bus: Arc<RwLock<PciBus>>,
|
||||
devices: Vec<Arc<RwLock<VirtioDevice>>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
kvm,
|
||||
memory,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::sync::{Arc,RwLock};
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use crate::memory::{GuestRam,AddressRange};
|
||||
use crate::memory::{AddressRange, MemoryManager};
|
||||
use super::bus::VirtioDeviceConfig;
|
||||
use super::VirtQueue;
|
||||
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 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 start(&mut self, memory: GuestRam, queues: Vec<VirtQueue>);
|
||||
fn start(&mut self, memory: &MemoryManager, queues: Vec<VirtQueue>);
|
||||
}
|
||||
|
||||
pub struct VirtioDevice {
|
||||
memory: GuestRam,
|
||||
memory: MemoryManager,
|
||||
vq_config: VirtQueueConfig,
|
||||
common_cfg_mmio: AddressRange,
|
||||
isr_mmio: AddressRange,
|
||||
@ -43,10 +43,10 @@ fn get_hi32(val: u64) -> u32 { (val >> 32) as u32 }
|
||||
|
||||
|
||||
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 {
|
||||
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(),
|
||||
isr_mmio: config.isr_mmio(),
|
||||
notify_mmio: config.notify_mmio(),
|
||||
@ -85,8 +85,8 @@ impl VirtioDevice {
|
||||
let new_bits = val & !self.status;
|
||||
|
||||
if new_bits & VIRTIO_CONFIG_S_DRIVER_OK != 0 {
|
||||
match self.vq_config.create_queues(&self.memory) {
|
||||
Ok(queues) => self.with_ops(|ops| ops.start(self.memory.clone(), queues)),
|
||||
match self.vq_config.create_queues(self.memory.guest_ram()) {
|
||||
Ok(queues) => self.with_ops(|ops| ops.start(&self.memory, queues)),
|
||||
Err(e) => {
|
||||
println!("creating virtqueues failed {}", e);
|
||||
self.status |= VIRTIO_CONFIG_S_NEEDS_RESET;
|
||||
|
@ -8,7 +8,7 @@ use self::io::IoDispatcher;
|
||||
use crate::virtio::VirtioBus;
|
||||
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::*;
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ impl VmConfig {
|
||||
}
|
||||
pub struct Vm {
|
||||
kvm: Kvm,
|
||||
memory: GuestRam,
|
||||
memory: MemoryManager,
|
||||
io_dispatcher: Arc<IoDispatcher>,
|
||||
_virtio: VirtioBus,
|
||||
}
|
||||
@ -80,6 +80,14 @@ static REQUIRED_EXTENSIONS: &[u32] = &[
|
||||
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 {
|
||||
pub fn open(config: VmConfig) -> Result<Vm> {
|
||||
let mut kvm = Kvm::open(&REQUIRED_EXTENSIONS)?;
|
||||
@ -87,16 +95,20 @@ impl Vm {
|
||||
kvm.set_tss_addr(0xFFFbd000)?;
|
||||
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()?;
|
||||
|
||||
let verbose = env::args().any(|arg| arg == "-v");
|
||||
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);
|
||||
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();
|
||||
|
||||
@ -126,8 +138,8 @@ impl Vm {
|
||||
pub fn start(&self) -> Result<()> {
|
||||
let mut handles = Vec::new();
|
||||
for vcpu in self.kvm.get_vcpus() {
|
||||
setup::cpu::setup_protected_mode(&vcpu, KVM_KERNEL_LOAD_ADDRESS + 0x200, &self.memory)?;
|
||||
let mut run_area = KvmRunArea::new(vcpu, self.io_dispatcher.clone())?;
|
||||
setup::cpu::setup_protected_mode(&vcpu, KVM_KERNEL_LOAD_ADDRESS + 0x200, self.memory.guest_ram())?;
|
||||
let mut run_area = KvmRunArea::new(vcpu, shutdown.clone(), self.io_dispatcher.clone())?;
|
||||
let h = thread::spawn(move || run_area.run());
|
||||
handles.push(h);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user