Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use alloc::vec::Vec;
- use core::{arch::asm, ops::Deref, str};
- use x86_64::registers::model_specific::{Efer, EferFlags, LStar, SFMask, Star};
- use ku::{
- allocator::BigAllocator, log::{self, event, level_into_symbol, Level}, memory::mmu::PageTable, process::{ExitCode, MiniContext, RFlags, ResultCode, State, Syscall}, sync::spinlock::SpinlockGuard
- };
- use crate::{
- allocator::MemoryAllocator,
- error::{
- Error::{InvalidArgument, NoPage, Overflow, PermissionDenied, WrongAlignment},
- Result,
- },
- gdt::Gdt,
- log::{debug, error, info, trace, warn},
- memory::{
- self,
- mmu::{PageTableEntry, PageTableFlags},
- Block,
- Page,
- Virt,
- FRAME_ALLOCATOR,
- KERNEL_RW,
- USER,
- USER_READ,
- },
- smp::{Cpu, KERNEL_RSP_OFFSET_IN_CPU},
- };
- use super::{process::TrapContext, Pid, Process, Scheduler, Table};
- use lock_set::{lock_dst, lock_src_dst};
- // Used in docs.
- #[allow(unused)]
- use crate::error::Error;
- /// Инициализация системных вызовов.
- /// Подготавливает процессор к выполнению инструкций
- /// [syscall](https://www.felixcloutier.com/x86/syscall) и
- /// [sysret](https://www.felixcloutier.com/x86/sysret).
- pub(crate) fn init() {
- let syscall_virt = Virt::from_ptr(syscall_trampoline as *const ());
- let mut efer = Efer::read();
- efer.insert(EferFlags::SYSTEM_CALL_EXTENSIONS);
- unsafe { Efer::write(efer) };
- Star::write(
- Gdt::user_code(),
- Gdt::user_data(),
- Gdt::kernel_code(),
- Gdt::kernel_data()
- ).unwrap();
- LStar::write(Virt::from_ptr(syscall_trampoline as *const ()).into());
- SFMask::write(RFlags::ALL.into());
- }
- /// Получает управление при выполнении инструкции
- /// [syscall](https://www.felixcloutier.com/x86/syscall).
- ///
- /// Переключает стек на стек ядра, разрешает прерывания и
- /// передаёт управление в функцию [`syscall()`].
- #[naked]
- extern "C" fn syscall_trampoline() -> ! {
- unsafe {
- asm!(
- "
- mov rbx, rsp
- mov rsp, gs:[{kernel_rsp_offset}]
- sti
- push rbx
- push rcx
- push rax
- mov rcx, r10
- call {syscall}
- ",
- syscall = sym syscall,
- kernel_rsp_offset = const KERNEL_RSP_OFFSET_IN_CPU,
- options(noreturn),
- );
- }
- }
- // ANCHOR: syscall
- /// Выполняет диспетчеризацию системных вызовов по аргументу `number` --- номеру системного вызова.
- ///
- /// Передаёт в функции, реализующие конкретные системные вызовы,
- /// нужную часть аргументов `arg0`--`arg4`.
- /// После выполнения функции конкретного системного вызова,
- /// с помощью функции [`sysret()`] возвращает управление в контекст пользователя,
- /// задаваемый `rip` и `rsp`.
- extern "C" fn syscall(
- // https://wiki.osdev.org/System_V_ABI#x86-64:
- // Parameters to functions are passed in the registers
- // rdi, rsi, rdx, rcx, r8, r9,
- // and further values are passed on the stack in reverse order.
- arg0: usize, // rdi
- arg1: usize, // rsi
- arg2: usize, // rdx
- arg3: usize, // rcx
- arg4: usize, // r8
- _arg5: usize, // r9
- // Stack, push in reverse order.
- number: usize,
- rip: Virt,
- rsp: Virt,
- ) -> ! {
- // ANCHOR_END: syscall
- assert!(
- RFlags::read().contains(RFlags::INTERRUPT_FLAG),
- "enable the interrupts during the system calls",
- );
- let context = MiniContext::new(rip, rsp);
- let process = Table::get(Cpu::current_process().unwrap()).unwrap();
- let syscall_number = Syscall::try_from(number);
- if syscall_number.is_ok() {
- match syscall_number {
- Ok(Syscall::Exit) => {
- exit(process, arg0);
- },
- Ok(Syscall::LogValue) => {
- let log_result = log_value(process, arg0, arg1, arg2, arg3);
- if log_result.is_err() {
- sysret(context, log_result);
- }
- sysret(context, Ok(0));
- },
- Ok(Syscall::SchedYield) => {
- sched_yield(process, context);
- },
- Ok(Syscall::Unmap) => {
- let sysval = unmap(process, arg0, arg1, arg2);
- sysret(context, sysval)
- },
- Ok(Syscall::Map) => {
- let sysval = map(process, arg0, arg1, arg2, arg3);
- sysret(context, sysval)
- },
- Ok(Syscall::CopyMapping) => {
- let sysval = copy_mapping(process, arg0, arg1, arg2, arg3, arg4);
- sysret(context, sysval);
- },
- _ => {}
- }
- }
- sysret(context, Err(Error::InvalidArgument));
- }
- // ANCHOR: sysret
- /// С помощью инструкции [sysret](https://www.felixcloutier.com/x86/sysret)
- /// возвращает управление в контекст пользователя `context`.
- ///
- /// Передаёт пользователю результат системного вызова в виде кода успеха или ошибки и
- /// полезной нагрузки из `result`.
- fn sysret(context: MiniContext, result: Result<usize>) -> ! {
- // ANCHOR_END: sysret
- trace!(%context, ?result, "sysret");
- let rflags = RFlags::default();
- let rdi = match result.clone() {
- Ok(value) => value,
- Err(_) => 0,
- };
- unsafe {
- asm!(
- "
- mov rsp, r8
- xor r8, r8
- xor r9, r9
- xor r10, r10
- xor r12, r12
- xor r13, r13
- xor r14, r14
- xor r15, r15
- xor rbp, rbp
- xor rbx, rbx
- xor rdx, rdx
- xor rsi, rsi
- sysretq
- ",
- in("r11") rflags.into_usize(),
- in("rax") usize::from(ResultCode::from(result.clone())),
- in("rdi") rdi,
- in("rcx") context.rip().into_usize(),
- in("r8") context.rsp().into_usize(),
- options(noreturn)
- );
- }
- }
- // ANCHOR: exit
- /// Выполняет системный вызов
- /// [`lib::syscall::exit(code)`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.exit.html).
- ///
- /// Освобождает слот таблицы процессов и возвращается в контекст ядра,
- /// из которого пользовательский процесс был запущен.
- fn exit(process: SpinlockGuard<Process>, code: usize) -> ! {
- // ANCHOR_END: exit
- log::info!("Exit with code: {}", code);
- Table::free(process.pid()).unwrap();
- Process::sched_yield();
- }
- // ANCHOR: log_value
- /// Выполняет системный вызов
- /// [`lib::syscall::log_value(message, value)`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.log_value.html).
- ///
- /// Логирует строку `message` типа `&str`, заданную началом `start` и длиной `len`,
- /// а также число `value`.
- #[allow(unused_mut)] // TODO: remove before flight.
- fn log_value(
- mut process: SpinlockGuard<Process>,
- level: usize,
- start: usize,
- len: usize,
- value: usize,
- ) -> Result<usize> {
- let end = start.checked_add(len).ok_or(Overflow)?;
- let block = Block::<Virt>::from_index(start, end)?;
- let buf = process.address_space().check_permission::<u8>(block, PageTableFlags::USER_ACCESSIBLE)?;
- let msg = str::from_utf8(buf).map_err(|_|InvalidArgument)?;
- log::info!(
- "{}; value = {}; hex_value = 0x{:X}; pid = {}",
- msg, value, value, process.pid()
- );
- return Ok(0);
- }
- /// Выполняет системный вызов
- /// [`lib::syscall::sched_yield()`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.sched_yield.html).
- ///
- /// Перепланирует процесс в конец очереди готовых к исполнению процессов и
- /// забирает у него CPU функцией [`Process::sched_yield()`],
- /// которая вернёт управление в другой контекст ядра ---
- /// в контекст из которого была вызвана функция [`Process::enter_user_mode()`].
- /// Текущий контекст исходного процесса --- `context` --- записывает в него,
- /// чтобы в дальнейшем в него можно было вернуться через [`Process::enter_user_mode()`].
- #[allow(unused_mut)] // TODO: remove before flight.
- fn sched_yield(mut process: SpinlockGuard<Process>, context: MiniContext) -> ! {
- process.set_context(context);
- let current_pid = process.pid();
- Scheduler::enqueue(current_pid);
- drop(process);
- Process::sched_yield();
- }
- // ANCHOR: exofork
- /// Выполняет системный вызов
- /// [`lib::syscall::exofork()`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.exofork.html).
- ///
- /// Создаёт копию вызывающего процесса `process` и возвращает исходному процессу [`Pid`] копии.
- /// Внутри копии возвращает [`Pid::Current`].
- /// При этом новый процесс создаётся практически без адресного пространства и не готовый к работе.
- /// Поэтому он, в частности, не ставится в очередь планировщика.
- /// Текущий контекст исходного процесса --- `context` --- записывает в копию, чтобы в копии
- /// вернуться туда же, куда происходит возврат из системного вызова для вызывающего процесса.
- #[allow(unused_mut)] // TODO: remove before flight.
- fn exofork(mut process: SpinlockGuard<Process>, context: MiniContext) -> Result<usize> {
- // ANCHOR_END: exofork
- // TODO: your code here.
- unimplemented!();
- }
- // ANCHOR: map
- /// Выполняет системный вызов
- /// [`lib::syscall::map(dst_pid, dst_block, flags)`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.map.html).
- ///
- /// Отображает в памяти процесса, заданного `dst_pid`, блок страниц размера `dst_size` байт
- /// начиная с виртуального адреса `dst_address` с флагами доступа `flags`.
- /// Если `dst_address` равен нулю,
- /// сам выбирает свободный участок адресного пространства размера `dst_size`.
- fn map(
- mut process: SpinlockGuard<Process>,
- dst_pid: usize,
- dst_address: usize,
- dst_size: usize,
- flags: usize,
- ) -> Result<usize> {
- // ANCHOR_END: map
- //debug!(?dst_address);
- if dst_size == 0 {
- return Err(InvalidArgument);
- }
- let dst_block = if dst_address != 0 {
- check_block(dst_address, dst_size)?
- } else {
- if dst_size % Page::SIZE != 0 {
- return Err(WrongAlignment);
- }
- process.address_space().allocate(dst_size, PageTableFlags::USER_ACCESSIBLE)?
- };
- //debug!(?dst_block);
- let flags_option = check_page_flags(flags)?;
- if flags_option.is_none() {
- return Err(InvalidArgument);
- }
- let dst_set = lock_dst(process, dst_pid)?;
- let mut dst_process = match dst_set {
- lock_set::LockSet::Dst { dst } => dst,
- lock_set::LockSet::Same { src_dst } => src_dst,
- lock_set::LockSet::Different { dst, .. } => dst,
- };
- let address_space = dst_process.address_space();
- unsafe {
- address_space.map_block(dst_block, flags_option.unwrap())?;
- }
- drop(dst_process);
- Ok(dst_block.start_address().into_usize())
- }
- // ANCHOR: unmap
- /// Выполняет системный вызов
- /// [`lib::syscall::unmap(dst_pid, dst_block)`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.unmap.html).
- ///
- /// Удаляет из виртуальной памяти целевого процесса `dst_pid` блок страниц
- /// размера `dst_size` байт начиная с виртуального адреса `dst_address`.
- fn unmap(
- process: SpinlockGuard<Process>,
- dst_pid: usize,
- dst_address: usize,
- dst_size: usize,
- ) -> Result<usize> {
- // ANCHOR_END: unmap
- let dst_block = check_block(dst_address, dst_size)?;
- let dst_set = lock_dst(process, dst_pid)?;
- let mut dst_process = match dst_set {
- lock_set::LockSet::Dst { dst } => dst,
- lock_set::LockSet::Same { src_dst } => src_dst,
- lock_set::LockSet::Different { dst, .. } => dst,
- };
- let address_space = dst_process.address_space();
- unsafe {
- address_space.unmap_block(dst_block)?;
- }
- Ok(dst_address)
- }
- // ANCHOR: copy_mapping
- /// Выполняет системный вызов
- /// [`lib::syscall::copy_mapping(dst_pid, src_block, dst_block, flags)`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.copy_mapping.html).
- ///
- /// Создаёт копию отображения виртуальной памяти из вызывающего процесса `process`
- /// в процесс, заданный `dst_pid`.
- /// Исходный диапазон начинается с виртуального адреса `src_address`,
- /// целевой --- с виртуального адреса `dst_address`.
- /// Размер диапазона --- `dst_size` байт.
- ///
- /// В целевом процессе диапазон должен быть отображён с флагами:
- /// - `flags`, если `flags != 0`.
- /// - такими же, как в исходном диапазоне, если `flags == 0`.
- ///
- /// Не допускает целевое отображение с более широким набором флагов, чем исходное.
- /// После выполнения у процессов появляется область
- /// [разделяемой памяти](https://en.wikipedia.org/wiki/Shared_memory).
- fn copy_mapping(
- mut process: SpinlockGuard<Process>,
- dst_pid: usize,
- src_address: usize,
- dst_address: usize,
- dst_size: usize,
- flags: usize,
- ) -> Result<usize> {
- let pte_flags = PageTableFlags::from_bits(flags).ok_or(InvalidArgument)?;
- let mut src_block = check_block(src_address, dst_size)?;
- let mut dst_block = check_block(dst_address, dst_size)?;
- if src_block.count() != dst_block.count() {
- return Err(InvalidArgument);
- }
- let flags_option = check_page_flags(flags)?;
- for page in src_block {
- check_frame(&mut process, page, pte_flags)?;
- }
- let dst_set = lock_src_dst(process, dst_pid)?;
- match dst_set {
- lock_set::LockSet::Same { mut src_dst } => {
- let mut allocator = src_dst.address_space().allocator(pte_flags);
- let intersection = src_block.intersection(dst_block);
- if intersection.is_empty() {
- unsafe {
- allocator.copy_mapping(src_block, dst_block, flags_option)?;
- }
- return Ok(dst_size);
- }
- if intersection == src_block {
- unsafe {
- allocator.copy_mapping(src_block, dst_block, flags_option)?;
- }
- return Ok(dst_size);
- }
- assert!(intersection.count() < src_block.count());
- let tail_count = src_block.count() - intersection.count();
- if src_address < dst_address {
- let src_tail = src_block.tail(tail_count).unwrap();
- let dst_tail = dst_block.tail(tail_count).unwrap();
- unsafe {
- allocator.copy_mapping(src_tail, dst_tail, flags_option)?;
- }
- } else {
- src_block.tail(intersection.count());
- dst_block.tail(intersection.count());
- unsafe {
- allocator.copy_mapping(src_block, dst_block, flags_option)?;
- }
- }
- return Ok(dst_size);
- },
- lock_set::LockSet::Different { mut dst, mut src } => {
- let dst_address_space = dst.address_space();
- let dst_beg = dst_block.start_address();
- let dst_end = dst_block.end_address().unwrap();
- let addr_block = Block::new(dst_beg, dst_end)?;
- if dst_address_space
- .check_permission::<u8>(addr_block, PageTableFlags::USER_ACCESSIBLE)
- .is_ok()
- {
- return Err(PermissionDenied);
- }
- for (src_page, dst_page) in src_block.into_iter().zip(dst_block) {
- let src_pte = check_frame(&mut src, src_page, pte_flags).unwrap();
- unsafe {
- dst_address_space.map_page_to_frame(
- dst_page,
- src_pte.frame().unwrap(),
- flags_option.unwrap_or(src_pte.flags()),
- )?;
- }
- }
- return Ok(dst_size);
- },
- _ => panic!("Unexpected lock set"),
- };
- }
- /// Проверяет, что заданный блок виртуальных страниц `block` отображён в
- /// адресное пространство процесса `process` с корректно заданными флагами `flags`.
- /// Возвращает вектор физических фреймов, в которые отображены эти страницы.
- /// См. также [`check_frame()`].
- fn check_frames<'a>(
- process: &'a SpinlockGuard<Process>,
- block: Block<Page>,
- flags: PageTableFlags,
- ) -> Result<Vec<PageTableEntry, MemoryAllocator<'a>>> {
- unimplemented!();
- // let mut retval = Vec::new_in(process.allocator(PageTableFlags::all()));
- // for page in block {
- // retval.push(check_frame(process, page, flags)?);
- // }
- // return Ok(retval);
- }
- /// Выполняет отображение `src_ptes` в `dst_pages`
- /// в адресное пространство процесса `process`.
- ///
- /// В целевом процессе диапазон должен быть отображён с флагами:
- /// - `flags`, если `flags` --- [`Some`].
- /// - такими же, как в исходном диапазоне, если `flags` --- [`None`].
- ///
- /// Количества элементов в `src_ptes` и `dst_pages` должны совпадать.
- fn map_pages_to_frames(
- process: &SpinlockGuard<Process>,
- src_ptes: Vec<PageTableEntry, MemoryAllocator>,
- dst_pages: Block<Page>,
- flags: Option<PageTableFlags>,
- ) -> Result<()> {
- assert!(src_ptes.len() == dst_pages.count());
- let mut address_space = process.lock_address_space();
- for (pte, dst_page) in src_ptes.into_iter().zip(dst_pages.into_iter()) {
- let effective_flags = match flags {
- Some(f) => {
- if !pte.flags().contains(f) {
- return Err(PermissionDenied);
- }
- f
- },
- None => pte.flags(),
- };
- unsafe {
- address_space.map_page_to_frame(dst_page, pte.frame()?, effective_flags)?;
- }
- }
- return Ok(());
- }
- // ANCHOR: set_state
- /// Выполняет системный вызов
- /// [`lib::syscall::set_state(dst_pid, state)`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.set_state.html).
- ///
- /// Переводит целевой процесс, заданный идентификатором `dst_pid`, в заданное состояние `state`.
- /// Ставит его в очередь планировщика в случае [`State::Runnable`].
- fn set_state(process: SpinlockGuard<Process>, dst_pid: usize, state: usize) -> Result<usize> {
- // ANCHOR_END: set_state
- // TODO: your code here.
- unimplemented!();
- }
- // ANCHOR: set_trap_handler
- /// Выполняет системный вызов
- /// [`lib::syscall::set_trap_handler(dst_pid, trap_handler, trap_stack)`](https://sergey-v-galtsev.gitlab.io/labs-description/doc/lib/syscall/fn.set_trap_handler.html).
- ///
- /// Устанавливает для целевого процесса, заданного идентификатором `dst_pid`,
- /// пользовательский обработчик прерывания с виртуальным адресом `rip` и стеком,
- /// который задаётся блоком виртуальных адресов начиная с `stack_address` и размера `stack_size`.
- /// Стек может быть не выровнен по границе страниц.
- fn set_trap_handler(
- process: SpinlockGuard<Process>,
- dst_pid: usize,
- rip: usize,
- stack_address: usize,
- stack_size: usize,
- ) -> Result<usize> {
- // ANCHOR_END: set_trap_handler
- // TODO: your code here.
- unimplemented!();
- }
- /// Проверяет, что `address` и `size` задают корректно выровненный диапазон страниц,
- /// целиком лежащий внутри одной из
- /// [двух непрерывных половин](https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details)
- /// адресного пространства.
- /// Возвращает блок соответствующих виртуальных страниц.
- fn check_block(address: usize, size: usize) -> Result<Block<Page>> {
- let start = Virt::new(address)?;
- let end = Virt::new(address.checked_add(size).ok_or(Overflow)?)?;
- if start.is_lower_half() != end.is_lower_half() {
- return Err(InvalidArgument);
- }
- let addr_block = Block::new(start, end)?;
- let page_block = addr_block.enclosing();
- if page_block.start_address() != addr_block.start_address() ||
- page_block.end_address() != addr_block.end_address()
- {
- return Err(WrongAlignment);
- }
- if !memory::is_user_block(page_block) {
- return Err(PermissionDenied);
- }
- Ok(page_block)
- }
- /// Проверяет, что заданная виртуальная страница `page` отображена в
- /// адресное пространство процесса `process` с заданными флагами `flags`.
- /// Флаги [`PageTableFlags::COPY_ON_WRITE`] и [`PageTableFlags::WRITABLE`]
- /// при проверке считаются эквивалентными,
- /// а флаг [`PageTableFlags::USER_ACCESSIBLE`] --- обязательно включённым.
- /// Возвращает копию [`PageTableEntry`], с физическим фреймом,
- /// в который она отображена, и флагами исходного отображения.
- ///
- /// Возвращает ошибки:
- /// - [`Error::NoPage`] если страница `page` не отображена.
- /// - [`Error::PermissionDenied`] если страница отображена,
- /// но не со всеми запрошенными флагами.
- fn check_frame(
- process: &mut SpinlockGuard<Process>,
- page: Page,
- flags: PageTableFlags,
- ) -> Result<PageTableEntry> {
- let address_space = process.address_space();
- let path = address_space.mapping().path(page.address());
- let pte = path.get().map_err(|_| NoPage)?;
- if !pte.present() {
- return Err(NoPage);
- }
- if !pte.flags().contains(PageTableFlags::USER_ACCESSIBLE) {
- return Err(PermissionDenied);
- }
- if !pte.flags().contains(flags) {
- return Err(PermissionDenied);
- }
- Ok(*pte)
- }
- /// Проверяет, что `flags` задаёт валидный набор флагов отображения страниц пользователя.
- ///
- /// Возвращает:
- /// - [`None`], если `flags == 0`.
- /// - Входные `flags` в виде [`PageTableFlags`], если `flags != 0`.
- ///
- /// Возвращает ошибки:
- /// - [`Error::InvalidArgument`], если во `flags` установлен бит,
- /// не соответствующий никакому флагу [`PageTableFlags`].
- /// - [`Error::PermissionDenied`], если `flags != 0` и
- /// в них не включён [`PageTableFlags::USER_ACCESSIBLE`].
- fn check_page_flags(flags: usize) -> Result<Option<PageTableFlags>> {
- if flags == 0 {
- return Ok(None);
- }
- let result = PageTableFlags::from_bits(flags);
- if result.is_none() {
- return Err(InvalidArgument);
- }
- if !result.unwrap().contains(PageTableFlags::USER_ACCESSIBLE) {
- return Err(PermissionDenied);
- }
- Ok(result)
- }
- /// Работа с блокировкой одного процесса или парой блокировок двух разных процессов.
- mod lock_set {
- use duplicate::duplicate_item;
- use ku::{
- error::{
- Error::{NoProcess, PermissionDenied},
- Result,
- },
- process::Pid,
- sync::spinlock::SpinlockGuard,
- };
- use tracing::debug;
- use super::super::{Process, Table};
- /// Проверяет, что процесс `src` имеет право модифицировать целевой процесс,
- /// заданный своим идентификатором `dst_pid`.
- /// Целевой процесс может совпадать с `src`.
- ///
- /// Модифицировать можно:
- /// - Либо самого себя, задавая [`Pid::Current`] или явно собственный идентификатор [`Pid::Id`].
- /// - Либо свой непосредственно дочерний процесс, задавая его идентификатор.
- ///
- /// Возвращает блокировку на процесс `dst`.
- pub(super) fn lock_dst(src: SpinlockGuard<Process>, dst_pid: usize) -> Result<LockSet> {
- LockSet::new(src, dst_pid, ProcessSet::Dst)
- }
- /// Проверяет, что процесс `src` имеет право модифицировать целевой процесс,
- /// заданный своим идентификатором `dst_pid`.
- /// Целевой процесс может совпадать с `src`.
- ///
- /// Модифицировать можно:
- /// - Либо самого себя, задавая [`Pid::Current`] или явно собственный идентификатор [`Pid::Id`].
- /// - Либо свой непосредственно дочерний процесс, задавая его идентификатор.
- ///
- /// Возвращает:
- /// - Исходную блокировку на `src`, если `dst_pid` задаёт тот же процесс.
- /// - Блокировки на процессы `src` и `dst`, если это разные процессы.
- ///
- /// Захватывает блокировки в правильном порядке для избежания
- /// [взаимоблокировки](https://en.wikipedia.org/wiki/Deadlock).
- pub(super) fn lock_src_dst(src: SpinlockGuard<Process>, dst_pid: usize) -> Result<LockSet> {
- LockSet::new(src, dst_pid, ProcessSet::SrcDst)
- }
- #[derive(Debug)]
- /// Блокировка одного процесса или пара блокировок двух разных процессов.
- pub(super) enum LockSet<'a> {
- /// Пара блокировок двух разных процессов.
- Different {
- /// Процесс, над которым совершается действие системного вызова.
- dst: SpinlockGuard<'a, Process>,
- /// Процесс, запустивший системный вызов.
- src: SpinlockGuard<'a, Process>,
- },
- /// Блокировка только процесса, над которым совершается действие системного вызова.
- /// Используется когда не нужна блокировка на процесс, запустивший системный вызов.
- Dst {
- /// Процесс, над которым совершается действие системного вызова.
- dst: SpinlockGuard<'a, Process>,
- },
- /// Блокировка одного процесса, который и запустил системный вызов
- /// и одновременно является целевым процессом для этого системного вызова.
- Same {
- /// Процесс, запустивший системный вызов на себя же.
- src_dst: SpinlockGuard<'a, Process>,
- },
- }
- impl<'a> LockSet<'a> {
- // ANCHOR: lock_set
- /// Проверяет, что процесс `src` имеет право модифицировать целевой процесс,
- /// заданный своим идентификатором `dst_pid`.
- /// Целевой процесс может совпадать с `src`.
- ///
- /// Модифицировать можно:
- /// - Либо самого себя, задавая [`Pid::Current`] или явно собственный идентификатор [`Pid::Id`].
- /// - Либо свой непосредственно дочерний процесс, задавая его идентификатор.
- ///
- /// Возвращает:
- /// - Исходную блокировку на `src`, если `dst_pid` задаёт тот же процесс.
- /// - Блокировку на процесс `dst`, если `process_set == ProcessSet::Dst`.
- /// - Блокировки на процессы `src` и `dst`, если это разные процессы.
- ///
- /// Захватывает блокировки в правильном порядке для избежания
- /// [взаимоблокировки](https://en.wikipedia.org/wiki/Deadlock).
- fn new(
- src: SpinlockGuard<Process>,
- dst_pid: usize,
- process_set: ProcessSet,
- ) -> Result<LockSet<'_>> {
- let dst = Pid::from_usize(dst_pid)?;
- match process_set {
- ProcessSet::Dst => {
- if src.pid().into_usize() == dst_pid {
- return Ok(LockSet::Dst { dst: src });
- }
- if dst == Pid::Current {
- return Ok(LockSet::Dst { dst: src });
- }
- let dst_process = Table::get(dst)?;
- if dst_process.parent() != Some(src.pid()) && dst != src.pid() && dst != Pid::Current {
- return Err(PermissionDenied);
- }
- return Ok(LockSet::Dst { dst: dst_process });
- },
- ProcessSet::SrcDst => {
- if src.pid().into_usize() == dst_pid {
- return Ok(LockSet::Same { src_dst: src });
- }
- if dst == Pid::Current {
- return Ok(LockSet::Same { src_dst: src });
- }
- let dst_process = Table::get(dst)?;
- if dst_process.parent() != Some(src.pid()) && dst != src.pid() && dst != Pid::Current {
- return Err(PermissionDenied);
- }
- return Ok(LockSet::Different { dst: dst_process, src: src });
- },
- }
- }
- /// Возвращает процесс, над которым совершается действие системного вызова.
- #[allow(clippy::needless_arbitrary_self_type)]
- #[duplicate_item(
- dst_accessor reference(type);
- [dst] [&'b type];
- [dst_mut] [&'b mut type];
- )]
- pub(super) fn dst_accessor<'b>(
- self: reference([Self]),
- ) -> reference([SpinlockGuard<'a, Process>]) {
- match self {
- LockSet::Same { src_dst } => src_dst,
- LockSet::Different { dst, .. } => dst,
- LockSet::Dst { dst } => dst,
- }
- }
- /// Возвращает процесс, запустивший системный вызов.
- ///
- /// # Panics
- ///
- /// Паникует, если изначально блокировка захватывалась только на целевой процесс.
- #[allow(clippy::needless_arbitrary_self_type)]
- #[allow(dead_code)]
- #[duplicate_item(
- src_accessor reference(type);
- [src] [&'b type];
- [src_mut] [&'b mut type];
- )]
- pub(super) fn src_accessor<'b>(
- self: reference([Self]),
- ) -> reference([SpinlockGuard<'a, Process>]) {
- match self {
- LockSet::Same { src_dst } => src_dst,
- LockSet::Different { src, .. } => src,
- LockSet::Dst { .. } => panic!("only destination process is locked"),
- }
- }
- /// Возвращает `true`, если `src` и `dst` --- это один и тот же процесс.
- ///
- /// # Panics
- ///
- /// Паникует, если изначально блокировка захватывалась только на целевой процесс.
- #[allow(dead_code)]
- pub(super) fn is_same(&self) -> bool {
- match self {
- LockSet::Same { .. } => true,
- LockSet::Different { .. } => false,
- LockSet::Dst { .. } => panic!("only destination process is locked"),
- }
- }
- }
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- /// Указывает какой набор процессов блокировать.
- enum ProcessSet {
- /// Блокировать только процесс, над которым совершается действие системного вызова.
- Dst,
- /// Блокировать оба процесса ---
- /// и процесс, запустивший системный вызов,
- /// и процесс, над которым совершается действие системного вызова.
- SrcDst,
- }
- }
- #[doc(hidden)]
- pub mod test_scaffolding {
- use ku::{process::MiniContext, sync::spinlock::SpinlockGuard};
- use crate::error::Result;
- use super::super::Process;
- pub fn log_value(
- process: SpinlockGuard<Process>,
- level: usize,
- start: usize,
- len: usize,
- value: usize,
- ) -> Result<usize> {
- super::log_value(process, level, start, len, value)
- }
- pub fn exofork(process: SpinlockGuard<Process>) -> Result<usize> {
- super::exofork(process, MiniContext::default())
- }
- pub fn map(
- process: SpinlockGuard<Process>,
- dst_pid: usize,
- dst_address: usize,
- dst_size: usize,
- flags: usize,
- ) -> Result<usize> {
- super::map(process, dst_pid, dst_address, dst_size, flags)
- }
- pub fn unmap(
- process: SpinlockGuard<Process>,
- dst_pid: usize,
- dst_address: usize,
- dst_size: usize,
- ) -> Result<usize> {
- super::unmap(process, dst_pid, dst_address, dst_size)
- }
- pub fn copy_mapping(
- process: SpinlockGuard<Process>,
- dst_pid: usize,
- src_address: usize,
- dst_address: usize,
- dst_size: usize,
- flags: usize,
- ) -> Result<usize> {
- super::copy_mapping(process, dst_pid, src_address, dst_address, dst_size, flags)
- }
- pub fn set_state(
- process: SpinlockGuard<Process>,
- dst_pid: usize,
- state: usize,
- ) -> Result<usize> {
- super::set_state(process, dst_pid, state)
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment