WIP: Implement userspace-driven shutdown.
This commit is contained in:
@@ -5,7 +5,7 @@ use self::drhd::Drhd;
|
||||
use crate::memory::Frame;
|
||||
use crate::paging::{ActivePageTable, PageFlags, PhysicalAddress};
|
||||
|
||||
use super::{find_sdt, load_table, get_sdt_signature};
|
||||
use super::find_sdt;
|
||||
|
||||
pub mod drhd;
|
||||
|
||||
@@ -22,7 +22,6 @@ impl Dmar {
|
||||
pub fn init(active_table: &mut ActivePageTable) {
|
||||
let dmar_sdt = find_sdt("DMAR");
|
||||
let dmar = if dmar_sdt.len() == 1 {
|
||||
load_table(get_sdt_signature(dmar_sdt[0]));
|
||||
Dmar::new(dmar_sdt[0])
|
||||
} else {
|
||||
println!("Unable to find DMAR");
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::memory::Frame;
|
||||
use crate::paging::{ActivePageTable, PhysicalAddress, Page, PageFlags, VirtualAddress};
|
||||
|
||||
use super::sdt::Sdt;
|
||||
use super::{ACPI_TABLE, find_sdt, load_table, get_sdt_signature};
|
||||
use super::{ACPI_TABLE, find_sdt};
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
@@ -38,7 +38,6 @@ impl Hpet {
|
||||
pub fn init(active_table: &mut ActivePageTable) {
|
||||
let hpet_sdt = find_sdt("HPET");
|
||||
let hpet = if hpet_sdt.len() == 1 {
|
||||
load_table(get_sdt_signature(hpet_sdt[0]));
|
||||
Hpet::new(hpet_sdt[0], active_table)
|
||||
} else {
|
||||
println!("Unable to find HPET");
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::memory::{allocate_frames, Frame};
|
||||
use crate::paging::{ActivePageTable, Page, PageFlags, PhysicalAddress, VirtualAddress};
|
||||
|
||||
use super::sdt::Sdt;
|
||||
use super::{find_sdt, load_table, get_sdt_signature};
|
||||
use super::find_sdt;
|
||||
|
||||
use core::intrinsics::{atomic_load, atomic_store};
|
||||
use core::sync::atomic::Ordering;
|
||||
@@ -31,7 +31,6 @@ impl Madt {
|
||||
pub fn init(active_table: &mut ActivePageTable) {
|
||||
let madt_sdt = find_sdt("APIC");
|
||||
let madt = if madt_sdt.len() == 1 {
|
||||
load_table(get_sdt_signature(madt_sdt[0]));
|
||||
Madt::new(madt_sdt[0])
|
||||
} else {
|
||||
println!("Unable to find MADT");
|
||||
|
||||
@@ -81,11 +81,6 @@ pub unsafe fn init(active_table: &mut ActivePageTable, already_supplied_rsdps: O
|
||||
*sdt_ptrs = Some(BTreeMap::new());
|
||||
}
|
||||
|
||||
{
|
||||
let mut order = SDT_ORDER.write();
|
||||
*order = Some(vec!());
|
||||
}
|
||||
|
||||
// Search for RSDP
|
||||
if let Some(rsdp) = RSDP::get_rsdp(active_table, already_supplied_rsdps) {
|
||||
info!("RSDP: {:?}", rsdp);
|
||||
@@ -149,7 +144,6 @@ pub unsafe fn init(active_table: &mut ActivePageTable, already_supplied_rsdps: O
|
||||
|
||||
pub type SdtSignature = (String, [u8; 6], [u8; 8]);
|
||||
pub static SDT_POINTERS: RwLock<Option<BTreeMap<SdtSignature, &'static Sdt>>> = RwLock::new(None);
|
||||
pub static SDT_ORDER: RwLock<Option<Vec<SdtSignature>>> = RwLock::new(None);
|
||||
|
||||
pub fn find_sdt(name: &str) -> Vec<&'static Sdt> {
|
||||
let mut sdts: Vec<&'static Sdt> = vec!();
|
||||
@@ -170,41 +164,6 @@ pub fn get_sdt_signature(sdt: &'static Sdt) -> SdtSignature {
|
||||
(signature, sdt.oem_id, sdt.oem_table_id)
|
||||
}
|
||||
|
||||
pub fn load_table(signature: SdtSignature) {
|
||||
let mut order = SDT_ORDER.write();
|
||||
|
||||
if let Some(ref mut o) = *order {
|
||||
o.push(signature);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_signature_from_index(index: usize) -> Option<SdtSignature> {
|
||||
if let Some(ref order) = *(SDT_ORDER.read()) {
|
||||
if index < order.len() {
|
||||
Some(order[index].clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_index_from_signature(signature: SdtSignature) -> Option<usize> {
|
||||
if let Some(ref order) = *(SDT_ORDER.read()) {
|
||||
let mut i = order.len();
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
|
||||
if order[i] == signature {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub struct Acpi {
|
||||
pub hpet: RwLock<Option<Hpet>>,
|
||||
pub next_ctx: RwLock<u64>,
|
||||
|
||||
@@ -24,9 +24,6 @@ impl IoApicRegs {
|
||||
// offset 0x10
|
||||
unsafe { self.pointer.offset(4) }
|
||||
}
|
||||
fn read_ioregsel(&self) -> u32 {
|
||||
unsafe { ptr::read_volatile::<u32>(self.ioregsel()) }
|
||||
}
|
||||
fn write_ioregsel(&mut self, value: u32) {
|
||||
unsafe { ptr::write_volatile::<u32>(self.ioregsel() as *mut u32, value) }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#[cfg(feature = "acpi")]
|
||||
use crate::acpi;
|
||||
use crate::{
|
||||
context,
|
||||
scheme::acpi,
|
||||
time,
|
||||
};
|
||||
|
||||
use crate::syscall::io::{Io, Pio};
|
||||
|
||||
#[no_mangle]
|
||||
@@ -29,8 +34,22 @@ pub unsafe extern fn kstop() -> ! {
|
||||
// FIXME: RPC into userspace, maybe allowing the kernel ACPI scheme to support e.g. registering
|
||||
// an event queue, so that a special file can only be read/written when about to shut down.
|
||||
|
||||
// #[cfg(feature = "acpi")]
|
||||
// acpi::set_global_s_state(5);
|
||||
#[cfg(feature = "acpi")]
|
||||
{
|
||||
// Tell whatever driver that handles ACPI, that it should enter the S5 state (i.e.
|
||||
// shutdown).
|
||||
acpi::register_kstop();
|
||||
|
||||
// Since this driver is a userspace process, and we do not use any magic like directly
|
||||
// context switching, we have to wait for the userspace driver to complete, with a timeout.
|
||||
//
|
||||
// We switch context, and wait for one second.
|
||||
while time::monotonic().0 < 1 {
|
||||
if ! context::switch() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Magic shutdown code for bochs and qemu (older versions).
|
||||
for c in "Shutdown".bytes() {
|
||||
|
||||
@@ -1,36 +1,41 @@
|
||||
use core::convert::TryInto;
|
||||
use core::fmt::Write;
|
||||
use core::str;
|
||||
use core::sync::atomic::{self, AtomicUsize};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::{EACCES, EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR};
|
||||
use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDWR, O_STAT, O_WRONLY, SEEK_SET, SEEK_CUR, SEEK_END};
|
||||
use syscall::scheme::{calc_seek_offset_usize, Scheme};
|
||||
use syscall::{Error, Result};
|
||||
use syscall::{MODE_DIR, MODE_FILE};
|
||||
|
||||
use spin::{Once, RwLock};
|
||||
use spin::{Mutex, Once, RwLock};
|
||||
|
||||
use crate::acpi::{RXSDT_ENUM, RxsdtEnum};
|
||||
use crate::event;
|
||||
use crate::scheme::SchemeId;
|
||||
use crate::sync::WaitCondition;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct PhysSlice {
|
||||
phys_ptr: usize,
|
||||
len: usize,
|
||||
/// These appear to be identity mapped, so this is technically not needed.
|
||||
virt: usize,
|
||||
}
|
||||
use crate::syscall::data::Stat;
|
||||
use crate::syscall::error::{EACCES, EBADF, EBADFD, EINTR, EINVAL, EISDIR, ENOENT, ENOTDIR, EROFS};
|
||||
use crate::syscall::flag::{
|
||||
EventFlags, EVENT_READ,
|
||||
MODE_CHR, MODE_DIR, MODE_FILE,
|
||||
O_ACCMODE, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, O_STAT, O_SYMLINK,
|
||||
SEEK_SET, SEEK_CUR, SEEK_END,
|
||||
};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::error::{Error, Result};
|
||||
|
||||
/// A scheme used to access the RSDT or XSDT, which is needed for e.g. `acpid` to function.
|
||||
pub struct AcpiScheme;
|
||||
|
||||
struct Handle {
|
||||
offset: usize,
|
||||
kind: HandleKind,
|
||||
stat: bool,
|
||||
}
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum HandleKind {
|
||||
TopLevel,
|
||||
Rxsdt,
|
||||
ShutdownPipe,
|
||||
}
|
||||
|
||||
static HANDLES: RwLock<BTreeMap<usize, Handle>> = RwLock::new(BTreeMap::new());
|
||||
@@ -38,15 +43,50 @@ static NEXT_FD: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
static DATA: Once<Box<[u8]>> = Once::new();
|
||||
|
||||
const TOPLEVEL_CONTENTS: &[u8] = b"rxsdt\nkstop\n";
|
||||
|
||||
static KSTOP_WAITCOND: WaitCondition = WaitCondition::new();
|
||||
static KSTOP_FLAG: Mutex<bool> = Mutex::new(false);
|
||||
|
||||
static SCHEME_ID: Once<SchemeId> = Once::new();
|
||||
|
||||
pub fn register_kstop() -> bool {
|
||||
*KSTOP_FLAG.lock() = true;
|
||||
let mut waiters_awoken = KSTOP_WAITCOND.notify();
|
||||
|
||||
if let Some(&acpi_scheme) = SCHEME_ID.r#try() {
|
||||
let handles = HANDLES.read();
|
||||
|
||||
for (&fd, _) in handles.iter().filter(|(_, handle)| handle.kind == HandleKind::ShutdownPipe) {
|
||||
event::trigger(acpi_scheme, fd, EVENT_READ);
|
||||
waiters_awoken += 1;
|
||||
}
|
||||
} else {
|
||||
log::error!("Calling register_kstop before kernel ACPI scheme was initialized");
|
||||
}
|
||||
|
||||
if waiters_awoken == 0 {
|
||||
log::error!("No userspace ACPI handler was notified when trying to shutdown. This is bad.");
|
||||
// Let the kernel shutdown without ACPI.
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Context switch directly to the waiting context, to avoid annoying timeouts.
|
||||
true
|
||||
}
|
||||
|
||||
impl AcpiScheme {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(id: SchemeId) -> Self {
|
||||
// NOTE: This __must__ be called from the main kernel context, while initializing all
|
||||
// schemes. If it is called by any other context, then all ACPI data will probably not even
|
||||
// be mapped.
|
||||
|
||||
let mut initialized = false;
|
||||
let mut data_init = false;
|
||||
let mut id_init = false;
|
||||
|
||||
DATA.call_once(|| {
|
||||
data_init = true;
|
||||
|
||||
let rsdt_or_xsdt = RXSDT_ENUM
|
||||
.r#try()
|
||||
.expect("expected RXSDT_ENUM to be initialized before AcpiScheme");
|
||||
@@ -58,8 +98,13 @@ impl AcpiScheme {
|
||||
|
||||
Box::from(table)
|
||||
});
|
||||
SCHEME_ID.call_once(|| {
|
||||
id_init = true;
|
||||
|
||||
if !initialized {
|
||||
id
|
||||
});
|
||||
|
||||
if !data_init || !id_init {
|
||||
log::error!("AcpiScheme::init called multiple times");
|
||||
}
|
||||
|
||||
@@ -68,40 +113,91 @@ impl AcpiScheme {
|
||||
}
|
||||
|
||||
impl Scheme for AcpiScheme {
|
||||
fn open(&self, _path: &str, flags: usize, opener_uid: u32, _opener_gid: u32) -> Result<usize> {
|
||||
fn open(&self, path: &str, flags: usize, opener_uid: u32, _opener_gid: u32) -> Result<usize> {
|
||||
let path = path.trim_start_matches('/');
|
||||
|
||||
if opener_uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
if flags & O_DIRECTORY == O_DIRECTORY && flags & O_STAT != O_STAT {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
if flags & O_CREAT == O_CREAT {
|
||||
return Err(Error::new(EROFS));
|
||||
}
|
||||
if flags & O_EXCL == O_EXCL || flags & O_SYMLINK == O_SYMLINK {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if flags & O_ACCMODE != O_RDONLY && flags & O_STAT != O_STAT {
|
||||
return Err(Error::new(EROFS));
|
||||
}
|
||||
let handle_kind = match path {
|
||||
"" => {
|
||||
if flags & O_DIRECTORY != O_DIRECTORY && flags & O_STAT != O_STAT {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
|
||||
HandleKind::TopLevel
|
||||
}
|
||||
"rxsdt" => {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY && flags & O_STAT != O_STAT {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
HandleKind::Rxsdt
|
||||
}
|
||||
"kstop" => {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY && flags & O_STAT != O_STAT {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
HandleKind::ShutdownPipe
|
||||
}
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
|
||||
let fd = NEXT_FD.fetch_add(1, atomic::Ordering::Relaxed);
|
||||
|
||||
let mut handles_guard = HANDLES.write();
|
||||
let handle = Handle { offset: 0 };
|
||||
|
||||
let _ = handles_guard.insert(fd, handle);
|
||||
let _ = handles_guard.insert(fd, Handle {
|
||||
offset: 0,
|
||||
kind: handle_kind,
|
||||
stat: flags & O_STAT == O_STAT,
|
||||
});
|
||||
|
||||
Ok(fd)
|
||||
}
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
if ! HANDLES.read().contains_key(&id) {
|
||||
return Err(Error::new(EBADF));
|
||||
let handles = HANDLES.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match handle.kind {
|
||||
HandleKind::Rxsdt => {
|
||||
let data = DATA.r#try().ok_or(Error::new(EBADFD))?;
|
||||
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = data.len().try_into().unwrap_or(u64::max_value());
|
||||
}
|
||||
HandleKind::TopLevel => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = TOPLEVEL_CONTENTS.len().try_into().unwrap_or(u64::max_value());
|
||||
}
|
||||
HandleKind::ShutdownPipe => {
|
||||
stat.st_mode = MODE_CHR;
|
||||
stat.st_size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
let data = DATA.r#try().ok_or(Error::new(EBADFD))?;
|
||||
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = data.len().try_into().unwrap_or(u64::max_value());
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let data = DATA.r#try().ok_or(Error::new(EBADFD))?;
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let file_len = match handle.kind {
|
||||
HandleKind::Rxsdt => DATA.r#try().ok_or(Error::new(EBADFD))?.len(),
|
||||
HandleKind::ShutdownPipe => 1,
|
||||
HandleKind::TopLevel => TOPLEVEL_CONTENTS.len(),
|
||||
};
|
||||
|
||||
let new_offset = match whence {
|
||||
SEEK_SET => pos as usize,
|
||||
@@ -111,9 +207,9 @@ impl Scheme for AcpiScheme {
|
||||
handle.offset.saturating_add(pos as usize)
|
||||
}
|
||||
SEEK_END => if pos < 0 {
|
||||
data.len().checked_sub((-pos) as usize).ok_or(Error::new(EINVAL))?
|
||||
file_len.checked_sub((-pos) as usize).ok_or(Error::new(EINVAL))?
|
||||
} else {
|
||||
data.len()
|
||||
file_len
|
||||
}
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
@@ -126,7 +222,38 @@ impl Scheme for AcpiScheme {
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let data = DATA.r#try().ok_or(Error::new(EBADFD))?;
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let data = match handle.kind {
|
||||
HandleKind::ShutdownPipe => {
|
||||
let dst_byte = match dst_buf.first_mut() {
|
||||
None => return Ok(0),
|
||||
Some(dst) => if handle.offset >= 1 {
|
||||
return Ok(0)
|
||||
} else {
|
||||
dst
|
||||
},
|
||||
};
|
||||
|
||||
loop {
|
||||
let flag_guard = KSTOP_FLAG.lock();
|
||||
|
||||
if *flag_guard {
|
||||
break;
|
||||
} else if ! KSTOP_WAITCOND.wait(flag_guard, "waiting for kstop") {
|
||||
return Err(Error::new(EINTR));
|
||||
}
|
||||
}
|
||||
|
||||
*dst_byte = 0x42;
|
||||
handle.offset = 1;
|
||||
return Ok(1);
|
||||
}
|
||||
HandleKind::Rxsdt => DATA.r#try().ok_or(Error::new(EBADFD))?,
|
||||
HandleKind::TopLevel => TOPLEVEL_CONTENTS,
|
||||
};
|
||||
|
||||
let src_offset = core::cmp::min(handle.offset, data.len());
|
||||
let src_buf = data
|
||||
@@ -136,14 +263,25 @@ impl Scheme for AcpiScheme {
|
||||
let bytes_to_copy = core::cmp::min(dst_buf.len(), src_buf.len());
|
||||
|
||||
dst_buf[..bytes_to_copy].copy_from_slice(&src_buf[..bytes_to_copy]);
|
||||
handle.offset += bytes_to_copy;
|
||||
|
||||
Ok(bytes_to_copy)
|
||||
}
|
||||
fn fevent(&self, id: usize, flags: EventFlags) -> Result<EventFlags> {
|
||||
let handles = HANDLES.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
fn write(&self, _id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
if ! HANDLES.read().contains_key(&id) {
|
||||
if HANDLES.write().remove(&id).is_none() {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
Ok(0)
|
||||
|
||||
@@ -162,7 +162,7 @@ impl SchemeList {
|
||||
|
||||
// These schemes should only be available on the root
|
||||
#[cfg(all(feature = "acpi", target_arch = "x86_64"))] {
|
||||
self.insert(ns, "kernel/acpi", |_| Arc::new(AcpiScheme::new())).unwrap();
|
||||
self.insert(ns, "kernel/acpi", |scheme_id| Arc::new(AcpiScheme::new(scheme_id))).unwrap();
|
||||
}
|
||||
self.insert(ns, "debug", |scheme_id| Arc::new(DebugScheme::new(scheme_id))).unwrap();
|
||||
self.insert(ns, "initfs", |_| Arc::new(InitFsScheme::new())).unwrap();
|
||||
|
||||
@@ -10,7 +10,7 @@ pub struct WaitCondition {
|
||||
}
|
||||
|
||||
impl WaitCondition {
|
||||
pub fn new() -> WaitCondition {
|
||||
pub const fn new() -> WaitCondition {
|
||||
WaitCondition {
|
||||
contexts: Mutex::new(Vec::new())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user