1 // Copyright (c) 2012- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include <list>
19 #include "Common/Serialize/Serializer.h"
20 #include "Common/Serialize/SerializeFuncs.h"
21 #include "Common/Serialize/SerializeList.h"
22 #include "Core/HLE/sceKernel.h"
23 #include "Core/HLE/sceKernelAlarm.h"
24 #include "Core/HLE/sceKernelInterrupt.h"
25 #include "Core/HLE/HLE.h"
26 #include "Core/CoreTiming.h"
27 #include "Core/MemMap.h"
28
29 const int NATIVEALARM_SIZE = 20;
30
31 std::list<SceUID> triggeredAlarm;
32
33 struct NativeAlarm
34 {
35 SceSize_le size;
36 u32_le pad;
37 u64_le schedule;
38 u32_le handlerPtr;
39 u32_le commonPtr;
40 };
41
42 struct PSPAlarm : public KernelObject {
GetNamePSPAlarm43 const char *GetName() override {return "[Alarm]";}
GetTypeNamePSPAlarm44 const char *GetTypeName() override { return GetStaticTypeName(); }
GetStaticTypeNamePSPAlarm45 static const char *GetStaticTypeName() { return "Alarm"; }
GetMissingErrorCodePSPAlarm46 static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_ALMID; }
GetStaticIDTypePSPAlarm47 static int GetStaticIDType() { return SCE_KERNEL_TMID_Alarm; }
GetIDTypePSPAlarm48 int GetIDType() const override { return SCE_KERNEL_TMID_Alarm; }
49
DoStatePSPAlarm50 void DoState(PointerWrap &p) override {
51 auto s = p.Section("Alarm", 1);
52 if (!s)
53 return;
54
55 Do(p, alm);
56 }
57
58 NativeAlarm alm;
59 };
60
61 void __KernelScheduleAlarm(PSPAlarm *alarm, u64 micro);
62
63 class AlarmIntrHandler : public IntrHandler
64 {
65 public:
AlarmIntrHandler()66 AlarmIntrHandler() : IntrHandler(PSP_SYSTIMER0_INTR) {}
67
run(PendingInterrupt & pend)68 bool run(PendingInterrupt& pend) override
69 {
70 u32 error;
71 int alarmID = triggeredAlarm.front();
72
73 PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(alarmID, error);
74 if (error)
75 {
76 WARN_LOG(SCEKERNEL, "Ignoring deleted alarm %08x", alarmID);
77 return false;
78 }
79
80 currentMIPS->pc = alarm->alm.handlerPtr;
81 currentMIPS->r[MIPS_REG_A0] = alarm->alm.commonPtr;
82 DEBUG_LOG(SCEKERNEL, "Entering alarm %08x handler: %08x", alarmID, currentMIPS->pc);
83
84 return true;
85 }
86
handleResult(PendingInterrupt & pend)87 void handleResult(PendingInterrupt& pend) override
88 {
89 int result = currentMIPS->r[MIPS_REG_V0];
90
91 int alarmID = triggeredAlarm.front();
92 triggeredAlarm.pop_front();
93
94 // A non-zero result means to reschedule.
95 if (result > 0)
96 {
97 DEBUG_LOG(SCEKERNEL, "Rescheduling alarm %08x for +%dms", alarmID, result);
98 u32 error;
99 PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(alarmID, error);
100 __KernelScheduleAlarm(alarm, result);
101 }
102 else
103 {
104 if (result < 0)
105 WARN_LOG(SCEKERNEL, "Alarm requested reschedule for negative value %u, ignoring", (unsigned) result);
106
107 DEBUG_LOG(SCEKERNEL, "Finished alarm %08x", alarmID);
108
109 // Delete the alarm if it's not rescheduled.
110 kernelObjects.Destroy<PSPAlarm>(alarmID);
111 }
112 }
113 };
114
115 static int alarmTimer = -1;
116
__KernelTriggerAlarm(u64 userdata,int cyclesLate)117 static void __KernelTriggerAlarm(u64 userdata, int cyclesLate) {
118 int uid = (int) userdata;
119
120 u32 error;
121 PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(uid, error);
122 if (alarm) {
123 triggeredAlarm.push_back(uid);
124 __TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER0_INTR);
125 }
126 }
127
__KernelAlarmInit()128 void __KernelAlarmInit()
129 {
130 triggeredAlarm.clear();
131 __RegisterIntrHandler(PSP_SYSTIMER0_INTR, new AlarmIntrHandler());
132 alarmTimer = CoreTiming::RegisterEvent("Alarm", __KernelTriggerAlarm);
133 }
134
__KernelAlarmDoState(PointerWrap & p)135 void __KernelAlarmDoState(PointerWrap &p)
136 {
137 auto s = p.Section("sceKernelAlarm", 1);
138 if (!s)
139 return;
140
141 Do(p, alarmTimer);
142 Do(p, triggeredAlarm);
143 CoreTiming::RestoreRegisterEvent(alarmTimer, "Alarm", __KernelTriggerAlarm);
144 }
145
__KernelAlarmObject()146 KernelObject *__KernelAlarmObject() {
147 // Default object to load from state.
148 return new PSPAlarm;
149 }
150
__KernelScheduleAlarm(PSPAlarm * alarm,u64 micro)151 void __KernelScheduleAlarm(PSPAlarm *alarm, u64 micro) {
152 alarm->alm.schedule = CoreTiming::GetGlobalTimeUs() + micro;
153 CoreTiming::ScheduleEvent(usToCycles(micro), alarmTimer, alarm->GetUID());
154 }
155
__KernelSetAlarm(u64 micro,u32 handlerPtr,u32 commonPtr)156 static SceUID __KernelSetAlarm(u64 micro, u32 handlerPtr, u32 commonPtr)
157 {
158 if (!Memory::IsValidAddress(handlerPtr))
159 return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
160
161 PSPAlarm *alarm = new PSPAlarm();
162 SceUID uid = kernelObjects.Create(alarm);
163
164 alarm->alm.size = NATIVEALARM_SIZE;
165 alarm->alm.handlerPtr = handlerPtr;
166 alarm->alm.commonPtr = commonPtr;
167
168 __KernelScheduleAlarm(alarm, micro);
169 return uid;
170 }
171
sceKernelSetAlarm(SceUInt micro,u32 handlerPtr,u32 commonPtr)172 SceUID sceKernelSetAlarm(SceUInt micro, u32 handlerPtr, u32 commonPtr)
173 {
174 DEBUG_LOG(SCEKERNEL, "sceKernelSetAlarm(%d, %08x, %08x)", micro, handlerPtr, commonPtr);
175 return __KernelSetAlarm((u64) micro, handlerPtr, commonPtr);
176 }
177
sceKernelSetSysClockAlarm(u32 microPtr,u32 handlerPtr,u32 commonPtr)178 SceUID sceKernelSetSysClockAlarm(u32 microPtr, u32 handlerPtr, u32 commonPtr)
179 {
180 u64 micro;
181
182 if (Memory::IsValidAddress(microPtr))
183 micro = Memory::Read_U64(microPtr);
184 else
185 return -1;
186
187 DEBUG_LOG(SCEKERNEL, "sceKernelSetSysClockAlarm(%lld, %08x, %08x)", micro, handlerPtr, commonPtr);
188 return __KernelSetAlarm(micro, handlerPtr, commonPtr);
189 }
190
sceKernelCancelAlarm(SceUID uid)191 int sceKernelCancelAlarm(SceUID uid)
192 {
193 DEBUG_LOG(SCEKERNEL, "sceKernelCancelAlarm(%08x)", uid);
194
195 CoreTiming::UnscheduleEvent(alarmTimer, uid);
196
197 return kernelObjects.Destroy<PSPAlarm>(uid);
198 }
199
sceKernelReferAlarmStatus(SceUID uid,u32 infoPtr)200 int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr)
201 {
202 u32 error;
203 PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(uid, error);
204 if (!alarm)
205 {
206 ERROR_LOG(SCEKERNEL, "sceKernelReferAlarmStatus(%08x, %08x): invalid alarm", uid, infoPtr);
207 return error;
208 }
209
210 DEBUG_LOG(SCEKERNEL, "sceKernelReferAlarmStatus(%08x, %08x)", uid, infoPtr);
211
212 if (!Memory::IsValidAddress(infoPtr))
213 return -1;
214
215 u32 size = Memory::Read_U32(infoPtr);
216
217 // Alarms actually respect size and write (kinda) what it can hold.
218 if (size > 0)
219 Memory::Write_U32(alarm->alm.size, infoPtr);
220 if (size > 4)
221 Memory::Write_U64(alarm->alm.schedule, infoPtr + 4);
222 if (size > 12)
223 Memory::Write_U32(alarm->alm.handlerPtr, infoPtr + 12);
224 if (size > 16)
225 Memory::Write_U32(alarm->alm.commonPtr, infoPtr + 16);
226
227 return 0;
228 }
229