+
+int
+telemetry(__unused struct proc *p, struct telemetry_args *args, __unused int32_t *retval)
+{
+ int error = 0;
+
+ switch (args->cmd) {
+#if CONFIG_TELEMETRY
+ case TELEMETRY_CMD_TIMER_EVENT:
+ error = telemetry_timer_event(args->deadline, args->interval, args->leeway);
+ break;
+ case TELEMETRY_CMD_PMI_SETUP:
+ error = telemetry_pmi_setup((enum telemetry_pmi)args->deadline, args->interval);
+ break;
+#endif /* CONFIG_TELEMETRY */
+ case TELEMETRY_CMD_VOUCHER_NAME:
+ if (thread_set_voucher_name((mach_port_name_t)args->deadline)) {
+ error = EINVAL;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+#if DEVELOPMENT || DEBUG
+#if CONFIG_WAITQ_DEBUG
+static uint64_t g_wqset_num = 0;
+struct g_wqset {
+ queue_chain_t link;
+ struct waitq_set *wqset;
+};
+
+static queue_head_t g_wqset_list;
+static struct waitq_set *g_waitq_set = NULL;
+
+static inline struct waitq_set *
+sysctl_get_wqset(int idx)
+{
+ struct g_wqset *gwqs;
+
+ if (!g_wqset_num) {
+ queue_init(&g_wqset_list);
+ }
+
+ /* don't bother with locks: this is test-only code! */
+ qe_foreach_element(gwqs, &g_wqset_list, link) {
+ if ((int)(wqset_id(gwqs->wqset) & 0xffffffff) == idx) {
+ return gwqs->wqset;
+ }
+ }
+
+ /* allocate a new one */
+ ++g_wqset_num;
+ gwqs = (struct g_wqset *)kalloc(sizeof(*gwqs));
+ assert(gwqs != NULL);
+
+ gwqs->wqset = waitq_set_alloc(SYNC_POLICY_FIFO | SYNC_POLICY_PREPOST, NULL);
+ enqueue_tail(&g_wqset_list, &gwqs->link);
+ printf("[WQ]: created new waitq set 0x%llx\n", wqset_id(gwqs->wqset));
+
+ return gwqs->wqset;
+}
+
+#define MAX_GLOBAL_TEST_QUEUES 64
+static int g_wq_init = 0;
+static struct waitq g_wq[MAX_GLOBAL_TEST_QUEUES];
+
+static inline struct waitq *
+global_test_waitq(int idx)
+{
+ if (idx < 0) {
+ return NULL;
+ }
+
+ if (!g_wq_init) {
+ g_wq_init = 1;
+ for (int i = 0; i < MAX_GLOBAL_TEST_QUEUES; i++) {
+ waitq_init(&g_wq[i], SYNC_POLICY_FIFO);
+ }
+ }
+
+ return &g_wq[idx % MAX_GLOBAL_TEST_QUEUES];
+}
+
+static int sysctl_waitq_wakeup_one SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error;
+ int index;
+ struct waitq *waitq;
+ kern_return_t kr;
+ int64_t event64 = 0;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+ }
+
+ if (event64 < 0) {
+ index = (int)((-event64) & 0xffffffff);
+ waitq = wqset_waitq(sysctl_get_wqset(index));
+ index = -index;
+ } else {
+ index = (int)event64;
+ waitq = global_test_waitq(index);
+ }
+
+ event64 = 0;
+
+ printf("[WQ]: Waking one thread on waitq [%d] event:0x%llx\n",
+ index, event64);
+ kr = waitq_wakeup64_one(waitq, (event64_t)event64, THREAD_AWAKENED,
+ WAITQ_ALL_PRIORITIES);
+ printf("[WQ]: \tkr=%d\n", kr);
+
+ return SYSCTL_OUT(req, &kr, sizeof(kr));
+}
+SYSCTL_PROC(_kern, OID_AUTO, waitq_wakeup_one, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_waitq_wakeup_one, "Q", "wakeup one thread waiting on given event");
+
+
+static int sysctl_waitq_wakeup_all SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error;
+ int index;
+ struct waitq *waitq;
+ kern_return_t kr;
+ int64_t event64 = 0;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+ }
+
+ if (event64 < 0) {
+ index = (int)((-event64) & 0xffffffff);
+ waitq = wqset_waitq(sysctl_get_wqset(index));
+ index = -index;
+ } else {
+ index = (int)event64;
+ waitq = global_test_waitq(index);
+ }
+
+ event64 = 0;
+
+ printf("[WQ]: Waking all threads on waitq [%d] event:0x%llx\n",
+ index, event64);
+ kr = waitq_wakeup64_all(waitq, (event64_t)event64,
+ THREAD_AWAKENED, WAITQ_ALL_PRIORITIES);
+ printf("[WQ]: \tkr=%d\n", kr);
+
+ return SYSCTL_OUT(req, &kr, sizeof(kr));
+}
+SYSCTL_PROC(_kern, OID_AUTO, waitq_wakeup_all, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_waitq_wakeup_all, "Q", "wakeup all threads waiting on given event");
+
+
+static int sysctl_waitq_wait SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error;
+ int index;
+ struct waitq *waitq;
+ kern_return_t kr;
+ int64_t event64 = 0;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+ }
+
+ if (event64 < 0) {
+ index = (int)((-event64) & 0xffffffff);
+ waitq = wqset_waitq(sysctl_get_wqset(index));
+ index = -index;
+ } else {
+ index = (int)event64;
+ waitq = global_test_waitq(index);
+ }
+
+ event64 = 0;
+
+ printf("[WQ]: Current thread waiting on waitq [%d] event:0x%llx\n",
+ index, event64);
+ kr = waitq_assert_wait64(waitq, (event64_t)event64, THREAD_INTERRUPTIBLE, 0);
+ if (kr == THREAD_WAITING) {
+ thread_block(THREAD_CONTINUE_NULL);
+ }
+ printf("[WQ]: \tWoke Up: kr=%d\n", kr);
+
+ return SYSCTL_OUT(req, &kr, sizeof(kr));
+}
+SYSCTL_PROC(_kern, OID_AUTO, waitq_wait, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_waitq_wait, "Q", "start waiting on given event");
+
+
+static int sysctl_wqset_select SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error;
+ struct waitq_set *wqset;
+ uint64_t event64 = 0;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ goto out;
+ }
+
+ wqset = sysctl_get_wqset((int)(event64 & 0xffffffff));
+ g_waitq_set = wqset;
+
+ event64 = wqset_id(wqset);
+ printf("[WQ]: selected wqset 0x%llx\n", event64);
+
+out:
+ if (g_waitq_set) {
+ event64 = wqset_id(g_waitq_set);
+ } else {
+ event64 = (uint64_t)(-1);
+ }
+
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+}
+SYSCTL_PROC(_kern, OID_AUTO, wqset_select, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_wqset_select, "Q", "select/create a global waitq set");
+
+
+static int sysctl_waitq_link SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error;
+ int index;
+ struct waitq *waitq;
+ struct waitq_set *wqset;
+ kern_return_t kr;
+ uint64_t reserved_link = 0;
+ int64_t event64 = 0;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+ }
+
+ if (!g_waitq_set) {
+ g_waitq_set = sysctl_get_wqset(1);
+ }
+ wqset = g_waitq_set;
+
+ if (event64 < 0) {
+ struct waitq_set *tmp;
+ index = (int)((-event64) & 0xffffffff);
+ tmp = sysctl_get_wqset(index);
+ if (tmp == wqset) {
+ goto out;
+ }
+ waitq = wqset_waitq(tmp);
+ index = -index;
+ } else {
+ index = (int)event64;
+ waitq = global_test_waitq(index);
+ }
+
+ printf("[WQ]: linking waitq [%d] to global wqset (0x%llx)\n",
+ index, wqset_id(wqset));
+ reserved_link = waitq_link_reserve(waitq);
+ kr = waitq_link(waitq, wqset, WAITQ_SHOULD_LOCK, &reserved_link);
+ waitq_link_release(reserved_link);
+
+ printf("[WQ]: \tkr=%d\n", kr);
+
+out:
+ return SYSCTL_OUT(req, &kr, sizeof(kr));
+}
+SYSCTL_PROC(_kern, OID_AUTO, waitq_link, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_waitq_link, "Q", "link global waitq to test waitq set");
+
+
+static int sysctl_waitq_unlink SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error;
+ int index;
+ struct waitq *waitq;
+ struct waitq_set *wqset;
+ kern_return_t kr;
+ uint64_t event64 = 0;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+ }
+
+ if (!g_waitq_set) {
+ g_waitq_set = sysctl_get_wqset(1);
+ }
+ wqset = g_waitq_set;
+
+ index = (int)event64;
+ waitq = global_test_waitq(index);
+
+ printf("[WQ]: unlinking waitq [%d] from global wqset (0x%llx)\n",
+ index, wqset_id(wqset));
+
+ kr = waitq_unlink(waitq, wqset);
+ printf("[WQ]: \tkr=%d\n", kr);
+
+ return SYSCTL_OUT(req, &kr, sizeof(kr));
+}
+SYSCTL_PROC(_kern, OID_AUTO, waitq_unlink, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_waitq_unlink, "Q", "unlink global waitq from test waitq set");
+
+
+static int sysctl_waitq_clear_prepost SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ struct waitq *waitq;
+ uint64_t event64 = 0;
+ int error, index;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+ }
+
+ index = (int)event64;
+ waitq = global_test_waitq(index);
+
+ printf("[WQ]: clearing prepost on waitq [%d]\n", index);
+ waitq_clear_prepost(waitq);
+
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+}
+SYSCTL_PROC(_kern, OID_AUTO, waitq_clear_prepost, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_waitq_clear_prepost, "Q", "clear prepost on given waitq");
+
+
+static int sysctl_wqset_unlink_all SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error;
+ struct waitq_set *wqset;
+ kern_return_t kr;
+ uint64_t event64 = 0;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+ }
+
+ if (!g_waitq_set) {
+ g_waitq_set = sysctl_get_wqset(1);
+ }
+ wqset = g_waitq_set;
+
+ printf("[WQ]: unlinking all queues from global wqset (0x%llx)\n",
+ wqset_id(wqset));
+
+ kr = waitq_set_unlink_all(wqset);
+ printf("[WQ]: \tkr=%d\n", kr);
+
+ return SYSCTL_OUT(req, &kr, sizeof(kr));
+}
+SYSCTL_PROC(_kern, OID_AUTO, wqset_unlink_all, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_wqset_unlink_all, "Q", "unlink all queues from test waitq set");
+
+
+static int sysctl_wqset_clear_preposts SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ struct waitq_set *wqset = NULL;
+ uint64_t event64 = 0;
+ int error, index;
+
+ error = SYSCTL_IN(req, &event64, sizeof(event64));
+ if (error) {
+ return error;
+ }
+
+ if (!req->newptr) {
+ goto out;
+ }
+
+ index = (int)((event64) & 0xffffffff);
+ wqset = sysctl_get_wqset(index);
+ assert(wqset != NULL);
+
+ printf("[WQ]: clearing preposts on wqset 0x%llx\n", wqset_id(wqset));
+ waitq_set_clear_preposts(wqset);
+
+out:
+ if (wqset) {
+ event64 = wqset_id(wqset);
+ } else {
+ event64 = (uint64_t)(-1);
+ }
+
+ return SYSCTL_OUT(req, &event64, sizeof(event64));
+}
+SYSCTL_PROC(_kern, OID_AUTO, wqset_clear_preposts, CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_wqset_clear_preposts, "Q", "clear preposts on given waitq set");
+
+#endif /* CONFIG_WAITQ_DEBUG */
+
+static int
+sysctl_waitq_set_nelem SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int nelem;
+
+ /* Read only */
+ if (req->newptr != USER_ADDR_NULL) {
+ return EPERM;
+ }
+
+ nelem = sysctl_helper_waitq_set_nelem();
+
+ return SYSCTL_OUT(req, &nelem, sizeof(nelem));
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, n_ltable_entries, CTLFLAG_RD | CTLFLAG_LOCKED,
+ 0, 0, sysctl_waitq_set_nelem, "I", "ltable elementis currently used");
+
+
+#endif /* DEVELOPMENT || DEBUG */
+
+/*Remote Time api*/
+SYSCTL_NODE(_machdep, OID_AUTO, remotetime, CTLFLAG_RD | CTLFLAG_LOCKED, 0, "Remote time api");
+
+#if DEVELOPMENT || DEBUG
+#if CONFIG_MACH_BRIDGE_SEND_TIME
+extern _Atomic uint32_t bt_init_flag;
+extern uint32_t mach_bridge_timer_enable(uint32_t, int);
+
+SYSCTL_INT(_machdep_remotetime, OID_AUTO, bridge_timer_init_flag,
+ CTLFLAG_RD | CTLFLAG_LOCKED, &bt_init_flag, 0, "");
+
+static int sysctl_mach_bridge_timer_enable SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ uint32_t value = 0;
+ int error = 0;
+ /* User is querying buffer size */
+ if (req->oldptr == USER_ADDR_NULL && req->newptr == USER_ADDR_NULL) {
+ req->oldidx = sizeof(value);
+ return 0;
+ }
+ if (bt_init_flag) {
+ if (req->newptr) {
+ int new_value = 0;
+ error = SYSCTL_IN(req, &new_value, sizeof(new_value));
+ if (error) {
+ return error;
+ }
+ if (new_value == 0 || new_value == 1) {
+ value = mach_bridge_timer_enable(new_value, 1);
+ } else {
+ return EPERM;
+ }
+ } else {
+ value = mach_bridge_timer_enable(0, 0);
+ }
+ }
+ error = SYSCTL_OUT(req, &value, sizeof(value));
+ return error;
+}
+
+SYSCTL_PROC(_machdep_remotetime, OID_AUTO, bridge_timer_enable,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_mach_bridge_timer_enable, "I", "");
+
+#endif /* CONFIG_MACH_BRIDGE_SEND_TIME */
+
+static int sysctl_mach_bridge_remote_time SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ uint64_t ltime = 0, rtime = 0;
+ if (req->oldptr == USER_ADDR_NULL) {
+ req->oldidx = sizeof(rtime);
+ return 0;
+ }
+ if (req->newptr) {
+ int error = SYSCTL_IN(req, <ime, sizeof(ltime));
+ if (error) {
+ return error;
+ }
+ }
+ rtime = mach_bridge_remote_time(ltime);
+ return SYSCTL_OUT(req, &rtime, sizeof(rtime));
+}
+SYSCTL_PROC(_machdep_remotetime, OID_AUTO, mach_bridge_remote_time,
+ CTLTYPE_QUAD | CTLFLAG_RW | CTLFLAG_LOCKED,
+ 0, 0, sysctl_mach_bridge_remote_time, "Q", "");
+
+#endif /* DEVELOPMENT || DEBUG */
+
+#if CONFIG_MACH_BRIDGE_RECV_TIME
+extern struct bt_params bt_params_get_latest(void);
+
+static int sysctl_mach_bridge_conversion_params SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ struct bt_params params = {};
+ if (req->oldptr == USER_ADDR_NULL) {
+ req->oldidx = sizeof(struct bt_params);
+ return 0;
+ }
+ if (req->newptr) {
+ return EPERM;
+ }
+ params = bt_params_get_latest();
+ return SYSCTL_OUT(req, ¶ms, MIN(sizeof(params), req->oldlen));
+}
+
+SYSCTL_PROC(_machdep_remotetime, OID_AUTO, conversion_params,
+ CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED, 0,
+ 0, sysctl_mach_bridge_conversion_params, "S,bt_params", "");
+
+#endif /* CONFIG_MACH_BRIDGE_RECV_TIME */
+
+
+
+static int
+sysctl_kern_tcsm_available SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ uint32_t value = machine_csv(CPUVN_CI) ? 1 : 0;
+
+ if (req->newptr) {
+ return EINVAL;
+ }
+
+ return SYSCTL_OUT(req, &value, sizeof(value));
+}
+SYSCTL_PROC(_kern, OID_AUTO, tcsm_available,
+ CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_LOCKED | CTLFLAG_MASKED | CTLFLAG_ANYBODY,
+ 0, 0, sysctl_kern_tcsm_available, "I", "");
+
+
+static int
+sysctl_kern_tcsm_enable SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ uint32_t soflags = 0;
+ uint32_t old_value = thread_get_no_smt() ? 1 : 0;
+
+ int error = SYSCTL_IN(req, &soflags, sizeof(soflags));
+ if (error) {
+ return error;
+ }
+
+ if (soflags && machine_csv(CPUVN_CI)) {
+ thread_set_no_smt(true);
+ machine_tecs(current_thread());
+ }
+
+ return SYSCTL_OUT(req, &old_value, sizeof(old_value));
+}
+SYSCTL_PROC(_kern, OID_AUTO, tcsm_enable,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED | CTLFLAG_MASKED | CTLFLAG_ANYBODY,
+ 0, 0, sysctl_kern_tcsm_enable, "I", "");
+
+
+#if DEVELOPMENT || DEBUG
+extern void sysctl_task_set_no_smt(char no_smt);
+extern char sysctl_task_get_no_smt(void);
+
+static int
+sysctl_kern_sched_task_set_no_smt SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ char buff[4];
+
+ int error = SYSCTL_IN(req, buff, 1);
+ if (error) {
+ return error;
+ }
+ char no_smt = buff[0];
+
+ if (!req->newptr) {
+ goto out;
+ }
+
+ sysctl_task_set_no_smt(no_smt);
+out:
+ no_smt = sysctl_task_get_no_smt();
+ buff[0] = no_smt;
+
+ return SYSCTL_OUT(req, buff, 1);
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, sched_task_set_no_smt, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_LOCKED | CTLFLAG_ANYBODY,
+ 0, 0, sysctl_kern_sched_task_set_no_smt, "A", "");
+
+static int
+sysctl_kern_sched_thread_set_no_smt(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
+{
+ int new_value, changed;
+ int old_value = thread_get_no_smt() ? 1 : 0;
+ int error = sysctl_io_number(req, old_value, sizeof(int), &new_value, &changed);
+
+ if (changed) {
+ thread_set_no_smt(!!new_value);
+ }
+
+ return error;
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, sched_thread_set_no_smt,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED | CTLFLAG_ANYBODY,
+ 0, 0, sysctl_kern_sched_thread_set_no_smt, "I", "");
+#endif