소스 검색

Kernel+Userland: Introduce a new way to reboot and poweroff the machine

This change removes the halt and reboot syscalls, and create a new
mechanism to change the power state of the machine.
Instead of how power state was changed until now, put a SysFS node as
writable only for the superuser, that with a defined value, can result
in either reboot or poweroff.
In the future, a power group can be assigned to this node (which will be
the GroupID responsible for power management).

This opens an opportunity to permit to shutdown/reboot without superuser
permissions, so in the future, a userspace daemon can take control of
this node to perform power management operations without superuser
permissions, if we enforce different UserID/GroupID on that node.
Liav A 3 년 전
부모
커밋
8d0dbdeaac

+ 0 - 2
Kernel/API/Syscall.h

@@ -106,7 +106,6 @@ enum class NeedsBigProcessLock {
     S(getsockopt, NeedsBigProcessLock::Yes)                 \
     S(gettid, NeedsBigProcessLock::No)                      \
     S(getuid, NeedsBigProcessLock::Yes)                     \
-    S(halt, NeedsBigProcessLock::Yes)                       \
     S(inode_watcher_add_watch, NeedsBigProcessLock::Yes)    \
     S(inode_watcher_remove_watch, NeedsBigProcessLock::Yes) \
     S(ioctl, NeedsBigProcessLock::Yes)                      \
@@ -144,7 +143,6 @@ enum class NeedsBigProcessLock {
     S(readlink, NeedsBigProcessLock::Yes)                   \
     S(readv, NeedsBigProcessLock::Yes)                      \
     S(realpath, NeedsBigProcessLock::Yes)                   \
-    S(reboot, NeedsBigProcessLock::Yes)                     \
     S(recvfd, NeedsBigProcessLock::Yes)                     \
     S(recvmsg, NeedsBigProcessLock::Yes)                    \
     S(rename, NeedsBigProcessLock::Yes)                     \

+ 1 - 1
Kernel/CMakeLists.txt

@@ -127,6 +127,7 @@ set(KERNEL_SOURCES
     Firmware/ACPI/MultiProcessorParser.cpp
     Firmware/ACPI/Parser.cpp
     Firmware/BIOS.cpp
+    Firmware/PowerStateSwitch.cpp
     Firmware/SysFSFirmware.cpp
     FutexQueue.cpp
     Interrupts/APIC.cpp
@@ -241,7 +242,6 @@ set(KERNEL_SOURCES
     Syscalls/sendfd.cpp
     Syscalls/setpgid.cpp
     Syscalls/setuid.cpp
-    Syscalls/shutdown.cpp
     Syscalls/sigaction.cpp
     Syscalls/socket.cpp
     Syscalls/stat.cpp

+ 99 - 0
Kernel/Firmware/PowerStateSwitch.cpp

@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <Kernel/FileSystem/FileSystem.h>
+#include <Kernel/Firmware/ACPI/Parser.h>
+#include <Kernel/Firmware/PowerStateSwitch.h>
+#include <Kernel/IO.h>
+#include <Kernel/Process.h>
+#include <Kernel/Sections.h>
+#include <Kernel/TTY/ConsoleManagement.h>
+
+namespace Kernel {
+
+mode_t PowerStateSwitchNode::permissions() const
+{
+    return S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
+}
+
+UNMAP_AFTER_INIT NonnullRefPtr<PowerStateSwitchNode> PowerStateSwitchNode::must_create(FirmwareSysFSDirectory& firmware_directory)
+{
+    return adopt_ref_if_nonnull(new (nothrow) PowerStateSwitchNode(firmware_directory)).release_nonnull();
+}
+
+UNMAP_AFTER_INIT PowerStateSwitchNode::PowerStateSwitchNode(FirmwareSysFSDirectory&)
+    : SysFSComponent("power_state")
+{
+}
+
+KResultOr<size_t> PowerStateSwitchNode::write_bytes(off_t offset, size_t count, UserOrKernelBuffer const& data, OpenFileDescription*)
+{
+    if (Checked<off_t>::addition_would_overflow(offset, count))
+        return EOVERFLOW;
+    if (offset > 0)
+        return EINVAL;
+    if (count > 1)
+        return EINVAL;
+
+    char buf[1];
+    TRY(data.read(buf, 1));
+    switch (buf[0]) {
+    case '0':
+        return EINVAL;
+    case '1':
+        reboot();
+        VERIFY_NOT_REACHED();
+    case '2':
+        poweroff();
+        VERIFY_NOT_REACHED();
+    default:
+        return EINVAL;
+    }
+    VERIFY_NOT_REACHED();
+}
+
+void PowerStateSwitchNode::reboot()
+{
+    MutexLocker locker(Process::current().big_lock());
+
+    dbgln("acquiring FS locks...");
+    FileSystem::lock_all();
+    dbgln("syncing mounted filesystems...");
+    FileSystem::sync();
+    dbgln("attempting reboot via ACPI");
+    if (ACPI::is_enabled())
+        ACPI::Parser::the()->try_acpi_reboot();
+    dbgln("attempting reboot via KB Controller...");
+    IO::out8(0x64, 0xFE);
+    dbgln("reboot attempts failed, applications will stop responding.");
+    dmesgln("Reboot can't be completed. It's safe to turn off the computer!");
+    Processor::halt();
+}
+
+void PowerStateSwitchNode::poweroff()
+{
+    MutexLocker locker(Process::current().big_lock());
+
+    ConsoleManagement::the().switch_to_debug();
+
+    dbgln("acquiring FS locks...");
+    FileSystem::lock_all();
+    dbgln("syncing mounted filesystems...");
+    FileSystem::sync();
+    dbgln("attempting system shutdown...");
+    // QEMU Shutdown
+    IO::out16(0x604, 0x2000);
+    // If we're here, the shutdown failed. Try VirtualBox shutdown.
+    IO::out16(0x4004, 0x3400);
+    // VirtualBox shutdown failed. Try Bochs/Old QEMU shutdown.
+    IO::out16(0xb004, 0x2000);
+    dbgln("shutdown attempts failed, applications will stop responding.");
+    dmesgln("Shutdown can't be completed. It's safe to turn off the computer!");
+    Processor::halt();
+}
+
+}

+ 36 - 0
Kernel/Firmware/PowerStateSwitch.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefPtr.h>
+#include <AK/Types.h>
+#include <AK/Vector.h>
+#include <Kernel/FileSystem/SysFS.h>
+#include <Kernel/Firmware/SysFSFirmware.h>
+#include <Kernel/KBuffer.h>
+#include <Kernel/Memory/MappedROM.h>
+#include <Kernel/Memory/Region.h>
+#include <Kernel/PhysicalAddress.h>
+#include <Kernel/VirtualAddress.h>
+
+namespace Kernel {
+
+class PowerStateSwitchNode final : public SysFSComponent {
+public:
+    static NonnullRefPtr<PowerStateSwitchNode> must_create(FirmwareSysFSDirectory&);
+    virtual mode_t permissions() const override;
+    virtual KResultOr<size_t> write_bytes(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) override;
+
+private:
+    PowerStateSwitchNode(FirmwareSysFSDirectory&);
+
+    void reboot();
+    void poweroff();
+};
+
+}

+ 3 - 0
Kernel/Firmware/SysFSFirmware.cpp

@@ -6,6 +6,7 @@
 
 #include <Kernel/Firmware/ACPI/Parser.h>
 #include <Kernel/Firmware/BIOS.h>
+#include <Kernel/Firmware/PowerStateSwitch.h>
 #include <Kernel/Firmware/SysFSFirmware.h>
 #include <Kernel/Sections.h>
 
@@ -24,8 +25,10 @@ void FirmwareSysFSDirectory::create_components()
     VERIFY(!bios_directory_or_error.is_error());
     auto acpi_directory_or_error = ACPI::ACPISysFSDirectory::try_create(*this);
     VERIFY(!acpi_directory_or_error.is_error());
+    auto power_state_switch_node = PowerStateSwitchNode::must_create(*this);
     m_components.append(bios_directory_or_error.release_value());
     m_components.append(acpi_directory_or_error.release_value());
+    m_components.append(power_state_switch_node);
 }
 
 UNMAP_AFTER_INIT FirmwareSysFSDirectory::FirmwareSysFSDirectory()

+ 0 - 2
Kernel/Process.h

@@ -388,8 +388,6 @@ public:
     KResultOr<FlatPtr> sys$kill_thread(pid_t tid, int signal);
     KResultOr<FlatPtr> sys$rename(Userspace<const Syscall::SC_rename_params*>);
     KResultOr<FlatPtr> sys$mknod(Userspace<const Syscall::SC_mknod_params*>);
-    KResultOr<FlatPtr> sys$halt();
-    KResultOr<FlatPtr> sys$reboot();
     KResultOr<FlatPtr> sys$realpath(Userspace<const Syscall::SC_realpath_params*>);
     KResultOr<FlatPtr> sys$getrandom(Userspace<void*>, size_t, unsigned int);
     KResultOr<FlatPtr> sys$getkeymap(Userspace<const Syscall::SC_getkeymap_params*>);

+ 0 - 61
Kernel/Syscalls/shutdown.cpp

@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <Kernel/FileSystem/FileSystem.h>
-#include <Kernel/Firmware/ACPI/Parser.h>
-#include <Kernel/IO.h>
-#include <Kernel/Process.h>
-#include <Kernel/TTY/ConsoleManagement.h>
-
-namespace Kernel {
-
-KResultOr<FlatPtr> Process::sys$reboot()
-{
-    VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this)
-    if (!is_superuser())
-        return EPERM;
-
-    REQUIRE_NO_PROMISES;
-
-    dbgln("acquiring FS locks...");
-    FileSystem::lock_all();
-    dbgln("syncing mounted filesystems...");
-    FileSystem::sync();
-    dbgln("attempting reboot via ACPI");
-    if (ACPI::is_enabled())
-        ACPI::Parser::the()->try_acpi_reboot();
-    dbgln("attempting reboot via KB Controller...");
-    IO::out8(0x64, 0xFE);
-
-    return 0;
-}
-
-KResultOr<FlatPtr> Process::sys$halt()
-{
-    VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this)
-    if (!is_superuser())
-        return EPERM;
-
-    REQUIRE_NO_PROMISES;
-    ConsoleManagement::the().switch_to_debug();
-
-    dbgln("acquiring FS locks...");
-    FileSystem::lock_all();
-    dbgln("syncing mounted filesystems...");
-    FileSystem::sync();
-    dbgln("attempting system shutdown...");
-    // QEMU Shutdown
-    IO::out16(0x604, 0x2000);
-    // If we're here, the shutdown failed. Try VirtualBox shutdown.
-    IO::out16(0x4004, 0x3400);
-    // VirtualBox shutdown failed. Try Bochs/Old QEMU shutdown.
-    IO::out16(0xb004, 0x2000);
-    dbgln("shutdown attempts failed, applications will stop responding.");
-    dmesgln("Shutdown can't be completed. It's safe to turn off the computer!");
-    Processor::halt();
-}
-
-}

+ 0 - 12
Userland/Libraries/LibC/unistd.cpp

@@ -715,18 +715,6 @@ int fsync(int fd)
     __RETURN_WITH_ERRNO(rc, rc, -1);
 }
 
-int halt()
-{
-    int rc = syscall(SC_halt);
-    __RETURN_WITH_ERRNO(rc, rc, -1);
-}
-
-int reboot()
-{
-    int rc = syscall(SC_reboot);
-    __RETURN_WITH_ERRNO(rc, rc, -1);
-}
-
 int mount(int source_fd, const char* target, const char* fs_type, int flags)
 {
     if (!target || !fs_type) {

+ 0 - 2
Userland/Libraries/LibC/unistd.h

@@ -108,8 +108,6 @@ int chown(const char* pathname, uid_t, gid_t);
 int fchown(int fd, uid_t, gid_t);
 int ftruncate(int fd, off_t length);
 int truncate(const char* path, off_t length);
-int halt();
-int reboot();
 int mount(int source_fd, const char* target, const char* fs_type, int flags);
 int umount(const char* mountpoint);
 int pledge(const char* promises, const char* execpromises);

+ 10 - 2
Userland/Utilities/reboot.cpp

@@ -1,16 +1,24 @@
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
 
 int main(int, char**)
 {
-    if (reboot() < 0) {
-        perror("reboot");
+    int power_state_switch_node = open("/sys/firmware/power_state", O_WRONLY);
+    if (power_state_switch_node < 0) {
+        perror("open");
+        return 1;
+    }
+    const char* value = "1";
+    if (write(power_state_switch_node, value, 1) < 0) {
+        perror("write");
         return 1;
     }
     return 0;

+ 12 - 15
Userland/Utilities/shutdown.cpp

@@ -1,28 +1,25 @@
 /*
  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
-#include <LibCore/ArgsParser.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
 
-int main(int argc, char** argv)
+int main(int, char**)
 {
-    bool now = false;
-
-    Core::ArgsParser args_parser;
-    args_parser.add_option(now, "Shut down now", "now", 'n');
-    args_parser.parse(argc, argv);
-
-    if (now) {
-        if (halt() < 0) {
-            perror("shutdown");
-            return 1;
-        }
-    } else {
-        args_parser.print_usage(stderr, argv[0]);
+    int power_state_switch_node = open("/sys/firmware/power_state", O_WRONLY);
+    if (power_state_switch_node < 0) {
+        perror("open");
+        return 1;
+    }
+    const char* value = "2";
+    if (write(power_state_switch_node, value, 1) < 0) {
+        perror("write");
         return 1;
     }
+    return 0;
 }