Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
redox-os
kernel
Commits
7ac5bdba
Verified
Commit
7ac5bdba
authored
Mar 10, 2021
by
4lDO2
🖖
Browse files
WIP: Implement userspace-driven shutdown.
parent
64b2dd23
Changes
9
Hide whitespace changes
Inline
Side-by-side
src/acpi/dmar/mod.rs
View file @
7ac5bdba
...
...
@@ -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"
);
...
...
src/acpi/hpet.rs
View file @
7ac5bdba
...
...
@@ -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"
);
...
...
src/acpi/madt.rs
View file @
7ac5bdba
...
...
@@ -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"
);
...
...
src/acpi/mod.rs
View file @
7ac5bdba
...
...
@@ -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
>
,
...
...
src/arch/x86_64/device/ioapic.rs
View file @
7ac5bdba
...
...
@@ -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
)
}
}
...
...
src/arch/x86_64/stop.rs
View file @
7ac5bdba
#[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
()
{
...
...
src/scheme/acpi.rs
View file @
7ac5bdba
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
};
#[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
::
event
;
use
crate
::
scheme
::
SchemeId
;
use
crate
::
sync
::
WaitCondition
;
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
\n
kstop
\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
;
id
});
if
!
initialized
{
if
!
data_init
||
!
id_init
{
log
::
error!
(
"AcpiScheme::init called multiple times"
);
}
...
...
@@ -68,32 +113,75 @@ 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
));
}
let
fd
=
NEXT_FD
.fetch_add
(
1
,
atomic
::
Ordering
::
Relaxed
);
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
))
?
;
let
data
=
DATA
.r
#
try
()
.ok_or
(
Error
::
new
(
EBADFD
))
?
;
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
());
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
;
}
}
Ok
(
0
)
}
...
...
@@ -101,7 +189,15 @@ 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
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
)
...
...
src/scheme/mod.rs
View file @
7ac5bdba
...
...
@@ -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
();
...
...
src/sync/wait_condition.rs
View file @
7ac5bdba
...
...
@@ -10,7 +10,7 @@ pub struct WaitCondition {
}
impl
WaitCondition
{
pub
fn
new
()
->
WaitCondition
{
pub
const
fn
new
()
->
WaitCondition
{
WaitCondition
{
contexts
:
Mutex
::
new
(
Vec
::
new
())
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment