summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/svc/svc_address_arbiter.cpp
blob: e6a5d2ae504b33a2819655016dea959eb087c2e1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"

namespace Kernel::Svc {
namespace {

constexpr bool IsValidSignalType(Svc::SignalType type) {
    switch (type) {
    case Svc::SignalType::Signal:
    case Svc::SignalType::SignalAndIncrementIfEqual:
    case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
        return true;
    default:
        return false;
    }
}

constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
    switch (type) {
    case Svc::ArbitrationType::WaitIfLessThan:
    case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
    case Svc::ArbitrationType::WaitIfEqual:
        return true;
    default:
        return false;
    }
}

} // namespace

// Wait for an address (via Address Arbiter)
Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
                      s64 timeout_ns) {
    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
              address, arb_type, value, timeout_ns);

    // Validate input.
    if (IsKernelAddress(address)) {
        LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
        return ResultInvalidCurrentMemory;
    }
    if (!Common::IsAligned(address, sizeof(s32))) {
        LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
        return ResultInvalidAddress;
    }
    if (!IsValidArbitrationType(arb_type)) {
        LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
        return ResultInvalidEnumValue;
    }

    // Convert timeout from nanoseconds to ticks.
    s64 timeout{};
    if (timeout_ns > 0) {
        const s64 offset_tick(timeout_ns);
        if (offset_tick > 0) {
            timeout = offset_tick + 2;
            if (timeout <= 0) {
                timeout = std::numeric_limits<s64>::max();
            }
        } else {
            timeout = std::numeric_limits<s64>::max();
        }
    } else {
        timeout = timeout_ns;
    }

    return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
}

// Signals to an address (via Address Arbiter)
Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value,
                       s32 count) {
    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
              address, signal_type, value, count);

    // Validate input.
    if (IsKernelAddress(address)) {
        LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
        return ResultInvalidCurrentMemory;
    }
    if (!Common::IsAligned(address, sizeof(s32))) {
        LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
        return ResultInvalidAddress;
    }
    if (!IsValidSignalType(signal_type)) {
        LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
        return ResultInvalidEnumValue;
    }

    return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
                                                                  count);
}

Result WaitForAddress64(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
                        s64 timeout_ns) {
    return WaitForAddress(system, address, arb_type, value, timeout_ns);
}

Result SignalToAddress64(Core::System& system, VAddr address, SignalType signal_type, s32 value,
                         s32 count) {
    return SignalToAddress(system, address, signal_type, value, count);
}

Result WaitForAddress64From32(Core::System& system, u32 address, ArbitrationType arb_type,
                              s32 value, s64 timeout_ns) {
    return WaitForAddress(system, address, arb_type, value, timeout_ns);
}

Result SignalToAddress64From32(Core::System& system, u32 address, SignalType signal_type, s32 value,
                               s32 count) {
    return SignalToAddress(system, address, signal_type, value, count);
}

} // namespace Kernel::Svc