blob: 8c8da1922ae437469b67c01775cbb3417622059c [file] [log] [blame]
Soby Mathewb4c6df42022-11-09 11:13:29 +00001/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4 */
5
Javier Almansa Sobrino2f717dd2024-02-12 20:49:46 +00006#include <buffer.h>
Soby Mathewb4c6df42022-11-09 11:13:29 +00007#include <granule.h>
8#include <psci.h>
9#include <realm.h>
10#include <rec.h>
AlexeiFedorov97844202023-04-27 15:17:35 +010011#include <rsi-handler.h>
Soby Mathewb4c6df42022-11-09 11:13:29 +000012#include <smc-rmi.h>
13#include <smc.h>
14#include <stdint.h>
15
AlexeiFedorov97844202023-04-27 15:17:35 +010016/*
17 * Copy @count GPRs from @rec to @rec_exit.
18 * The remaining @rec_exit.gprs[] values are zero filled.
19 */
20static void forward_args_to_host(unsigned int count, struct rec *rec,
21 struct rmi_rec_exit *rec_exit)
Soby Mathewb4c6df42022-11-09 11:13:29 +000022{
AlexeiFedorov97844202023-04-27 15:17:35 +010023 unsigned int i;
Soby Mathewb4c6df42022-11-09 11:13:29 +000024
AlexeiFedorov97844202023-04-27 15:17:35 +010025 assert(count <= 4U);
26
27 for (i = 0U; i < count; ++i) {
28 rec_exit->gprs[i] = rec->regs[i];
29 }
30
31 for (i = count; i < REC_EXIT_NR_GPRS; ++i) {
32 rec_exit->gprs[i] = 0UL;
33 }
Soby Mathewb4c6df42022-11-09 11:13:29 +000034}
35
AlexeiFedorov97844202023-04-27 15:17:35 +010036static void psci_version(struct rsi_result *res)
Soby Mathewb4c6df42022-11-09 11:13:29 +000037{
AlexeiFedorov97844202023-04-27 15:17:35 +010038 const unsigned long version_1_1 = (1UL << 16) | 1UL;
39
40 res->action = UPDATE_REC_RETURN_TO_REALM;
41 res->smc_res.x[0] = version_1_1;
42}
43
44static void psci_cpu_suspend(struct rec *rec, struct rmi_rec_exit *rec_exit,
45 struct rsi_result *res)
46{
47 res->action = UPDATE_REC_EXIT_TO_HOST;
Soby Mathewb4c6df42022-11-09 11:13:29 +000048
49 /*
AlexeiFedorov97844202023-04-27 15:17:35 +010050 * We treat all target power states as suspend requests,
51 * so all we need to do is forward the FID to the NS hypervisor,
52 * and we can ignore all the parameters.
Soby Mathewb4c6df42022-11-09 11:13:29 +000053 */
AlexeiFedorov97844202023-04-27 15:17:35 +010054 forward_args_to_host(1U, rec, rec_exit);
Soby Mathewb4c6df42022-11-09 11:13:29 +000055
AlexeiFedorov97844202023-04-27 15:17:35 +010056 /*
57 * The exit to the Host is just a notification; the Host does not need
58 * to complete a PSCI request before the next call to RMI_REC_ENTER.
59 * We therefore update the REC immediately with the results of the PSCI
60 * command.
61 */
62 res->smc_res.x[0] = PSCI_RETURN_SUCCESS;
Soby Mathewb4c6df42022-11-09 11:13:29 +000063}
64
AlexeiFedorov97844202023-04-27 15:17:35 +010065static void psci_cpu_off(struct rec *rec, struct rmi_rec_exit *rec_exit,
66 struct rsi_result *res)
Soby Mathewb4c6df42022-11-09 11:13:29 +000067{
AlexeiFedorov97844202023-04-27 15:17:35 +010068 res->action = UPDATE_REC_EXIT_TO_HOST;
Soby Mathewb4c6df42022-11-09 11:13:29 +000069
70 /*
71 * It should be fine to set this flag without holding a lock on the
72 * REC or without explicit memory barriers or ordering semantics
73 * operations, because we already ensure that a REC can only be in an
74 * executing state once at any given time, and we're in this execution
75 * context already, and we will be holding a reference count on the
76 * REC at this point, which will be dropped and re-evaluated with
77 * proper barriers before any CPU can evaluate the runnable field
78 * after this change.
79 */
80 rec->runnable = false;
81
AlexeiFedorov97844202023-04-27 15:17:35 +010082 /* Notify the Host, passing the FID only. */
83 forward_args_to_host(1U, rec, rec_exit);
84
85 /*
86 * The exit to the Host is just a notification; the Host does not need
87 * to complete a PSCI request before the next call to RMI_REC_ENTER.
88 * We therefore update the REC immediately with the results of the PSCI
89 * command.
90 */
91 res->smc_res.x[0] = PSCI_RETURN_SUCCESS;
Soby Mathewb4c6df42022-11-09 11:13:29 +000092}
93
94static void psci_reset_rec(struct rec *rec, unsigned long caller_sctlr_el1)
95{
96 /* Set execution level to EL1 (AArch64) and mask exceptions */
97 rec->pstate = SPSR_EL2_MODE_EL1h |
98 SPSR_EL2_nRW_AARCH64 |
99 SPSR_EL2_F_BIT |
100 SPSR_EL2_I_BIT |
101 SPSR_EL2_A_BIT |
102 SPSR_EL2_D_BIT;
103
104 /* Disable stage 1 MMU and caches */
105 rec->sysregs.sctlr_el1 = SCTLR_EL1_FLAGS;
106
107 /* Set the endianness of the target to that of the caller */
Arvind Ram Prakashbd36a1b2022-12-15 12:16:36 -0600108 rec->sysregs.sctlr_el1 |= caller_sctlr_el1 & SCTLR_ELx_EE_BIT;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000109}
110
111static unsigned long rd_map_read_rec_count(struct granule *g_rd)
112{
113 unsigned long rec_count;
Javier Almansa Sobrino2f717dd2024-02-12 20:49:46 +0000114 struct rd *rd = buffer_granule_map(g_rd, SLOT_RD);
AlexeiFedorov47165422023-09-13 11:47:57 +0100115
AlexeiFedorov9a9062c2023-08-21 15:41:48 +0100116 assert(rd != NULL);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000117
118 rec_count = get_rd_rec_count_unlocked(rd);
119 buffer_unmap(rd);
120 return rec_count;
121}
122
AlexeiFedorov97844202023-04-27 15:17:35 +0100123static void psci_cpu_on(struct rec *rec, struct rmi_rec_exit *rec_exit,
124 struct rsi_result *res)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000125{
AlexeiFedorov97844202023-04-27 15:17:35 +0100126 unsigned long target_cpu = rec->regs[1];
127 unsigned long entry_point_address = rec->regs[2];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000128 unsigned long target_rec_idx;
129
AlexeiFedorov97844202023-04-27 15:17:35 +0100130 res->action = UPDATE_REC_RETURN_TO_REALM;
131
Soby Mathewb4c6df42022-11-09 11:13:29 +0000132 /* Check that entry_point_address is a Protected Realm Address */
133 if (!addr_in_rec_par(rec, entry_point_address)) {
AlexeiFedorov97844202023-04-27 15:17:35 +0100134 res->smc_res.x[0] = PSCI_RETURN_INVALID_ADDRESS;
135 return;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000136 }
137
138 /* Get REC index from MPIDR */
139 target_rec_idx = mpidr_to_rec_idx(target_cpu);
140
141 /*
142 * Check that the target_cpu is a valid value.
143 * Note that the RMM enforces that the REC are created with
144 * consecutively increasing indexes starting from zero.
145 */
146 if (target_rec_idx >= rd_map_read_rec_count(rec->realm_info.g_rd)) {
AlexeiFedorov97844202023-04-27 15:17:35 +0100147 res->smc_res.x[0] = PSCI_RETURN_INVALID_PARAMS;
148 return;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000149 }
150
151 /* Check if we're trying to turn ourselves on */
152 if (target_rec_idx == rec->rec_idx) {
AlexeiFedorov97844202023-04-27 15:17:35 +0100153 res->smc_res.x[0] = PSCI_RETURN_ALREADY_ON;
154 return;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000155 }
156
AlexeiFedorov97844202023-04-27 15:17:35 +0100157 /* Record that a PSCI request is outstanding */
Soby Mathewb4c6df42022-11-09 11:13:29 +0000158 rec->psci_info.pending = true;
159
AlexeiFedorov97844202023-04-27 15:17:35 +0100160 /*
161 * Notify the Host, passing the FID and MPIDR arguments.
162 * Leave REC registers unchanged; these will be read and updated by
163 * psci_complete_request.
164 */
165 forward_args_to_host(2U, rec, rec_exit);
166 res->action = EXIT_TO_HOST;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000167}
168
AlexeiFedorov97844202023-04-27 15:17:35 +0100169static void psci_affinity_info(struct rec *rec, struct rmi_rec_exit *rec_exit,
170 struct rsi_result *res)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000171{
AlexeiFedorov97844202023-04-27 15:17:35 +0100172 unsigned long target_affinity = rec->regs[1];
173 unsigned long lowest_affinity_level = rec->regs[2];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000174 unsigned long target_rec_idx;
175
AlexeiFedorov97844202023-04-27 15:17:35 +0100176 res->action = UPDATE_REC_RETURN_TO_REALM;
177
Soby Mathewb4c6df42022-11-09 11:13:29 +0000178 if (lowest_affinity_level != 0UL) {
AlexeiFedorov97844202023-04-27 15:17:35 +0100179 res->smc_res.x[0] = PSCI_RETURN_INVALID_PARAMS;
180 return;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000181 }
182
183 /* Get REC index from MPIDR */
184 target_rec_idx = mpidr_to_rec_idx(target_affinity);
185
186 /*
187 * Check that the target_affinity is a valid value.
188 * Note that the RMM enforces that the REC are created with
189 * consecutively increasing indexes starting from zero.
190 */
191 if (target_rec_idx >= rd_map_read_rec_count(rec->realm_info.g_rd)) {
AlexeiFedorov97844202023-04-27 15:17:35 +0100192 res->smc_res.x[0] = PSCI_RETURN_INVALID_PARAMS;
193 return;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000194 }
195
196 /* Check if the vCPU targets itself */
197 if (target_rec_idx == rec->rec_idx) {
AlexeiFedorov97844202023-04-27 15:17:35 +0100198 res->smc_res.x[0] = PSCI_AFFINITY_INFO_ON;
199 return;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000200 }
201
AlexeiFedorov97844202023-04-27 15:17:35 +0100202 /* Record that a PSCI request is outstanding */
Soby Mathewb4c6df42022-11-09 11:13:29 +0000203 rec->psci_info.pending = true;
204
AlexeiFedorov97844202023-04-27 15:17:35 +0100205 /*
206 * Notify the Host, passing the FID and MPIDR arguments.
207 * Leave REC registers unchanged; these will be read and updated
208 * by psci_complete_request.
209 */
210 forward_args_to_host(2U, rec, rec_exit);
211
212 res->action = EXIT_TO_HOST;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000213}
214
215/*
216 * Turning a system off or requesting a reboot of a realm is enforced by the
217 * RMM by preventing execution of a REC after the function has run. Reboot
218 * functionality must be provided by the host hypervisor by creating a new
219 * Realm with associated attestation, measurement etc.
220 */
221static void system_off_reboot(struct rec *rec)
222{
223 struct rd *rd;
224 struct granule *g_rd = rec->realm_info.g_rd;
225
226 /*
227 * The RECs (and, consequently, the PSCI calls) run without any
228 * RMM lock held. Therefore, we cannot cause a deadlock when we acquire
229 * the rd lock here before we set the Realm's new state.
230 */
231 granule_lock(g_rd, GRANULE_STATE_RD);
Javier Almansa Sobrino2f717dd2024-02-12 20:49:46 +0000232 rd = buffer_granule_map(rec->realm_info.g_rd, SLOT_RD);
AlexeiFedorov9a9062c2023-08-21 15:41:48 +0100233 assert(rd != NULL);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000234
Mate Toth-Pal988dfcb2024-01-19 10:52:06 +0100235 set_rd_state(rd, REALM_SYSTEM_OFF);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000236
237 buffer_unmap(rd);
238 granule_unlock(g_rd);
239
240 /* TODO: Invalidate all stage 2 entris to ensure REC exits */
241}
242
AlexeiFedorov97844202023-04-27 15:17:35 +0100243static void psci_system_off_reset(struct rec *rec,
244 struct rmi_rec_exit *rec_exit,
245 struct rsi_result *res)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000246{
Soby Mathewb4c6df42022-11-09 11:13:29 +0000247 system_off_reboot(rec);
248
AlexeiFedorov97844202023-04-27 15:17:35 +0100249 /* Notify the Host, passing the FID only */
250 forward_args_to_host(1U, rec, rec_exit);
251
252 res->action = EXIT_TO_HOST;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000253}
254
AlexeiFedorov97844202023-04-27 15:17:35 +0100255static void psci_features(struct rec *rec, struct rsi_result *res)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000256{
AlexeiFedorov97844202023-04-27 15:17:35 +0100257 unsigned int psci_func_id = (unsigned int)rec->regs[1];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000258
259 switch (psci_func_id) {
260 case SMC32_PSCI_CPU_SUSPEND:
261 case SMC64_PSCI_CPU_SUSPEND:
262 case SMC32_PSCI_CPU_OFF:
263 case SMC32_PSCI_CPU_ON:
264 case SMC64_PSCI_CPU_ON:
265 case SMC32_PSCI_AFFINITY_INFO:
266 case SMC64_PSCI_AFFINITY_INFO:
267 case SMC32_PSCI_SYSTEM_OFF:
268 case SMC32_PSCI_SYSTEM_RESET:
269 case SMC32_PSCI_FEATURES:
270 case SMCCC_VERSION:
AlexeiFedorov97844202023-04-27 15:17:35 +0100271 res->smc_res.x[0] = PSCI_RETURN_SUCCESS;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000272 break;
273 default:
AlexeiFedorov97844202023-04-27 15:17:35 +0100274 res->smc_res.x[0] = PSCI_RETURN_NOT_SUPPORTED;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000275 }
276
AlexeiFedorov97844202023-04-27 15:17:35 +0100277 res->action = UPDATE_REC_RETURN_TO_REALM;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000278}
279
AlexeiFedorov97844202023-04-27 15:17:35 +0100280void handle_psci(struct rec *rec,
281 struct rmi_rec_exit *rec_exit,
282 struct rsi_result *res)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000283{
AlexeiFedorov97844202023-04-27 15:17:35 +0100284 unsigned int function_id = (unsigned int)rec->regs[0];
Soby Mathewb4c6df42022-11-09 11:13:29 +0000285
286 switch (function_id) {
287 case SMC32_PSCI_VERSION:
AlexeiFedorov97844202023-04-27 15:17:35 +0100288 psci_version(res);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000289 break;
290 case SMC32_PSCI_CPU_SUSPEND:
291 case SMC64_PSCI_CPU_SUSPEND:
AlexeiFedorov97844202023-04-27 15:17:35 +0100292 psci_cpu_suspend(rec, rec_exit, res);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000293 break;
294 case SMC32_PSCI_CPU_OFF:
AlexeiFedorov97844202023-04-27 15:17:35 +0100295 psci_cpu_off(rec, rec_exit, res);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000296 break;
297 case SMC32_PSCI_CPU_ON:
Soby Mathewb4c6df42022-11-09 11:13:29 +0000298 case SMC64_PSCI_CPU_ON:
AlexeiFedorov97844202023-04-27 15:17:35 +0100299 psci_cpu_on(rec, rec_exit, res);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000300 break;
301 case SMC32_PSCI_AFFINITY_INFO:
Soby Mathewb4c6df42022-11-09 11:13:29 +0000302 case SMC64_PSCI_AFFINITY_INFO:
AlexeiFedorov97844202023-04-27 15:17:35 +0100303 psci_affinity_info(rec, rec_exit, res);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000304 break;
305 case SMC32_PSCI_SYSTEM_OFF:
Soby Mathewb4c6df42022-11-09 11:13:29 +0000306 case SMC32_PSCI_SYSTEM_RESET:
AlexeiFedorov97844202023-04-27 15:17:35 +0100307 psci_system_off_reset(rec, rec_exit, res);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000308 break;
309 case SMC32_PSCI_FEATURES:
AlexeiFedorov97844202023-04-27 15:17:35 +0100310 psci_features(rec, res);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000311 break;
312 default:
AlexeiFedorov97844202023-04-27 15:17:35 +0100313 res->action = UPDATE_REC_RETURN_TO_REALM;
314 res->smc_res.x[0] = PSCI_RETURN_NOT_SUPPORTED;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000315 break;
316 }
317
AlexeiFedorove5dcae22023-08-29 12:58:18 +0100318 if (((unsigned int)res->action & FLAG_EXIT_TO_HOST) != 0U) {
AlexeiFedorov97844202023-04-27 15:17:35 +0100319 rec_exit->exit_reason = RMI_EXIT_PSCI;
320 }
Soby Mathewb4c6df42022-11-09 11:13:29 +0000321}
322
323/*
324 * In the following two functions, it is only safe to access the runnable field
325 * on the target_rec once the target_rec is no longer running on another PE and
326 * all writes performed by the other PE as part of smc_rec_enter is also
327 * guaranteed to be observed here, which we know when we read a zero refcount
328 * on the target rec using acquire semantics paired with the release semantics
329 * on the reference count in smc_rec_enter. If we observe a non-zero refcount
330 * it simply means that the target_rec is running and we can return the
331 * corresponding value.
332 */
333static unsigned long complete_psci_cpu_on(struct rec *target_rec,
334 unsigned long entry_point_address,
Soby Mathewe52db162023-09-19 13:46:08 +0100335 unsigned long context_id,
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100336 unsigned long caller_sctlr_el1,
337 unsigned long status)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000338{
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000339 if ((granule_refcount_read_acquire(target_rec->g_rec) != 0U) ||
Soby Mathewb4c6df42022-11-09 11:13:29 +0000340 target_rec->runnable) {
341 return PSCI_RETURN_ALREADY_ON;
342 }
343
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100344 /*
345 * Host is permitted to deny a PSCI_CPU_ON request,
346 * if the target CPU is not already on.
347 */
348 if (status == PSCI_RETURN_DENIED) {
349 return PSCI_RETURN_DENIED;
350 }
351
Soby Mathewb4c6df42022-11-09 11:13:29 +0000352 psci_reset_rec(target_rec, caller_sctlr_el1);
Soby Mathewe52db162023-09-19 13:46:08 +0100353 target_rec->regs[0] = context_id;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000354 target_rec->pc = entry_point_address;
355 target_rec->runnable = true;
356 return PSCI_RETURN_SUCCESS;
357}
358
359static unsigned long complete_psci_affinity_info(struct rec *target_rec)
360{
AlexeiFedorovd6d93d82024-02-13 16:52:11 +0000361 if ((granule_refcount_read_acquire(target_rec->g_rec) != 0U) ||
Soby Mathewb4c6df42022-11-09 11:13:29 +0000362 target_rec->runnable) {
363 return PSCI_AFFINITY_INFO_ON;
364 }
365
366 return PSCI_AFFINITY_INFO_OFF;
367}
368
369unsigned long psci_complete_request(struct rec *calling_rec,
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100370 struct rec *target_rec, unsigned long status)
Soby Mathewb4c6df42022-11-09 11:13:29 +0000371{
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100372 unsigned long ret = RMI_SUCCESS;
373 unsigned long rec_ret = PSCI_RETURN_NOT_SUPPORTED;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000374 unsigned long mpidr = calling_rec->regs[1];
375
376 if (!calling_rec->psci_info.pending) {
377 return RMI_ERROR_INPUT;
378 }
379
380 if (calling_rec->realm_info.g_rd != target_rec->realm_info.g_rd) {
381 return RMI_ERROR_INPUT;
382 }
383
384 if (mpidr_to_rec_idx(mpidr) != target_rec->rec_idx) {
385 return RMI_ERROR_INPUT;
386 }
387
388 switch (calling_rec->regs[0]) {
389 case SMC32_PSCI_CPU_ON:
390 case SMC64_PSCI_CPU_ON:
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100391 if ((status != PSCI_RETURN_SUCCESS) &&
392 (status != PSCI_RETURN_DENIED)) {
393 return RMI_ERROR_INPUT;
394 }
395
396 rec_ret = complete_psci_cpu_on(target_rec,
397 calling_rec->regs[2],
Soby Mathewe52db162023-09-19 13:46:08 +0100398 calling_rec->regs[3],
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100399 calling_rec->sysregs.sctlr_el1,
400 status);
401 /*
402 * If the target CPU is already running and the Host has denied the
403 * PSCI_CPU_ON request, then return error back to Host.
404 */
405 if ((status == PSCI_RETURN_DENIED) &&
406 (rec_ret == PSCI_RETURN_ALREADY_ON)) {
407 ret = RMI_ERROR_INPUT;
408 }
Soby Mathewb4c6df42022-11-09 11:13:29 +0000409 break;
410 case SMC32_PSCI_AFFINITY_INFO:
411 case SMC64_PSCI_AFFINITY_INFO:
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100412 if (status != PSCI_RETURN_SUCCESS) {
413 return RMI_ERROR_INPUT;
414 }
415
416 rec_ret = complete_psci_affinity_info(target_rec);
Soby Mathewb4c6df42022-11-09 11:13:29 +0000417 break;
418 default:
419 assert(false);
420 }
421
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100422 calling_rec->regs[0] = rec_ret;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000423 calling_rec->regs[1] = 0;
424 calling_rec->regs[2] = 0;
425 calling_rec->regs[3] = 0;
426 calling_rec->psci_info.pending = false;
427
AlexeiFedorov120d7d02023-08-02 16:51:48 +0100428 return ret;
Soby Mathewb4c6df42022-11-09 11:13:29 +0000429}