11c3c4a5fSJens Wiklander // SPDX-License-Identifier: BSD-2-Clause
21c3c4a5fSJens Wiklander /*
31c3c4a5fSJens Wiklander * Copyright (c) 2024, Linaro Limited
41c3c4a5fSJens Wiklander */
51c3c4a5fSJens Wiklander
61c3c4a5fSJens Wiklander #include <initcall.h>
71c3c4a5fSJens Wiklander #include <kernel/callout.h>
81c3c4a5fSJens Wiklander #include <kernel/notif.h>
91c3c4a5fSJens Wiklander #include <kernel/panic.h>
101c3c4a5fSJens Wiklander #include <kernel/tee_time.h>
111c3c4a5fSJens Wiklander #include <kernel/virtualization.h>
121c3c4a5fSJens Wiklander #include <types_ext.h>
131c3c4a5fSJens Wiklander
141c3c4a5fSJens Wiklander #define TEST_WD_TIMER_PERIOD_MS 1000
151c3c4a5fSJens Wiklander
161c3c4a5fSJens Wiklander struct wd_data {
171c3c4a5fSJens Wiklander bool pending;
181c3c4a5fSJens Wiklander bool enabled;
19*d237e616SJens Wiklander uint16_t guest_id;
201c3c4a5fSJens Wiklander unsigned int timeout_count;
211c3c4a5fSJens Wiklander unsigned int call_count;
221c3c4a5fSJens Wiklander struct callout callout;
231c3c4a5fSJens Wiklander };
241c3c4a5fSJens Wiklander
251c3c4a5fSJens Wiklander static struct wd_data default_wd_data;
26*d237e616SJens Wiklander static unsigned int wd_data_id __nex_bss;
271c3c4a5fSJens Wiklander
get_wd_data(struct guest_partition * prtn)28*d237e616SJens Wiklander static struct wd_data *get_wd_data(struct guest_partition *prtn)
291c3c4a5fSJens Wiklander {
30*d237e616SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
31*d237e616SJens Wiklander assert(prtn);
32*d237e616SJens Wiklander return virt_get_guest_spec_data(prtn, wd_data_id);
33*d237e616SJens Wiklander }
341c3c4a5fSJens Wiklander return &default_wd_data;
351c3c4a5fSJens Wiklander }
361c3c4a5fSJens Wiklander
test_wd_callback(struct callout * co)371c3c4a5fSJens Wiklander static bool test_wd_callback(struct callout *co)
381c3c4a5fSJens Wiklander {
391c3c4a5fSJens Wiklander struct wd_data *wd = container_of(co, struct wd_data, callout);
401c3c4a5fSJens Wiklander
411c3c4a5fSJens Wiklander if (wd->pending)
421c3c4a5fSJens Wiklander wd->timeout_count++;
431c3c4a5fSJens Wiklander wd->call_count++;
44*d237e616SJens Wiklander if (wd->call_count < 10 || !(wd->call_count % 60) || wd->pending) {
45*d237e616SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION))
46*d237e616SJens Wiklander DMSG("WD %"PRIu16" call_count %u, timeout_count %u",
47*d237e616SJens Wiklander wd->guest_id, wd->call_count, wd->timeout_count);
48*d237e616SJens Wiklander else
491c3c4a5fSJens Wiklander DMSG("WD call_count %u, timeout_count %u",
501c3c4a5fSJens Wiklander wd->call_count, wd->timeout_count);
51*d237e616SJens Wiklander }
521c3c4a5fSJens Wiklander wd->pending = true;
53*d237e616SJens Wiklander notif_send_async(NOTIF_VALUE_DO_BOTTOM_HALF, wd->guest_id);
541c3c4a5fSJens Wiklander
551c3c4a5fSJens Wiklander return true;
561c3c4a5fSJens Wiklander }
571c3c4a5fSJens Wiklander
wd_ndrv_atomic_cb(struct notif_driver * ndrv __unused,enum notif_event ev,uint16_t guest_id)581c3c4a5fSJens Wiklander static void wd_ndrv_atomic_cb(struct notif_driver *ndrv __unused,
59*d237e616SJens Wiklander enum notif_event ev, uint16_t guest_id)
601c3c4a5fSJens Wiklander {
611c3c4a5fSJens Wiklander if (ev == NOTIF_EVENT_STARTED) {
62*d237e616SJens Wiklander struct guest_partition *prtn = virt_get_guest(guest_id);
63*d237e616SJens Wiklander struct wd_data *wd = get_wd_data(prtn);
641c3c4a5fSJens Wiklander
651c3c4a5fSJens Wiklander if (!wd->enabled) {
66*d237e616SJens Wiklander wd->guest_id = guest_id;
671c3c4a5fSJens Wiklander callout_add(&wd->callout, test_wd_callback,
681c3c4a5fSJens Wiklander TEST_WD_TIMER_PERIOD_MS);
691c3c4a5fSJens Wiklander
701c3c4a5fSJens Wiklander wd->enabled = true;
711c3c4a5fSJens Wiklander }
72*d237e616SJens Wiklander virt_put_guest(prtn);
731c3c4a5fSJens Wiklander }
741c3c4a5fSJens Wiklander }
75fd3f2d69SJens Wiklander DECLARE_KEEP_PAGER(wd_ndrv_atomic_cb);
761c3c4a5fSJens Wiklander
wd_ndrv_yielding_cb(struct notif_driver * ndrv __unused,enum notif_event ev)771c3c4a5fSJens Wiklander static void wd_ndrv_yielding_cb(struct notif_driver *ndrv __unused,
781c3c4a5fSJens Wiklander enum notif_event ev)
791c3c4a5fSJens Wiklander {
801c3c4a5fSJens Wiklander if (ev == NOTIF_EVENT_DO_BOTTOM_HALF) {
81*d237e616SJens Wiklander struct guest_partition *prtn = virt_get_current_guest();
82*d237e616SJens Wiklander struct wd_data *wd = get_wd_data(prtn);
831c3c4a5fSJens Wiklander
841c3c4a5fSJens Wiklander if (wd->pending && wd->call_count < 10)
851c3c4a5fSJens Wiklander DMSG("Clearing pending");
861c3c4a5fSJens Wiklander wd->pending = false;
87*d237e616SJens Wiklander virt_put_guest(prtn);
881c3c4a5fSJens Wiklander }
891c3c4a5fSJens Wiklander }
901c3c4a5fSJens Wiklander
911c3c4a5fSJens Wiklander struct notif_driver wd_ndrv __nex_data = {
921c3c4a5fSJens Wiklander .atomic_cb = wd_ndrv_atomic_cb,
931c3c4a5fSJens Wiklander .yielding_cb = wd_ndrv_yielding_cb,
941c3c4a5fSJens Wiklander };
951c3c4a5fSJens Wiklander
wd_data_destroy(void * data)96*d237e616SJens Wiklander static void wd_data_destroy(void *data)
97*d237e616SJens Wiklander {
98*d237e616SJens Wiklander struct wd_data *wd = data;
99*d237e616SJens Wiklander
100*d237e616SJens Wiklander callout_rem(&wd->callout);
101*d237e616SJens Wiklander }
102*d237e616SJens Wiklander
nex_init_test_wd(void)1031c3c4a5fSJens Wiklander static TEE_Result nex_init_test_wd(void)
1041c3c4a5fSJens Wiklander {
105*d237e616SJens Wiklander TEE_Result res = TEE_SUCCESS;
106*d237e616SJens Wiklander
107*d237e616SJens Wiklander if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) {
108*d237e616SJens Wiklander res = virt_add_guest_spec_data(&wd_data_id,
109*d237e616SJens Wiklander sizeof(struct wd_data),
110*d237e616SJens Wiklander wd_data_destroy);
111*d237e616SJens Wiklander if (res)
112*d237e616SJens Wiklander return res;
113*d237e616SJens Wiklander }
114*d237e616SJens Wiklander
1151c3c4a5fSJens Wiklander notif_register_driver(&wd_ndrv);
1161c3c4a5fSJens Wiklander
1171c3c4a5fSJens Wiklander return TEE_SUCCESS;
1181c3c4a5fSJens Wiklander }
1191c3c4a5fSJens Wiklander
1201c3c4a5fSJens Wiklander nex_early_init(nex_init_test_wd);
1211c3c4a5fSJens Wiklander
1221c3c4a5fSJens Wiklander struct periodic_data {
1231c3c4a5fSJens Wiklander unsigned int count;
1241c3c4a5fSJens Wiklander struct callout callout;
1251c3c4a5fSJens Wiklander };
1261c3c4a5fSJens Wiklander
periodic_callback(struct callout * co)1271c3c4a5fSJens Wiklander static bool periodic_callback(struct callout *co)
1281c3c4a5fSJens Wiklander {
1291c3c4a5fSJens Wiklander struct periodic_data *d = container_of(co, struct periodic_data,
1301c3c4a5fSJens Wiklander callout);
1311c3c4a5fSJens Wiklander TEE_Time t = { };
1321c3c4a5fSJens Wiklander
1331c3c4a5fSJens Wiklander if (tee_time_get_sys_time(&t))
1341c3c4a5fSJens Wiklander panic();
1351c3c4a5fSJens Wiklander d->count++;
1361c3c4a5fSJens Wiklander DMSG("seconds %"PRIu32" millis %"PRIu32" count %u",
1371c3c4a5fSJens Wiklander t.seconds, t.millis, d->count);
1381c3c4a5fSJens Wiklander
1391c3c4a5fSJens Wiklander if (d->count > 20) {
1401c3c4a5fSJens Wiklander DMSG("Disabling periodic callout");
1411c3c4a5fSJens Wiklander return false;
1421c3c4a5fSJens Wiklander }
1431c3c4a5fSJens Wiklander
1441c3c4a5fSJens Wiklander return true;
1451c3c4a5fSJens Wiklander }
146fd3f2d69SJens Wiklander DECLARE_KEEP_PAGER(periodic_callback);
1471c3c4a5fSJens Wiklander
nex_init_periodic_callback(void)1481c3c4a5fSJens Wiklander static TEE_Result nex_init_periodic_callback(void)
1491c3c4a5fSJens Wiklander {
1501c3c4a5fSJens Wiklander struct periodic_data *d = nex_calloc(1, sizeof(*d));
1511c3c4a5fSJens Wiklander
1521c3c4a5fSJens Wiklander if (!d)
1531c3c4a5fSJens Wiklander return TEE_ERROR_OUT_OF_MEMORY;
1541c3c4a5fSJens Wiklander
1551c3c4a5fSJens Wiklander DMSG("Adding a periodic callout");
1561c3c4a5fSJens Wiklander callout_add(&d->callout, periodic_callback, TEST_WD_TIMER_PERIOD_MS);
1571c3c4a5fSJens Wiklander
1581c3c4a5fSJens Wiklander return TEE_SUCCESS;
1591c3c4a5fSJens Wiklander }
1601c3c4a5fSJens Wiklander
1611c3c4a5fSJens Wiklander nex_early_init(nex_init_periodic_callback);
162