blob: dafcb6aedd393d822c4903d85f09d99e62a0c034 [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::sys_net::{
PingGroupRangeFile, ProcSysNetIpv4Conf, ProcSysNetIpv4Neigh, ProcSysNetIpv6Conf,
ProcSysNetIpv6Neigh,
};
use fidl::endpoints::SynchronousProxy;
use fidl_fuchsia_hardware_power_suspend as fhps;
use starnix_core::security;
use starnix_core::task::{CurrentTask, SeccompAction};
use starnix_core::vfs::pseudo::simple_directory::{SimpleDirectory, SimpleDirectoryMutator};
use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps, parse_unsigned_file};
use starnix_core::vfs::pseudo::stub_bytes_file::StubBytesFile;
use starnix_core::vfs::{FileSystemHandle, FsNodeHandle, FsNodeOps, FsString, fs_args, inotify};
use starnix_logging::bug_ref;
use starnix_uapi::auth::{CAP_LAST_CAP, CAP_NET_ADMIN, CAP_SYS_ADMIN, CAP_SYS_RESOURCE};
use starnix_uapi::errors::Errno;
use starnix_uapi::file_mode::mode;
use starnix_uapi::version::{KERNEL_RELEASE, KERNEL_VERSION};
use starnix_uapi::{errno, error};
use std::borrow::Cow;
use std::sync::atomic::Ordering;
use uuid::Uuid;
pub fn sysctl_directory(fs: &FileSystemHandle) -> FsNodeHandle {
let mode = mode!(IFREG, 0o644);
let root_dir = SimpleDirectory::new();
let dir = SimpleDirectoryMutator::new(fs.clone(), root_dir.clone());
dir.subdir("abi", 0o555, |_dir| {
#[cfg(target_arch = "aarch64")]
_dir.entry("swp", StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")), mode);
#[cfg(target_arch = "aarch64")]
_dir.entry(
"tagged_addr_disabled",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/408554469")),
mode,
);
});
dir.subdir("conf", 0o555, |dir| {
dir.subdir("security", 0o555, |dir| {
dir.entry(
"lsm_policy",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
mode,
);
});
});
dir.subdir("crypto", 0o555, |dir| {
dir.entry("fips_enabled", BytesFile::new_node(b"0\n".to_vec()), mode!(IFREG, 0o444));
dir.entry(
"fips_name",
BytesFile::new_node(b"Linux Kernel Cryptographic API\n".to_vec()),
mode!(IFREG, 0o444),
);
dir.entry(
"fips_version",
BytesFile::new_node(|| Ok(format!("{}\n", KERNEL_VERSION))),
mode!(IFREG, 0o444),
);
});
dir.subdir("debug", 0o555, |dir| {
dir.entry(
"exception-trace",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
mode,
);
});
dir.subdir("kernel", 0o555, |dir| {
dir.entry(
"cap_last_cap",
BytesFile::new_node(|| Ok(format!("{}\n", CAP_LAST_CAP))),
mode!(IFREG, 0o444),
);
dir.entry(
"core_pattern",
// TODO(https://fxbug.dev/322873960): Use the core pattern when generating a core dump.
BytesFile::new_node(b"core".to_vec()),
mode,
);
dir.entry(
"core_pipe_limit",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873721")),
mode,
);
dir.entry(
"dmsg_restrict",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874424")),
mode,
);
dir.entry(
"domainname",
StubBytesFile::new_node_with_data(bug_ref!("https://fxbug.dev/322873722"), "(none)"),
mode,
);
dir.entry(
"hostname",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873462")),
mode,
);
dir.entry(
"hung_task_check_count",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873962")),
mode,
);
dir.entry(
"hung_task_panic",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873962")),
mode,
);
dir.entry(
"hung_task_timeout_secs",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873962")),
mode,
);
dir.entry(
"hung_task_warnings",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873962")),
mode,
);
dir.entry("io_uring_disabled", SystemLimitFile::<IoUringDisabled>::new_node(), mode);
dir.entry("io_uring_group", SystemLimitFile::<IoUringGroup>::new_node(), mode);
// TODO(https://fxbug.dev/459771111): Remove this once devices can enter this state
// automatically.
dir.entry(
"force_lowest_power_mode",
SystemLimitFile::<ForceLowestPowerMode>::new_node(),
mode!(IFREG, 0o666),
);
dir.entry(
"modprobe",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874334")),
mode,
);
dir.entry(
"modules_disabled",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874489")),
mode,
);
dir.entry("ngroups_max", BytesFile::new_node(b"65536\n".to_vec()), mode!(IFREG, 0o444));
dir.entry(
"panic_on_oops",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874296")),
mode,
);
dir.entry(
"perf_cpu_time_max_percent",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873262")),
mode,
);
dir.entry(
"perf_event_max_sample_rate",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874604")),
mode,
);
dir.entry(
"perf_event_mlock_kb",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873800")),
mode,
);
dir.entry(
"perf_event_paranoid",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873896")),
mode,
);
dir.entry(
"randomize_va_space",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873202")),
mode,
);
dir.entry(
"sched_child_runs_first",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874709")),
mode,
);
dir.entry(
"sched_latency_ns",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874319")),
mode,
);
dir.entry(
"sched_rt_period_us",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874785")),
mode,
);
dir.entry(
"sched_rt_runtime_us",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874726")),
mode,
);
dir.entry(
"sched_schedstats",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874584")),
mode,
);
dir.entry(
"sched_tunable_scaling",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874666")),
mode,
);
dir.entry(
"sched_wakeup_granularity_ns",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874525")),
mode,
);
dir.entry("sysrq", StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874375")), mode);
dir.entry(
"sched_lib_mask",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
mode,
);
dir.entry(
"sched_lib_name",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
mode,
);
dir.entry("unprivileged_bpf_disabled", UnprivilegedBpfDisabled::new_node(), mode);
dir.entry("dmesg_restrict", DmesgRestrict::new_node(), mode);
dir.entry(
"kptr_restrict",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873878")),
mode,
);
dir.entry(
"osrelease",
BytesFile::new_node(|| Ok(format!("{}\n", KERNEL_RELEASE))),
mode!(IFREG, 0o444),
);
dir.entry("ostype", BytesFile::new_node(b"Linux\n".to_vec()), mode!(IFREG, 0o444));
dir.entry("overflowuid", BytesFile::new_node(b"65534".to_vec()), mode);
dir.entry("overflowgid", BytesFile::new_node(b"65534".to_vec()), mode);
dir.entry("printk", BytesFile::new_node(b"4\t4\t1\t7\n".to_vec()), mode);
dir.entry("pid_max", BytesFile::new_node(b"4194304".to_vec()), mode);
dir.subdir("random", 0o555, |dir| {
// Generate random UUID
let boot_id = Uuid::new_v4().hyphenated().to_string();
dir.entry("boot_id", BytesFile::new_node(boot_id.as_bytes().to_vec()), mode);
dir.entry("entropy_avail", BytesFile::new_node(b"256".to_vec()), mode!(IFREG, 0o444));
});
dir.entry("tainted", KernelTaintedFile::new_node(), mode);
dir.subdir("seccomp", 0o555, |dir| {
dir.entry(
"actions_avail",
BytesFile::new_node(SeccompAction::get_actions_avail_file()),
mode!(IFREG, 0o444),
);
dir.entry("actions_logged", SeccompActionsLogged::new_node(), mode);
});
dir.subdir("yama", 0o555, |dir| {
dir.entry("ptrace_scope", security::yama::PtraceScopeFile::new_node(), mode);
});
});
dir.subdir("lsm", 0o555, |dir| {
dir.entry(
"image_init",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
mode,
);
});
dir.subdir("net", 0o555, sysctl_net_diretory);
dir.entry(
"version",
BytesFile::new_node(|| Ok(format!("{}\n", KERNEL_VERSION))),
mode!(IFREG, 0o444),
);
dir.subdir("vm", 0o555, |dir| {
dir.entry(
"dirty_background_ratio",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874492")),
mode,
);
dir.entry(
"dirty_expire_centisecs",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874237")),
mode,
);
dir.entry(
"drop_caches",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874299")),
mode,
);
dir.entry(
"extra_free_kbytes",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873761")),
mode,
);
dir.entry(
"max_map_count",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874684")),
mode,
);
dir.entry(
"mmap_min_addr",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874526")),
mode,
);
dir.entry(
"mmap_rnd_bits",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874505")),
mode,
);
dir.entry(
"mmap_rnd_compat_bits",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874685")),
mode,
);
dir.entry(
"overcommit_memory",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874159")),
mode,
);
dir.entry(
"page-cluster",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874302")),
mode,
);
dir.entry(
"watermark_scale_factor",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874321")),
mode,
);
});
dir.subdir("user", 0o555, |dir| {
dir.entry(
"max_user_namespaces",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
mode,
);
});
dir.subdir("fs", 0o555, |dir| {
dir.subdir("inotify", 0o555, |dir| {
dir.entry("max_queued_events", inotify::InotifyMaxQueuedEvents::new_node(), mode);
dir.entry("max_user_instances", inotify::InotifyMaxUserInstances::new_node(), mode);
dir.entry("max_user_watches", inotify::InotifyMaxUserWatches::new_node(), mode);
});
dir.entry("pipe-max-size", SystemLimitFile::<PipeMaxSize>::new_node(), mode);
dir.entry(
"protected_hardlinks",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874347")),
mode,
);
dir.entry(
"protected_symlinks",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874764")),
mode,
);
dir.entry(
"suid_dumpable",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874210")),
mode,
);
dir.subdir("selinux", 0o555, |_dir| {});
dir.subdir("verity", 0o555, |dir| {
dir.entry("require_signatures", VerityRequireSignaturesFile::new_node(), mode);
});
});
dir.subdir("security", 0o555, |dir| {
dir.entry(
"lsm_policy",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
mode,
);
});
// TODO: Validate the mode bits are correct.
root_dir.into_node(fs, 0o777)
}
struct VerityRequireSignaturesFile;
impl VerityRequireSignaturesFile {
fn new_node() -> impl FsNodeOps {
BytesFile::new_node(Self)
}
}
impl BytesFileOps for VerityRequireSignaturesFile {
fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
let state_str = std::str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
let clean_state_str = state_str.split('\n').next().unwrap_or("");
if clean_state_str != "0" {
return error!(EINVAL);
}
Ok(())
}
fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
Ok(Cow::Borrowed(&b"0\n"[..]))
}
}
pub fn net_directory(fs: &FileSystemHandle) -> FsNodeHandle {
let dir = SimpleDirectory::new();
dir.edit(fs, |dir| {
dir.entry(
"fib_trie",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873635")),
mode!(IFREG, 0o400),
);
dir.entry(
"if_inet6",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874669")),
mode!(IFREG, 0o444),
);
dir.entry(
"ip_tables_names",
BytesFile::new_node(b"nat\nfilter\nmangle\nraw\n".to_vec()),
mode!(IFREG, 0o644),
);
dir.entry(
"ip6_tables_names",
BytesFile::new_node(b"filter\nmangle\nraw\n".to_vec()),
mode!(IFREG, 0o644),
);
dir.entry(
"psched",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874710")),
mode!(IFREG, 0o444),
);
dir.entry(
"xt_qtaguid",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874322")),
mode!(IFREG, 0o644),
);
dir.subdir("xt_quota", 0o555, |dir| {
dir.entry(
"globalAlert",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322873636")),
mode!(IFREG, 0o444),
);
});
});
// TODO: Validate the mode bits are correct.
dir.into_node(fs, 0o777)
}
struct KernelTaintedFile;
impl KernelTaintedFile {
fn new_node() -> impl FsNodeOps {
BytesFile::new_node(Self)
}
}
impl BytesFileOps for KernelTaintedFile {
fn write(&self, _current_task: &CurrentTask, _data: Vec<u8>) -> Result<(), Errno> {
Ok(())
}
fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
Ok(Cow::Borrowed(&b"0\n"[..]))
}
}
fn sysctl_net_diretory(dir: &SimpleDirectoryMutator) {
let file_mode = mode!(IFREG, 0o644);
let dir_mode = mode!(IFDIR, 0o644);
dir.subdir("core", 0o555, |dir| {
dir.entry(
"bpf_jit_enable",
StubBytesFile::new_node_with_capabilities(
bug_ref!("https://fxbug.dev/322874627"),
CAP_NET_ADMIN,
),
file_mode,
);
dir.entry(
"bpf_jit_kallsyms",
StubBytesFile::new_node_with_capabilities(
bug_ref!("https://fxbug.dev/322874163"),
CAP_NET_ADMIN,
),
file_mode,
);
dir.entry(
"rmem_max",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322906968")),
file_mode,
);
dir.entry("somaxconn", SystemLimitFile::<SoMaxConn>::new_node(), file_mode);
dir.entry(
"wmem_max",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322907334")),
file_mode,
);
dir.entry(
"xfrm_acq_expires",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322907718")),
file_mode,
);
});
dir.subdir("ipv4", 0o555, |dir| {
dir.entry("conf", ProcSysNetIpv4Conf, dir_mode);
dir.entry(
"fwmark_reflect",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874495")),
file_mode,
);
dir.entry(
"ip_forward",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874452")),
file_mode,
);
dir.entry("neigh", ProcSysNetIpv4Neigh, dir_mode);
dir.entry("ping_group_range", PingGroupRangeFile::new_node(), file_mode);
dir.entry(
"tcp_default_init_rwnd",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874199")),
file_mode,
);
dir.entry(
"tcp_fwmark_accept",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874120")),
file_mode,
);
dir.entry(
"tcp_rmem",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874549")),
file_mode,
);
});
dir.subdir("ipv6", 0o555, |dir| {
dir.entry("conf", ProcSysNetIpv6Conf, dir_mode);
dir.entry(
"fwmark_reflect",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874711")),
file_mode,
);
dir.entry("neigh", ProcSysNetIpv6Neigh, dir_mode);
});
dir.subdir("unix", 0o555, |dir| {
dir.entry(
"max_dgram_qlen",
StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322874454")),
file_mode,
);
});
}
struct DmesgRestrict {}
impl DmesgRestrict {
fn new_node() -> impl FsNodeOps {
BytesFile::new_node(Self {})
}
}
impl BytesFileOps for DmesgRestrict {
fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
let restrict = parse_unsigned_file::<u32>(&data)? != 0;
current_task.kernel().restrict_dmesg.store(restrict, Ordering::Relaxed);
Ok(())
}
fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
Ok(format!("{}\n", current_task.kernel().restrict_dmesg.load(Ordering::Relaxed) as u32)
.into_bytes()
.into())
}
}
struct UnprivilegedBpfDisabled {}
impl UnprivilegedBpfDisabled {
fn new_node() -> impl FsNodeOps {
BytesFile::new_node(Self {})
}
}
impl BytesFileOps for UnprivilegedBpfDisabled {
fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
if current_task.kernel().disable_unprivileged_bpf.load(Ordering::Relaxed) == 2 {
return error!(EACCES);
}
let setting = parse_unsigned_file::<u8>(&data)?;
current_task.kernel().disable_unprivileged_bpf.store(setting, Ordering::Relaxed);
Ok(())
}
fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
Ok(format!("{}\n", current_task.kernel().disable_unprivileged_bpf.load(Ordering::Relaxed))
.into_bytes()
.into())
}
}
struct SeccompActionsLogged {}
impl SeccompActionsLogged {
fn new_node() -> impl FsNodeOps {
BytesFile::new_node(Self {})
}
}
impl BytesFileOps for SeccompActionsLogged {
fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
SeccompAction::set_actions_logged(current_task.kernel(), &data)?;
Ok(())
}
fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
Ok(SeccompAction::get_actions_logged(current_task.kernel()).into())
}
}
trait AtomicLimit {
type ValueType: std::str::FromStr + std::fmt::Display;
fn load(current_task: &CurrentTask) -> Self::ValueType;
fn store(current_task: &CurrentTask, value: Self::ValueType);
}
struct SystemLimitFile<T: AtomicLimit + Send + Sync + 'static> {
marker: std::marker::PhantomData<T>,
}
impl<T: AtomicLimit + Send + Sync + 'static> SystemLimitFile<T>
where
<T::ValueType as std::str::FromStr>::Err: std::fmt::Debug,
{
pub fn new_node() -> impl FsNodeOps {
BytesFile::new_node(Self { marker: Default::default() })
}
}
impl<T: AtomicLimit + Send + Sync + 'static> BytesFileOps for SystemLimitFile<T>
where
<T::ValueType as std::str::FromStr>::Err: std::fmt::Debug,
{
fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
// Is CAP_SYS_RESOURCE the correct capability for all these files?
security::check_task_capable(current_task, CAP_SYS_RESOURCE)?;
let value = fs_args::parse(FsString::from(data).as_ref())?;
T::store(current_task, value);
Ok(())
}
fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
Ok(T::load(current_task).to_string().into_bytes().into())
}
}
struct PipeMaxSize;
impl AtomicLimit for PipeMaxSize {
type ValueType = usize;
fn load(current_task: &CurrentTask) -> usize {
current_task.kernel().system_limits.pipe_max_size.load(Ordering::Relaxed)
}
fn store(current_task: &CurrentTask, value: usize) {
current_task.kernel().system_limits.pipe_max_size.store(value, Ordering::Relaxed);
}
}
struct SoMaxConn;
impl AtomicLimit for SoMaxConn {
type ValueType = i32;
fn load(current_task: &CurrentTask) -> i32 {
current_task.kernel().system_limits.socket.max_connections.load(Ordering::Relaxed)
}
fn store(current_task: &CurrentTask, value: i32) {
current_task.kernel().system_limits.socket.max_connections.store(value, Ordering::Relaxed);
}
}
struct IoUringDisabled;
impl AtomicLimit for IoUringDisabled {
type ValueType = i32;
fn load(current_task: &CurrentTask) -> i32 {
current_task.kernel().system_limits.io_uring_disabled.load(Ordering::Relaxed)
}
fn store(current_task: &CurrentTask, value: i32) {
if (0..=2).contains(&value) {
current_task.kernel().system_limits.io_uring_disabled.store(value, Ordering::Relaxed);
}
}
}
struct IoUringGroup;
impl AtomicLimit for IoUringGroup {
type ValueType = i32;
fn load(current_task: &CurrentTask) -> i32 {
current_task.kernel().system_limits.io_uring_group.load(Ordering::Relaxed)
}
fn store(current_task: &CurrentTask, value: i32) {
current_task.kernel().system_limits.io_uring_group.store(value, Ordering::Relaxed);
}
}
/// This file allows developers to force the device to enter its lowest power mode.
///
/// Once the device does this automatically, the file can be removed.
struct ForceLowestPowerMode;
impl ForceLowestPowerMode {
const SERVICE_DIRECTORY: &str = "/svc/fuchsia.hardware.power.suspend.SuspendService";
fn connect_to_device_channel(name: &str) -> zx::Channel {
// It's fine to kernel panic on failures here, as this service is only used for manual
// debugging purposes.
//
// Note that this code will be removed once the device automatically enters lowest power mode.
let mut dir =
std::fs::read_dir(Self::SERVICE_DIRECTORY).expect("Failed to read service directory");
let entry = dir.next().unwrap().expect("Failed to get service entry");
let path = entry
.path()
.join(name)
.into_os_string()
.into_string()
.expect("Failed to create service path");
let (client_channel, server_channel) = zx::Channel::create();
fdio::service_connect(&path, server_channel).expect("Failed to connect to service");
client_channel
}
}
impl AtomicLimit for ForceLowestPowerMode {
type ValueType = bool;
fn load(current_task: &CurrentTask) -> bool {
current_task.kernel().system_limits.force_lowest_power_mode.load(Ordering::Relaxed)
}
fn store(current_task: &CurrentTask, value: bool) {
let proxy = fhps::SuspenderSynchronousProxy::from_channel(Self::connect_to_device_channel(
"suspender",
));
let _ = proxy.force_lowest_power_mode(
&fhps::SuspenderForceLowestPowerModeRequest {
enable: Some(value),
..Default::default()
},
zx::MonotonicInstant::INFINITE,
);
starnix_logging::log_info!("Set force_lowest_power_mode to {value}");
current_task.kernel().system_limits.force_lowest_power_mode.store(value, Ordering::Relaxed);
}
}