1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2024, Linaro Limited 4 */ 5 6 #include <initcall.h> 7 #include <kernel/callout.h> 8 #include <kernel/notif.h> 9 #include <kernel/panic.h> 10 #include <kernel/tee_time.h> 11 #include <kernel/virtualization.h> 12 #include <types_ext.h> 13 14 #define TEST_WD_TIMER_PERIOD_MS 1000 15 16 struct wd_data { 17 bool pending; 18 bool enabled; 19 uint16_t guest_id; 20 unsigned int timeout_count; 21 unsigned int call_count; 22 struct callout callout; 23 }; 24 25 static struct wd_data default_wd_data; 26 static unsigned int wd_data_id __nex_bss; 27 28 static struct wd_data *get_wd_data(struct guest_partition *prtn) 29 { 30 if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) { 31 assert(prtn); 32 return virt_get_guest_spec_data(prtn, wd_data_id); 33 } 34 return &default_wd_data; 35 } 36 37 static bool test_wd_callback(struct callout *co) 38 { 39 struct wd_data *wd = container_of(co, struct wd_data, callout); 40 41 if (wd->pending) 42 wd->timeout_count++; 43 wd->call_count++; 44 if (wd->call_count < 10 || !(wd->call_count % 60) || wd->pending) { 45 if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) 46 DMSG("WD %"PRIu16" call_count %u, timeout_count %u", 47 wd->guest_id, wd->call_count, wd->timeout_count); 48 else 49 DMSG("WD call_count %u, timeout_count %u", 50 wd->call_count, wd->timeout_count); 51 } 52 wd->pending = true; 53 notif_send_async(NOTIF_VALUE_DO_BOTTOM_HALF, wd->guest_id); 54 55 return true; 56 } 57 58 static void wd_ndrv_atomic_cb(struct notif_driver *ndrv __unused, 59 enum notif_event ev, uint16_t guest_id) 60 { 61 if (ev == NOTIF_EVENT_STARTED) { 62 struct guest_partition *prtn = virt_get_guest(guest_id); 63 struct wd_data *wd = get_wd_data(prtn); 64 65 if (!wd->enabled) { 66 wd->guest_id = guest_id; 67 callout_add(&wd->callout, test_wd_callback, 68 TEST_WD_TIMER_PERIOD_MS); 69 70 wd->enabled = true; 71 } 72 virt_put_guest(prtn); 73 } 74 } 75 DECLARE_KEEP_PAGER(wd_ndrv_atomic_cb); 76 77 static void wd_ndrv_yielding_cb(struct notif_driver *ndrv __unused, 78 enum notif_event ev) 79 { 80 if (ev == NOTIF_EVENT_DO_BOTTOM_HALF) { 81 struct guest_partition *prtn = virt_get_current_guest(); 82 struct wd_data *wd = get_wd_data(prtn); 83 84 if (wd->pending && wd->call_count < 10) 85 DMSG("Clearing pending"); 86 wd->pending = false; 87 virt_put_guest(prtn); 88 } 89 } 90 91 struct notif_driver wd_ndrv __nex_data = { 92 .atomic_cb = wd_ndrv_atomic_cb, 93 .yielding_cb = wd_ndrv_yielding_cb, 94 }; 95 96 static void wd_data_destroy(void *data) 97 { 98 struct wd_data *wd = data; 99 100 callout_rem(&wd->callout); 101 } 102 103 static TEE_Result nex_init_test_wd(void) 104 { 105 TEE_Result res = TEE_SUCCESS; 106 107 if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) { 108 res = virt_add_guest_spec_data(&wd_data_id, 109 sizeof(struct wd_data), 110 wd_data_destroy); 111 if (res) 112 return res; 113 } 114 115 notif_register_driver(&wd_ndrv); 116 117 return TEE_SUCCESS; 118 } 119 120 nex_early_init(nex_init_test_wd); 121 122 struct periodic_data { 123 unsigned int count; 124 struct callout callout; 125 }; 126 127 static bool periodic_callback(struct callout *co) 128 { 129 struct periodic_data *d = container_of(co, struct periodic_data, 130 callout); 131 TEE_Time t = { }; 132 133 if (tee_time_get_sys_time(&t)) 134 panic(); 135 d->count++; 136 DMSG("seconds %"PRIu32" millis %"PRIu32" count %u", 137 t.seconds, t.millis, d->count); 138 139 if (d->count > 20) { 140 DMSG("Disabling periodic callout"); 141 return false; 142 } 143 144 return true; 145 } 146 DECLARE_KEEP_PAGER(periodic_callback); 147 148 static TEE_Result nex_init_periodic_callback(void) 149 { 150 struct periodic_data *d = nex_calloc(1, sizeof(*d)); 151 152 if (!d) 153 return TEE_ERROR_OUT_OF_MEMORY; 154 155 DMSG("Adding a periodic callout"); 156 callout_add(&d->callout, periodic_callback, TEST_WD_TIMER_PERIOD_MS); 157 158 return TEE_SUCCESS; 159 } 160 161 nex_early_init(nex_init_periodic_callback); 162