1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/* Copyright (c) [2023] [Syswonder Community]
* [Rukos] is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
//! Physical memory management.
use core::fmt;
#[doc(no_inline)]
pub use memory_addr::{PhysAddr, VirtAddr, PAGE_SIZE_4K};
bitflags::bitflags! {
/// The flags of a physical memory region.
pub struct MemRegionFlags: usize {
/// Readable.
const READ = 1 << 0;
/// Writable.
const WRITE = 1 << 1;
/// Executable.
const EXECUTE = 1 << 2;
/// Device memory. (e.g., MMIO regions)
const DEVICE = 1 << 4;
/// Uncachable memory. (e.g., framebuffer)
const UNCACHED = 1 << 5;
/// Reserved memory, do not use for allocation.
const RESERVED = 1 << 6;
/// Free memory for allocation.
const FREE = 1 << 7;
}
}
impl fmt::Debug for MemRegionFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
/// A physical memory region.
#[derive(Debug)]
pub struct MemRegion {
/// The start physical address of the region.
pub paddr: PhysAddr,
/// The size in bytes of the region.
pub size: usize,
/// The region flags, see [`MemRegionFlags`].
pub flags: MemRegionFlags,
/// The region name, used for identification.
pub name: &'static str,
}
/// Converts a virtual address to a physical address.
///
/// It assumes that there is a linear mapping with the offset
/// [`PHYS_VIRT_OFFSET`], that maps all the physical memory to the virtual
/// space at the address plus the offset. So we have
/// `paddr = vaddr - PHYS_VIRT_OFFSET`.
///
/// [`PHYS_VIRT_OFFSET`]: axconfig::PHYS_VIRT_OFFSET
#[inline]
pub const fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
PhysAddr::from(vaddr.as_usize() - axconfig::PHYS_VIRT_OFFSET)
}
/// Converts a physical address to a virtual address.
///
/// It assumes that there is a linear mapping with the offset
/// [`PHYS_VIRT_OFFSET`], that maps all the physical memory to the virtual
/// space at the address plus the offset. So we have
/// `vaddr = paddr + PHYS_VIRT_OFFSET`.
///
/// [`PHYS_VIRT_OFFSET`]: axconfig::PHYS_VIRT_OFFSET
#[inline]
pub const fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
VirtAddr::from(paddr.as_usize() + axconfig::PHYS_VIRT_OFFSET)
}
/// Returns an iterator over all physical memory regions.
pub fn memory_regions() -> impl Iterator<Item = MemRegion> {
kernel_image_regions().chain(crate::platform::mem::platform_regions())
}
/// Returns the memory regions of the kernel image (code and data sections).
fn kernel_image_regions() -> impl Iterator<Item = MemRegion> {
[
MemRegion {
paddr: virt_to_phys((_stext as usize).into()),
size: _etext as usize - _stext as usize,
flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::EXECUTE,
name: ".text",
},
MemRegion {
paddr: virt_to_phys((_srodata as usize).into()),
size: _erodata as usize - _srodata as usize,
flags: MemRegionFlags::RESERVED | MemRegionFlags::READ,
name: ".rodata",
},
MemRegion {
paddr: virt_to_phys((_sdata as usize).into()),
size: _edata as usize - _sdata as usize,
flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
name: ".data .tdata .tbss .percpu",
},
MemRegion {
paddr: virt_to_phys((boot_stack as usize).into()),
size: boot_stack_top as usize - boot_stack as usize,
flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
name: "boot stack",
},
MemRegion {
paddr: virt_to_phys((_sbss as usize).into()),
size: _ebss as usize - _sbss as usize,
flags: MemRegionFlags::RESERVED | MemRegionFlags::READ | MemRegionFlags::WRITE,
name: ".bss",
},
]
.into_iter()
}
/// Returns the default MMIO memory regions (from [`axconfig::MMIO_REGIONS`]).
#[allow(dead_code)]
pub(crate) fn default_mmio_regions() -> impl Iterator<Item = MemRegion> {
axconfig::MMIO_REGIONS.iter().map(|reg| MemRegion {
paddr: reg.0.into(),
size: reg.1,
flags: MemRegionFlags::RESERVED
| MemRegionFlags::DEVICE
| MemRegionFlags::READ
| MemRegionFlags::WRITE,
name: "mmio",
})
}
/// Returns the default free memory regions (kernel image end to physical memory end).
#[allow(dead_code)]
pub(crate) fn default_free_regions() -> impl Iterator<Item = MemRegion> {
let start = virt_to_phys((_ekernel as usize).into()).align_up_4k();
let end = PhysAddr::from(axconfig::PHYS_MEMORY_END).align_down_4k();
core::iter::once(MemRegion {
paddr: start,
size: end.as_usize() - start.as_usize(),
flags: MemRegionFlags::FREE | MemRegionFlags::READ | MemRegionFlags::WRITE,
name: "free memory",
})
}
/// Fills the `.bss` section with zeros.
#[allow(dead_code)]
pub(crate) fn clear_bss() {
unsafe {
core::slice::from_raw_parts_mut(_sbss as usize as *mut u8, _ebss as usize - _sbss as usize)
.fill(0);
}
}
extern "C" {
fn _stext();
fn _etext();
fn _srodata();
fn _erodata();
fn _sdata();
fn _edata();
fn _sbss();
fn _ebss();
fn _ekernel();
fn boot_stack();
fn boot_stack_top();
}