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