Repository: SerenityOS/serenity
File: Kernel/Arch/aarch64/PSCI.cpp (C++)

Code before:
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <Kernel/Arch/aarch64/PSCI.h>
#include <Kernel/Firmware/DeviceTree/DeviceTree.h>

namespace Kernel::PSCI {

#define PSCI_FUNCTION_BASE 0x84000000

#define PSCI_SYSTEM_OFF ((PSCI_FUNCTION_BASE) + 8)
#define PSCI_SYSTEM_RESET ((PSCI_FUNCTION_BASE) + 9)

static inline FlatPtr aarch64_hvc(FlatPtr arg0, FlatPtr arg1, FlatPtr arg2, FlatPtr arg3, FlatPtr arg4, FlatPtr arg5, FlatPtr arg6, FlatPtr arg7)
{
    register FlatPtr a0 asm("x0") = arg0;
    register FlatPtr a1 asm("x1") = arg1;
    register FlatPtr a2 asm("x2") = arg2;
    register FlatPtr a3 asm("x3") = arg3;
    register FlatPtr a4 asm("x4") = arg4;
    register FlatPtr a5 asm("x5") = arg5;
    register FlatPtr a6 asm("x6") = arg6;
    register FlatPtr a7 asm("x7") = arg7;

    asm volatile("hvc\t0\n"
                 : "=r"(a0), "=r"(a1), "=r"(a2), "=r"(a3)
                 : "0"(a0),
                 "r"(a1),
                 "r"(a2),
                 "r"(a3),
                 "r"(a4),
                 "r"(a5),
                 "r"(a6),
                 "r"(a7));

    return a0;
}

bool is_supported()
{
    // https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/psci.txt
    auto psci = DeviceTree::get().get_child("psci"sv);
    if (!psci.has_value())
        return false;

    // NOTE: We reject "arm,psci" on purpose, since these old devices don't have standardized function IDs.
    if (!psci->is_compatible_with("arm,psci-0.2"sv) && !psci->is_compatible_with("arm,psci-1.0"sv))
        return false;

    auto method = psci->get_property("method"sv);

Review comment (suggestion):
This should be something like
```c++
asm volatile(
    "hvc #0"
    : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3)
    :
    : "memory");
```

I'm not sure about what clobbers are needed though. You currently seem to assume that the firmware might write to x0-x3. Are you sure that only these registers may be clobbered?
SMCCC iirc supports multiple return values in x0-x7 (but PSCI only uses x0 I think) and only saves some registers similar to AAPCS64.

We don't use tabs for separating assembly parameters and for ARM we use the '#' prefix for immediates.
The input and output operands should be combined with `"+r"` when they refer to the same `register` variable.
The memory clobber is probably a good idea, since the calling into firmware likely causes memory accesses.

Code after:
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <Kernel/Arch/aarch64/PSCI.h>
#include <Kernel/Firmware/DeviceTree/DeviceTree.h>

// https://developer.arm.com/documentation/den0022/latest/
namespace Kernel::PSCI {

enum class Function : u32 {
    SystemOff = 0x8400'0008,
    SystemReset = 0x8400'0009,
};

static FlatPtr call(Function function, FlatPtr arg0, FlatPtr arg1, FlatPtr arg2)
{
    // 5.2.1 Register usage in arguments and return values
    // "For [PSCI] versions using 64-bit parameters, the arguments are passed in X0 to X3, with return values in X0."
    register FlatPtr x0 asm("x0") = static_cast<FlatPtr>(function);
    register FlatPtr x1 asm("x1") = arg0;
    register FlatPtr x2 asm("x2") = arg1;
    register FlatPtr x3 asm("x3") = arg2;

    // NOTE: x4-x17 are marked as clobbered since SMCCC 1.0 doesn't
    // require them to be preserved across SMC or HVC calls.
    asm volatile(
        "hvc #0"
        : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3)
        :
        : "memory", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17");

    return x0;
}

bool is_supported()
{
    // https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/psci.txt
    // FIXME: Make this a DeviceTree::Driver, and don't assume that the psci node is a child of the root node.
    auto psci = DeviceTree::get().get_child("psci"sv);
    if (!psci.has_value())
        return false;

    // NOTE: We reject "arm,psci" on purpose, since these old devices don't have standardized function IDs.
    if (!psci->is_compatible_with("arm,psci-0.2"sv) && !psci->is_compatible_with("arm,psci-1.0"sv))
        return false;

    auto method = psci->get_property("method"sv);
    if (!method.has_value()) {
        dbgln("PSCI: No method property found");
        return false;