1*295f1023SBrooks Davis /*-
2*295f1023SBrooks Davis * Copyright (c) 2026 Capabilities Limited
3*295f1023SBrooks Davis *
4*295f1023SBrooks Davis * SPDX-License-Identifier: BSD-2-Clause
5*295f1023SBrooks Davis *
6*295f1023SBrooks Davis * This software was developed by Capabilities Limited with funding from
7*295f1023SBrooks Davis * Innovate UK and the Department for Science, Innovation and Technology
8*295f1023SBrooks Davis * for the adoption and diffusion of CHERI technology under project
9*295f1023SBrooks Davis * 10168042 (“CheriBSD feature extraction, maturity, and testing”).
10*295f1023SBrooks Davis *
11*295f1023SBrooks Davis */
12*295f1023SBrooks Davis
13*295f1023SBrooks Davis #define EXTERR_CATEGORY_DYNAMIC "kern/subr_exterr.c"
14*295f1023SBrooks Davis
15*295f1023SBrooks Davis #include <sys/param.h>
16*295f1023SBrooks Davis #include <sys/exterrvar.h>
17*295f1023SBrooks Davis #include <sys/exterr_cat.h>
18*295f1023SBrooks Davis #include <sys/kernel.h>
19*295f1023SBrooks Davis #include <sys/libkern.h>
20*295f1023SBrooks Davis #include <sys/linker_set.h>
21*295f1023SBrooks Davis #include <sys/malloc.h>
22*295f1023SBrooks Davis #include <sys/linker.h> /* Need MALLOC_DECLARE */
23*295f1023SBrooks Davis #include <sys/rwlock.h>
24*295f1023SBrooks Davis #include <sys/stddef.h>
25*295f1023SBrooks Davis #include <sys/sysctl.h>
26*295f1023SBrooks Davis
27*295f1023SBrooks Davis struct exterr_cat_span {
28*295f1023SBrooks Davis unsigned int first;
29*295f1023SBrooks Davis unsigned int count;
30*295f1023SBrooks Davis struct exterr_cat **cat_sets;
31*295f1023SBrooks Davis TAILQ_ENTRY(exterr_cat_span) entries;
32*295f1023SBrooks Davis };
33*295f1023SBrooks Davis
34*295f1023SBrooks Davis TAILQ_HEAD(exterr_cat_span_head, exterr_cat_span) cat_span_head;
35*295f1023SBrooks Davis
36*295f1023SBrooks Davis SET_DECLARE(exterr_cats, struct exterr_cat);
37*295f1023SBrooks Davis
38*295f1023SBrooks Davis static struct exterr_cat_span kern_cats;
39*295f1023SBrooks Davis unsigned int ncats;
40*295f1023SBrooks Davis struct rwlock cat_lock;
41*295f1023SBrooks Davis
42*295f1023SBrooks Davis static bool
exterr_cat_register_set(struct exterr_cat_span * span,struct exterr_cat ** start,struct exterr_cat ** stop)43*295f1023SBrooks Davis exterr_cat_register_set(struct exterr_cat_span *span, struct exterr_cat **start,
44*295f1023SBrooks Davis struct exterr_cat **stop)
45*295f1023SBrooks Davis {
46*295f1023SBrooks Davis struct exterr_cat **catp;
47*295f1023SBrooks Davis ptrdiff_t count;
48*295f1023SBrooks Davis
49*295f1023SBrooks Davis count = stop - start;
50*295f1023SBrooks Davis if (count < 1)
51*295f1023SBrooks Davis return (true);
52*295f1023SBrooks Davis
53*295f1023SBrooks Davis rw_wlock(&cat_lock);
54*295f1023SBrooks Davis
55*295f1023SBrooks Davis if (ncats + count < ncats) {
56*295f1023SBrooks Davis printf("too many exterror categories\n");
57*295f1023SBrooks Davis rw_wunlock(&cat_lock);
58*295f1023SBrooks Davis return (false);
59*295f1023SBrooks Davis }
60*295f1023SBrooks Davis
61*295f1023SBrooks Davis span->first = ncats + 1;
62*295f1023SBrooks Davis for (catp = start; catp < stop; catp++)
63*295f1023SBrooks Davis (*catp)->cat = ++ncats;
64*295f1023SBrooks Davis span->count = count;
65*295f1023SBrooks Davis span->cat_sets = start;
66*295f1023SBrooks Davis TAILQ_INSERT_TAIL(&cat_span_head, span, entries);
67*295f1023SBrooks Davis
68*295f1023SBrooks Davis rw_wunlock(&cat_lock);
69*295f1023SBrooks Davis
70*295f1023SBrooks Davis return (true);
71*295f1023SBrooks Davis }
72*295f1023SBrooks Davis
73*295f1023SBrooks Davis void
exterr_cat_register_module(struct exterr_cat ** start,struct exterr_cat ** stop)74*295f1023SBrooks Davis exterr_cat_register_module(struct exterr_cat **start, struct exterr_cat **stop)
75*295f1023SBrooks Davis {
76*295f1023SBrooks Davis struct exterr_cat_span *span;
77*295f1023SBrooks Davis
78*295f1023SBrooks Davis span = malloc(sizeof(*span), M_LINKER, M_WAITOK | M_ZERO);
79*295f1023SBrooks Davis if (!exterr_cat_register_set(span, start, stop))
80*295f1023SBrooks Davis free(span, M_LINKER);
81*295f1023SBrooks Davis }
82*295f1023SBrooks Davis
83*295f1023SBrooks Davis void
exterr_cat_unregister_module(struct exterr_cat ** start,struct exterr_cat ** stop)84*295f1023SBrooks Davis exterr_cat_unregister_module(struct exterr_cat **start,
85*295f1023SBrooks Davis struct exterr_cat **stop)
86*295f1023SBrooks Davis {
87*295f1023SBrooks Davis struct exterr_cat_span *span;
88*295f1023SBrooks Davis
89*295f1023SBrooks Davis if (stop - start < 1)
90*295f1023SBrooks Davis return;
91*295f1023SBrooks Davis
92*295f1023SBrooks Davis rw_wlock(&cat_lock);
93*295f1023SBrooks Davis
94*295f1023SBrooks Davis TAILQ_FOREACH(span, &cat_span_head, entries) {
95*295f1023SBrooks Davis if (span->cat_sets == start) {
96*295f1023SBrooks Davis MPASS(span->first > 1);
97*295f1023SBrooks Davis MPASS(span->count == stop - start);
98*295f1023SBrooks Davis TAILQ_REMOVE(&cat_span_head, span, entries);
99*295f1023SBrooks Davis break;
100*295f1023SBrooks Davis }
101*295f1023SBrooks Davis }
102*295f1023SBrooks Davis KASSERT(span != NULL, ("start not found in spans"));
103*295f1023SBrooks Davis
104*295f1023SBrooks Davis /*
105*295f1023SBrooks Davis * NB: we leak category numbers on module unload because we can't
106*295f1023SBrooks Davis * reasonably know which ones are in use in running software.
107*295f1023SBrooks Davis */
108*295f1023SBrooks Davis
109*295f1023SBrooks Davis rw_wunlock(&cat_lock);
110*295f1023SBrooks Davis
111*295f1023SBrooks Davis free(span, M_LINKER);
112*295f1023SBrooks Davis }
113*295f1023SBrooks Davis
114*295f1023SBrooks Davis static void
exterr_cat_register_kern(void * arg)115*295f1023SBrooks Davis exterr_cat_register_kern(void *arg)
116*295f1023SBrooks Davis {
117*295f1023SBrooks Davis rw_init(&cat_lock, "exterr dynamic categories");
118*295f1023SBrooks Davis
119*295f1023SBrooks Davis TAILQ_INIT(&cat_span_head);
120*295f1023SBrooks Davis
121*295f1023SBrooks Davis if (SET_COUNT(exterr_cats) == 0)
122*295f1023SBrooks Davis return;
123*295f1023SBrooks Davis
124*295f1023SBrooks Davis exterr_cat_register_set(&kern_cats, SET_BEGIN(exterr_cats),
125*295f1023SBrooks Davis SET_LIMIT(exterr_cats));
126*295f1023SBrooks Davis }
127*295f1023SBrooks Davis SYSINIT(exterr, SI_SUB_KMEM, SI_ORDER_FIRST, exterr_cat_register_kern, NULL);
128*295f1023SBrooks Davis
129*295f1023SBrooks Davis static int
sysctl_exterr_categories(SYSCTL_HANDLER_ARGS)130*295f1023SBrooks Davis sysctl_exterr_categories(SYSCTL_HANDLER_ARGS)
131*295f1023SBrooks Davis {
132*295f1023SBrooks Davis struct exterr_cat_span *span;
133*295f1023SBrooks Davis const struct exterr_cat *cat = NULL;
134*295f1023SBrooks Davis int idx;
135*295f1023SBrooks Davis
136*295f1023SBrooks Davis if (arg2 != 1)
137*295f1023SBrooks Davis return (EXTERROR(EINVAL,
138*295f1023SBrooks Davis "too many args to kern.exterr.categories %d", arg2));
139*295f1023SBrooks Davis
140*295f1023SBrooks Davis idx = *(int *)arg1;
141*295f1023SBrooks Davis
142*295f1023SBrooks Davis rw_rlock(&cat_lock);
143*295f1023SBrooks Davis if (idx < 1 || idx > ncats) {
144*295f1023SBrooks Davis rw_runlock(&cat_lock);
145*295f1023SBrooks Davis return (EXTERROR(EINVAL, "category %d out of range (1...%d)",
146*295f1023SBrooks Davis idx, ncats));
147*295f1023SBrooks Davis }
148*295f1023SBrooks Davis
149*295f1023SBrooks Davis TAILQ_FOREACH(span, &cat_span_head, entries) {
150*295f1023SBrooks Davis if (idx < span->first)
151*295f1023SBrooks Davis break; /* Not here any more */
152*295f1023SBrooks Davis
153*295f1023SBrooks Davis if (idx < span->first + span->count)
154*295f1023SBrooks Davis cat = span->cat_sets[idx - span->first];
155*295f1023SBrooks Davis }
156*295f1023SBrooks Davis rw_runlock(&cat_lock);
157*295f1023SBrooks Davis
158*295f1023SBrooks Davis if (cat == NULL)
159*295f1023SBrooks Davis return (EXTERROR(ENOENT, "category not found %d", idx));
160*295f1023SBrooks Davis MPASS(cat->cat == idx);
161*295f1023SBrooks Davis return (SYSCTL_OUT(req, cat->file, strlen(cat->file) + 1));
162*295f1023SBrooks Davis }
163*295f1023SBrooks Davis
164*295f1023SBrooks Davis SYSCTL_NODE(_kern, OID_AUTO, exterr, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
165*295f1023SBrooks Davis "Extended error information");
166*295f1023SBrooks Davis SYSCTL_UINT(_kern_exterr, OID_AUTO, ncategories, CTLFLAG_RD | CTLFLAG_MPSAFE,
167*295f1023SBrooks Davis &ncats, 0, "Number of dynamic categories");
168*295f1023SBrooks Davis SYSCTL_NODE(_kern_exterr, OID_AUTO, categories, CTLFLAG_RD | CTLFLAG_MPSAFE,
169*295f1023SBrooks Davis sysctl_exterr_categories, "Extended error categories");
170