use crate::{Sv39, Sv39Manager, build_flags, map_portal, parse_flags};
use alloc::alloc::alloc_zeroed;
use core::alloc::Layout;
use tg_kernel_context::{LocalContext, foreign::ForeignContext};
use tg_kernel_vm::{
AddressSpace,
page_table::{MmuMeta, PPN, VAddr, VPN, VmFlags},
};
use tg_task_manage::ProcId;
use xmas_elf::{
ElfFile,
header::{self, HeaderPt2, Machine},
program,
};
pub struct Process {
pub pid: ProcId,
pub context: ForeignContext,
pub address_space: AddressSpace<Sv39, Sv39Manager>,
pub heap_bottom: usize,
pub program_brk: usize,
pub priority: usize,
pub stride: u128,
}
impl Process {
pub fn exec(&mut self, elf: ElfFile) {
let proc = Process::from_elf(elf).unwrap();
self.address_space = proc.address_space;
self.context = proc.context;
self.heap_bottom = proc.heap_bottom;
self.program_brk = proc.program_brk;
}
pub fn fork(&mut self) -> Option<Process> {
let pid = ProcId::new();
let parent_addr_space = &self.address_space;
let mut address_space: AddressSpace<Sv39, Sv39Manager> = AddressSpace::new();
parent_addr_space.cloneself(&mut address_space);
map_portal(&address_space);
let context = self.context.context.clone();
let satp = (8 << 60) | address_space.root_ppn().val();
let foreign_ctx = ForeignContext { context, satp };
Some(Self {
pid,
context: foreign_ctx,
address_space,
heap_bottom: self.heap_bottom,
program_brk: self.program_brk,
priority: self.priority,
stride: self.stride,
})
}
pub fn from_elf(elf: ElfFile) -> Option<Self> {
let entry = match elf.header.pt2 {
HeaderPt2::Header64(pt2)
if pt2.type_.as_type() == header::Type::Executable
&& pt2.machine.as_machine() == Machine::RISC_V =>
{
pt2.entry_point as usize
}
_ => None?,
};
const PAGE_SIZE: usize = 1 << Sv39::PAGE_BITS; const PAGE_MASK: usize = PAGE_SIZE - 1;
let mut address_space = AddressSpace::new();
let mut max_end_va: usize = 0;
for program in elf.program_iter() {
if !matches!(program.get_type(), Ok(program::Type::Load)) {
continue;
}
let off_file = program.offset() as usize; let len_file = program.file_size() as usize; let off_mem = program.virtual_addr() as usize; let end_mem = off_mem + program.mem_size() as usize; assert_eq!(off_file & PAGE_MASK, off_mem & PAGE_MASK);
if end_mem > max_end_va {
max_end_va = end_mem;
}
let mut flags: [u8; 5] = *b"U___V";
if program.flags().is_execute() {
flags[1] = b'X';
}
if program.flags().is_write() {
flags[2] = b'W';
}
if program.flags().is_read() {
flags[3] = b'R';
}
address_space.map(
VAddr::new(off_mem).floor()..VAddr::new(end_mem).ceil(),
&elf.input[off_file..][..len_file],
off_mem & PAGE_MASK,
parse_flags(unsafe { core::str::from_utf8_unchecked(&flags) }).unwrap(),
);
}
let heap_bottom = VAddr::<Sv39>::new(max_end_va).ceil().base().val();
let stack = unsafe {
alloc_zeroed(Layout::from_size_align_unchecked(
2 << Sv39::PAGE_BITS,
1 << Sv39::PAGE_BITS,
))
};
address_space.map_extern(
VPN::new((1 << 26) - 2)..VPN::new(1 << 26),
PPN::new(stack as usize >> Sv39::PAGE_BITS),
build_flags("U_WRV"),
);
map_portal(&address_space);
let mut context = LocalContext::user(entry);
let satp = (8 << 60) | address_space.root_ppn().val();
*context.sp_mut() = 1 << 38;
Some(Self {
pid: ProcId::new(),
context: ForeignContext { context, satp },
address_space,
heap_bottom,
program_brk: heap_bottom,
priority: 16,
stride: 0,
})
}
pub fn change_program_brk(&mut self, size: isize) -> Option<usize> {
let old_brk = self.program_brk;
let new_brk = self.program_brk as isize + size;
if new_brk < self.heap_bottom as isize {
return None;
}
let new_brk = new_brk as usize;
let old_brk_ceil = VAddr::<Sv39>::new(old_brk).ceil();
let new_brk_ceil = VAddr::<Sv39>::new(new_brk).ceil();
if size > 0 {
if new_brk_ceil.val() > old_brk_ceil.val() {
self.address_space
.map(old_brk_ceil..new_brk_ceil, &[], 0, build_flags("U_WRV"));
}
} else if size < 0 {
if old_brk_ceil.val() > new_brk_ceil.val() {
self.address_space.unmap(new_brk_ceil..old_brk_ceil);
}
}
self.program_brk = new_brk;
Some(old_brk)
}
#[inline]
pub fn pass(&self) -> u128 {
const BIG_STRIDE: u128 = 1u128 << 63;
BIG_STRIDE / self.priority as u128
}
#[inline]
pub fn advance_stride(&mut self) {
self.stride = self.stride.saturating_add(self.pass());
}
#[inline]
pub fn set_priority(&mut self, priority: usize) {
self.priority = priority;
}
pub fn mmap(&mut self, start: usize, len: usize, prot: usize) -> bool {
const PAGE_SIZE: usize = 1 << Sv39::PAGE_BITS;
if start & (PAGE_SIZE - 1) != 0 {
return false;
}
if prot == 0 || prot & !0x7 != 0 {
return false;
}
if len == 0 {
return true;
}
let end = if let Some(end) = start.checked_add(len) {
end
} else {
return false;
};
let start_vpn = VAddr::<Sv39>::new(start).floor();
let end_vpn = VAddr::<Sv39>::new(end).ceil();
if self
.address_space
.areas
.iter()
.any(|area| area.start < end_vpn && start_vpn < area.end)
{
return false;
}
let mut flags: [u8; 5] = *b"U___V";
if prot & 0x4 != 0 {
flags[1] = b'X';
}
if prot & 0x2 != 0 {
flags[2] = b'W';
}
if prot & 0x1 != 0 {
flags[3] = b'R';
}
let flags: VmFlags<Sv39> = parse_flags(core::str::from_utf8(&flags).unwrap()).unwrap();
self.address_space.map(start_vpn..end_vpn, &[], 0, flags);
true
}
pub fn munmap(&mut self, start: usize, len: usize) -> bool {
const PAGE_SIZE: usize = 1 << Sv39::PAGE_BITS;
if start & (PAGE_SIZE - 1) != 0 {
return false;
}
if len == 0 {
return true;
}
let end = if let Some(end) = start.checked_add(len) {
end
} else {
return false;
};
let start_vpn = VAddr::<Sv39>::new(start).floor();
let end_vpn = VAddr::<Sv39>::new(end).ceil();
let mut vpn = start_vpn;
while vpn < end_vpn {
if !self
.address_space
.areas
.iter()
.any(|area| area.start <= vpn && vpn < area.end)
{
return false;
}
vpn = vpn + 1;
}
self.address_space.unmap(start_vpn..end_vpn);
true
}
}