xref: /freebsd/sys/kern/subr_exterr.c (revision 295f10230903d54c700518467c4ea4492f5c4faa)
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