mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
feat: kernel/objects/endpoint: add do_send, do_recv, do_signal
This commit is contained in:
parent
ecb0142b69
commit
46a8ff9691
@ -1,22 +1,28 @@
|
||||
use crate::{objects::*, plat::trap::TrapContextOps};
|
||||
use crate::{
|
||||
objects::*,
|
||||
plat::{irq::IRQ_NUM, trap::TrapContextOps},
|
||||
};
|
||||
use core::{cmp::min, fmt::Debug};
|
||||
use tcb::{ThreadState, EP_QUEUE_LINK_ID};
|
||||
use uapi::{cap::ObjectType, fault::Fault, syscall::*};
|
||||
use uapi::{cap::ObjectType, syscall::*};
|
||||
use utils::{addr::*, array::*, linked_list::Link};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum EndpointState {
|
||||
Idle,
|
||||
Sending,
|
||||
Receiving,
|
||||
Send,
|
||||
Recv,
|
||||
Signal,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EndpointObject {
|
||||
pub queue: Link<TcbObject, EP_QUEUE_LINK_ID>,
|
||||
pub state: EndpointState,
|
||||
pub queue: Link<TcbObject, EP_QUEUE_LINK_ID>,
|
||||
pub signals: usize,
|
||||
}
|
||||
|
||||
const_assert!(usize::BITS as usize >= IRQ_NUM);
|
||||
|
||||
impl KernelObject for EndpointObject {
|
||||
const OBJ_TYPE: ObjectType = ObjectType::Endpoint;
|
||||
}
|
||||
@ -54,80 +60,109 @@ impl<'a> EndpointCap<'a> {
|
||||
self.cap().get().args[1]
|
||||
}
|
||||
|
||||
pub fn signals(&self) -> usize {
|
||||
self.as_object().signals
|
||||
}
|
||||
|
||||
pub fn set_signals(&mut self, signals: usize) {
|
||||
self.as_object_mut().signals = signals;
|
||||
}
|
||||
|
||||
pub fn state(&self) -> EndpointState {
|
||||
self.as_object().state
|
||||
let obj = self.as_object();
|
||||
|
||||
if obj.signals > 0 {
|
||||
return EndpointState::Signal;
|
||||
} else if obj.queue.is_empty() {
|
||||
return EndpointState::Idle;
|
||||
}
|
||||
|
||||
match obj.queue.next().unwrap().state() {
|
||||
ThreadState::Sending => EndpointState::Send,
|
||||
ThreadState::Receiving => EndpointState::Recv,
|
||||
_ => EndpointState::Idle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, state: EndpointState) {
|
||||
self.as_object_mut().state = state;
|
||||
}
|
||||
|
||||
pub fn do_send(&mut self, send: &mut TcbObject) -> SysResult<()> {
|
||||
pub fn do_send(&mut self, send: &mut TcbObject) -> SysResult {
|
||||
match self.state() {
|
||||
EndpointState::Idle | EndpointState::Sending => {
|
||||
send.set_state(ThreadState::Sending);
|
||||
send.set_badge(self.badge());
|
||||
EndpointState::Recv => {
|
||||
debug_assert!(
|
||||
!self.as_object().queue.is_empty(),
|
||||
"[Endpoint] do_send: Receive endpoint queue must not be empty"
|
||||
);
|
||||
|
||||
self.set_state(EndpointState::Sending);
|
||||
self.as_object_mut().queue.append(send);
|
||||
},
|
||||
EndpointState::Receiving => {
|
||||
assert!(!self.as_object().queue.is_empty(), "Receive endpoint queue must not be empty");
|
||||
let badge = self.badge();
|
||||
|
||||
let recv = {
|
||||
let recv = self.as_object_mut().queue.next_mut().unwrap();
|
||||
recv.ep_queue.detach();
|
||||
let recv = self.as_object_mut().queue.next_mut().unwrap();
|
||||
recv.ep_queue.detach();
|
||||
|
||||
// SAFETY: `recv` is detached from the queue, no longer related to `self`
|
||||
unsafe { &mut *(recv as *mut TcbObject) }
|
||||
};
|
||||
|
||||
if self.as_object().queue.is_empty() {
|
||||
self.set_state(EndpointState::Idle);
|
||||
}
|
||||
|
||||
do_ipc_transfer(send, send.get_message_info()?, recv, recv.get_message_info()?, self.badge())?;
|
||||
do_ipc(send, send.get_message_info()?, recv, recv.get_message_info()?, badge)?;
|
||||
|
||||
recv.set_state(ThreadState::Running);
|
||||
recv.schedule_next();
|
||||
},
|
||||
_ => {
|
||||
// no thread wants to recv msg, block sender and append it to queue
|
||||
send.set_state(ThreadState::Sending);
|
||||
self.as_object_mut().queue.append(send);
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_recv(&mut self, recv: &mut TcbObject) -> SysResult<()> {
|
||||
pub fn do_recv(&mut self, recv: &mut TcbObject) -> SysResult {
|
||||
match self.state() {
|
||||
EndpointState::Idle | EndpointState::Receiving => {
|
||||
recv.set_state(ThreadState::Receiving);
|
||||
EndpointState::Send => {
|
||||
debug_assert!(
|
||||
!self.as_object().queue.is_empty(),
|
||||
"[Endpoint] do_recv: Send endpoint queue must not be empty"
|
||||
);
|
||||
|
||||
self.set_state(EndpointState::Receiving);
|
||||
self.as_object_mut().queue.append(recv);
|
||||
},
|
||||
EndpointState::Sending => {
|
||||
assert!(!self.as_object().queue.is_empty(), "Send endpoint queue must not be empty");
|
||||
let badge = self.badge();
|
||||
|
||||
let send = {
|
||||
let send = self.as_object_mut().queue.next_mut().unwrap();
|
||||
send.ep_queue.detach();
|
||||
let send = self.as_object_mut().queue.next_mut().unwrap();
|
||||
send.ep_queue.detach();
|
||||
|
||||
// SAFETY: `send` is detached from the queue, no longer related to `self`
|
||||
unsafe { &mut *(send as *mut TcbObject) }
|
||||
};
|
||||
|
||||
if self.as_object().queue.is_empty() {
|
||||
self.set_state(EndpointState::Idle);
|
||||
}
|
||||
|
||||
do_ipc_transfer(send, send.get_message_info()?, recv, recv.get_message_info()?, self.badge())?;
|
||||
do_ipc(send, send.get_message_info()?, recv, recv.get_message_info()?, badge)?;
|
||||
|
||||
send.set_state(ThreadState::Running);
|
||||
send.schedule_next();
|
||||
},
|
||||
EndpointState::Signal => {
|
||||
do_signal(recv, self.signals())?;
|
||||
},
|
||||
_ => {
|
||||
// no thread sent msg or no signal happens, block receiver and append it to queue
|
||||
recv.set_state(ThreadState::Receiving);
|
||||
self.as_object_mut().queue.append(recv);
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_signal(&mut self, irq: usize) -> SysResult {
|
||||
let signals = self.signals() | (1 << irq);
|
||||
let state = self.state();
|
||||
|
||||
self.set_signals(signals);
|
||||
|
||||
if state == EndpointState::Recv {
|
||||
// do recv immediately
|
||||
self.set_signals(0);
|
||||
|
||||
let recv = self.as_object_mut().queue.next_mut().unwrap();
|
||||
recv.ep_queue.detach();
|
||||
recv.set_state(ThreadState::Running);
|
||||
recv.schedule_next();
|
||||
|
||||
do_signal(recv, signals)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for EndpointCap<'_> {
|
||||
@ -142,7 +177,12 @@ impl Debug for EndpointCap<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_message(send: &mut TcbObject, send_msg: MessageInfo, recv: &mut TcbObject, recv_msg: MessageInfo) -> SysResult<()> {
|
||||
fn copy_message(
|
||||
send: &mut TcbObject,
|
||||
send_msg: MessageInfo,
|
||||
recv: &mut TcbObject,
|
||||
recv_msg: MessageInfo,
|
||||
) -> SysResult<usize> {
|
||||
let send_len = send_msg.length() - if send_msg.transfer_cap() { 2 } else { 0 };
|
||||
let recv_len = recv_msg.length() - if recv_msg.transfer_cap() { 2 } else { 0 };
|
||||
let msg_len = min(send_len, recv_len);
|
||||
@ -163,7 +203,7 @@ fn copy_message(send: &mut TcbObject, send_msg: MessageInfo, recv: &mut TcbObjec
|
||||
recv.trapframe.set_reg(i, send.trapframe.get_reg(i));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(msg_len)
|
||||
}
|
||||
|
||||
fn ipc_get_args(tcb: &TcbObject, idx: usize) -> SysResult<usize> {
|
||||
@ -178,9 +218,14 @@ fn ipc_get_args(tcb: &TcbObject, idx: usize) -> SysResult<usize> {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn transfer_cap(send: &mut TcbObject, send_msg: MessageInfo, recv: &mut TcbObject, recv_msg: MessageInfo) -> SysResult<()> {
|
||||
fn transfer_cap(
|
||||
send: &mut TcbObject,
|
||||
send_msg: MessageInfo,
|
||||
recv: &mut TcbObject,
|
||||
recv_msg: MessageInfo,
|
||||
) -> SysResult<bool> {
|
||||
if !send_msg.transfer_cap() || !recv_msg.transfer_cap() {
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let send_cspace = send.cspace()?;
|
||||
@ -199,24 +244,33 @@ fn transfer_cap(send: &mut TcbObject, send_msg: MessageInfo, recv: &mut TcbObjec
|
||||
dest.override_cap(send_cap.cap.get());
|
||||
send_cap.cap.replace(NullCap::mint());
|
||||
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn do_ipc_transfer(
|
||||
fn do_ipc(
|
||||
send: &mut TcbObject,
|
||||
send_msg: MessageInfo,
|
||||
recv: &mut TcbObject,
|
||||
recv_msg: MessageInfo,
|
||||
_badge: usize,
|
||||
) -> SysResult<()> {
|
||||
if send.fault() == Fault::Null {
|
||||
// do normal transfer
|
||||
copy_message(send, send_msg, recv, recv_msg)?;
|
||||
transfer_cap(send, send_msg, recv, recv_msg)?;
|
||||
badge: usize,
|
||||
) -> SysResult {
|
||||
// do normal transfer
|
||||
|
||||
todo!("do_ipc_transfer: normal transfer: uapi/reply");
|
||||
} else {
|
||||
// do fault transfer
|
||||
todo!("do_ipc_transfer: fault transfer");
|
||||
}
|
||||
let length = copy_message(send, send_msg, recv, recv_msg)?;
|
||||
let transfered = transfer_cap(send, send_msg, recv, recv_msg)?;
|
||||
|
||||
let reply = ReplyInfo::new(ReplyLabel::Message, length, true, transfered, SysError::Ok);
|
||||
recv.trapframe.set_reg(REG_REPLY_INFO, reply.into());
|
||||
recv.trapframe.set_reg(REG_BADGE, badge);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_signal(recv: &mut TcbObject, signals: usize) -> SysResult {
|
||||
let reply = ReplyInfo::new(ReplyLabel::Signal, 2, false, false, SysError::Ok);
|
||||
|
||||
recv.trapframe.set_reg(REG_REPLY_INFO, reply.into());
|
||||
recv.trapframe.set_reg(REG_BADGE, signals);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ pub struct TcbObject {
|
||||
vspace: CapEntry,
|
||||
|
||||
// endpoint
|
||||
badge: usize,
|
||||
buffer: CapEntry,
|
||||
fault: Fault,
|
||||
|
||||
@ -63,7 +62,6 @@ impl TcbObject {
|
||||
trapframe: TrapContext::default(),
|
||||
cspace: CapEntry::new(NullCap::mint()),
|
||||
vspace: CapEntry::new(NullCap::mint()),
|
||||
badge: 0,
|
||||
buffer: CapEntry::new(NullCap::mint()),
|
||||
fault: Fault::Null,
|
||||
state: ThreadState::Inactive,
|
||||
@ -97,14 +95,6 @@ impl TcbObject {
|
||||
self.vspace = vspace;
|
||||
}
|
||||
|
||||
pub fn badge(&self) -> usize {
|
||||
self.badge
|
||||
}
|
||||
|
||||
pub fn set_badge(&mut self, badge: usize) {
|
||||
self.badge = badge;
|
||||
}
|
||||
|
||||
pub fn buffer(&self) -> SysResult<FrameCap> {
|
||||
FrameCap::try_from(&self.buffer)
|
||||
}
|
||||
@ -198,7 +188,6 @@ impl Debug for TcbCap<'_> {
|
||||
f.debug_struct("TcbCap")
|
||||
.field("cspace", &obj.cspace)
|
||||
.field("vspace", &obj.vspace)
|
||||
.field("badge", &obj.badge)
|
||||
.field("buffer", &obj.buffer)
|
||||
.field("fault", &obj.fault)
|
||||
.field("state", &obj.state)
|
||||
|
@ -1,10 +1,10 @@
|
||||
use super::utils::generate_driver;
|
||||
use crate::drivers::irq::IrqPlic;
|
||||
use crate::objects::*;
|
||||
use log::{error, warn};
|
||||
use log::{error, trace, warn};
|
||||
use spin::{lazy::Lazy, Mutex};
|
||||
|
||||
const IRQ_NUM: usize = 32;
|
||||
pub const IRQ_NUM: usize = 32;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum IrqState {
|
||||
@ -84,9 +84,10 @@ impl IrqManager {
|
||||
IRQ_DRIVER.lock().disable(irq);
|
||||
},
|
||||
IrqState::Signal => {
|
||||
// TODO: send signal via endpoint
|
||||
warn!("[IrqManager] Undelivered IRQ: {}", irq);
|
||||
IRQ_DRIVER.lock().disable(irq);
|
||||
trace!("[IrqManager] Dispatching IRQ: {}", irq);
|
||||
let mut cap = EndpointCap::try_from(&self.handler[irq]).unwrap();
|
||||
cap.do_signal(irq).unwrap();
|
||||
return;
|
||||
},
|
||||
IrqState::Ipi => {
|
||||
todo!("Kernel: NO SMP support now");
|
||||
|
@ -140,6 +140,12 @@ impl TryFrom<usize> for MessageInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<usize> for MessageInfo {
|
||||
fn into(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/* ReplyInfo layout:
|
||||
* > +---------+--------+--------+------------+-------+
|
||||
* > | label | length | badged | transfered | error |
|
||||
@ -150,6 +156,8 @@ impl TryFrom<usize> for MessageInfo {
|
||||
* > badged: if badged
|
||||
* > transfered: if cap is transfered
|
||||
* > error: error code
|
||||
* >
|
||||
* > Fault / Signal: badged = false, REG_BADGE = fault detail/signal number
|
||||
*/
|
||||
pub struct ReplyInfo(usize);
|
||||
|
||||
@ -228,3 +236,9 @@ impl TryFrom<usize> for ReplyInfo {
|
||||
(info.label() != ReplyLabel::Invalid).then_ok(info, SysError::Invalid)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<usize> for ReplyInfo {
|
||||
fn into(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user