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

Code before:
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);
    VERIFY(method.has_value());

    // Currently, we only support HVC (as opposed to SMC). While SMC is very similar to HVC, only the latter has been tested so far.
    if (method->as_string() != "hvc"sv)
        return false;

    return true;
}

void poweroff()
{
    aarch64_hvc(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0, 0);
}


Review comment (suggestion):
The binding doesn't seem to require the PSCI node to be at `/psci`. Making this a `DeviceTree::Driver` would allow us to remove the hardcoded path, but I think this is fine for now.
Just maybe add a FIXME that we shouldn't assume that it's there and that it should be converted to a `DeviceTree::Driver`.

Code after:

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;
    }

    // Currently, we only support HVC (as opposed to SMC). While SMC is very similar to HVC, only the latter has been tested so far.
    if (method->as_string() != "hvc"sv)
        return false;

    return true;
}

void poweroff()
{
    call(Function::SystemOff, 0, 0, 0);
}