1*208170f3Sskrll /* $NetBSD: kern_history.c,v 1.19 2019/10/09 05:59:51 skrll Exp $ */
28169e469Smrg
38169e469Smrg /*
48169e469Smrg * Copyright (c) 1997 Charles D. Cranor and Washington University.
58169e469Smrg * All rights reserved.
68169e469Smrg *
78169e469Smrg * Redistribution and use in source and binary forms, with or without
88169e469Smrg * modification, are permitted provided that the following conditions
98169e469Smrg * are met:
108169e469Smrg * 1. Redistributions of source code must retain the above copyright
118169e469Smrg * notice, this list of conditions and the following disclaimer.
128169e469Smrg * 2. Redistributions in binary form must reproduce the above copyright
138169e469Smrg * notice, this list of conditions and the following disclaimer in the
148169e469Smrg * documentation and/or other materials provided with the distribution.
158169e469Smrg *
168169e469Smrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178169e469Smrg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188169e469Smrg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198169e469Smrg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208169e469Smrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
218169e469Smrg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
228169e469Smrg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
238169e469Smrg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
248169e469Smrg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
258169e469Smrg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
268169e469Smrg *
278169e469Smrg * from: NetBSD: uvm_stat.c,v 1.36 2011/02/02 15:13:34 chuck Exp
288169e469Smrg * from: Id: uvm_stat.c,v 1.1.2.3 1997/12/19 15:01:00 mrg Exp
298169e469Smrg */
308169e469Smrg
318169e469Smrg /*
328169e469Smrg * subr_kernhist.c
338169e469Smrg */
348169e469Smrg
358169e469Smrg #include <sys/cdefs.h>
36*208170f3Sskrll __KERNEL_RCSID(0, "$NetBSD: kern_history.c,v 1.19 2019/10/09 05:59:51 skrll Exp $");
378169e469Smrg
388169e469Smrg #include "opt_ddb.h"
390895dad1Sskrll #include "opt_kernhist.h"
40679d7c25Smrg #include "opt_syscall_debug.h"
410895dad1Sskrll #include "opt_usb.h"
420895dad1Sskrll #include "opt_uvmhist.h"
437f0851ceSpgoyette #include "opt_biohist.h"
44c2efd8c9Spgoyette #include "opt_sysctl.h"
458169e469Smrg
464869ce0aSpgoyette #include <sys/atomic.h>
478169e469Smrg #include <sys/param.h>
488169e469Smrg #include <sys/systm.h>
498169e469Smrg #include <sys/cpu.h>
50c2efd8c9Spgoyette #include <sys/sysctl.h>
518169e469Smrg #include <sys/kernhist.h>
52c2efd8c9Spgoyette #include <sys/kmem.h>
538169e469Smrg
54679d7c25Smrg #ifdef UVMHIST
558169e469Smrg #include <uvm/uvm.h>
56679d7c25Smrg #endif
57679d7c25Smrg
58679d7c25Smrg #ifdef USB_DEBUG
59679d7c25Smrg #include <dev/usb/usbhist.h>
60679d7c25Smrg #endif
61679d7c25Smrg
627f0851ceSpgoyette #ifdef BIOHIST
63d05a55c8Spgoyette #include <sys/biohist.h>
647f0851ceSpgoyette #endif
657f0851ceSpgoyette
66679d7c25Smrg #ifdef SYSCALL_DEBUG
67679d7c25Smrg KERNHIST_DECL(scdebughist);
68679d7c25Smrg #endif
698169e469Smrg
70c2efd8c9Spgoyette struct addr_xlt {
71c2efd8c9Spgoyette const char *addr;
72c2efd8c9Spgoyette size_t len;
73c2efd8c9Spgoyette uint32_t offset;
74c2efd8c9Spgoyette };
75c2efd8c9Spgoyette
768169e469Smrg /*
778169e469Smrg * globals
788169e469Smrg */
798169e469Smrg
808169e469Smrg struct kern_history_head kern_histories;
815a30768dSpgoyette bool kernhist_sysctl_ready = 0;
828169e469Smrg
838169e469Smrg int kernhist_print_enabled = 1;
848169e469Smrg
85c2efd8c9Spgoyette int sysctl_hist_node;
86c2efd8c9Spgoyette
87f8968117Schristos static int sysctl_kernhist_helper(SYSCTLFN_PROTO);
88f8968117Schristos
898169e469Smrg #ifdef DDB
908169e469Smrg
918169e469Smrg /*
928169e469Smrg * prototypes
938169e469Smrg */
948169e469Smrg
95ba0c19d4Smrg void kernhist_dump(struct kern_history *, size_t count,
961a04d8c9Sskrll void (*)(const char *, ...) __printflike(1, 2));
97ba0c19d4Smrg static void kernhist_info(struct kern_history *,
98ba0c19d4Smrg void (*)(const char *, ...));
99c2efd8c9Spgoyette void kernhist_dumpmask(uint32_t);
100ba0c19d4Smrg static void kernhist_dump_histories(struct kern_history *[], size_t count,
1011a04d8c9Sskrll void (*)(const char *, ...) __printflike(1, 2));
1028169e469Smrg
103ba0c19d4Smrg /* display info about one kernhist */
104ba0c19d4Smrg static void
kernhist_info(struct kern_history * l,void (* pr)(const char *,...))105ba0c19d4Smrg kernhist_info(struct kern_history *l, void (*pr)(const char *, ...))
106ba0c19d4Smrg {
107ba0c19d4Smrg
108ba0c19d4Smrg pr("kernhist '%s': at %p total %u next free %u\n",
109ba0c19d4Smrg l->name, l, l->n, l->f);
110ba0c19d4Smrg }
1118169e469Smrg
1128169e469Smrg /*
1138169e469Smrg * call this from ddb
1148169e469Smrg *
1158169e469Smrg * expects the system to be quiesced, no locking
1168169e469Smrg */
1178169e469Smrg void
kernhist_dump(struct kern_history * l,size_t count,void (* pr)(const char *,...))118ba0c19d4Smrg kernhist_dump(struct kern_history *l, size_t count,
119ba0c19d4Smrg void (*pr)(const char *, ...))
1208169e469Smrg {
1218169e469Smrg int lcv;
1228169e469Smrg
1238169e469Smrg lcv = l->f;
124ba0c19d4Smrg if (count > l->n)
125ba0c19d4Smrg pr("%s: count %zu > size %u\n", __func__, count, l->n);
126ba0c19d4Smrg else if (count)
127ba0c19d4Smrg lcv = (lcv - count) % l->n;
128ba0c19d4Smrg
1298169e469Smrg do {
1308169e469Smrg if (l->e[lcv].fmt)
1311a04d8c9Sskrll kernhist_entry_print(&l->e[lcv], pr);
1328169e469Smrg lcv = (lcv + 1) % l->n;
1338169e469Smrg } while (lcv != l->f);
1348169e469Smrg }
1358169e469Smrg
1368169e469Smrg /*
137ba0c19d4Smrg * print a merged list of kern_history structures. count is unused so far.
1388169e469Smrg */
1398169e469Smrg static void
kernhist_dump_histories(struct kern_history * hists[],size_t count,void (* pr)(const char *,...))140ba0c19d4Smrg kernhist_dump_histories(struct kern_history *hists[], size_t count,
141ba0c19d4Smrg void (*pr)(const char *, ...))
1428169e469Smrg {
143c9b6361bSpgoyette struct bintime bt;
1448169e469Smrg int cur[MAXHISTS];
1458169e469Smrg int lcv, hi;
1468169e469Smrg
1478169e469Smrg /* find the first of each list */
1488169e469Smrg for (lcv = 0; hists[lcv]; lcv++)
1498169e469Smrg cur[lcv] = hists[lcv]->f;
1508169e469Smrg
1518169e469Smrg /*
1528169e469Smrg * here we loop "forever", finding the next earliest
1538169e469Smrg * history entry and printing it. cur[X] is the current
1548169e469Smrg * entry to test for the history in hists[X]. if it is
1558169e469Smrg * -1, then this history is finished.
1568169e469Smrg */
1578169e469Smrg for (;;) {
1588169e469Smrg hi = -1;
159c9b6361bSpgoyette bt.sec = 0; bt.frac = 0;
1608169e469Smrg
1618169e469Smrg /* loop over each history */
1628169e469Smrg for (lcv = 0; hists[lcv]; lcv++) {
1638169e469Smrg restart:
1648169e469Smrg if (cur[lcv] == -1)
1658169e469Smrg continue;
166679d7c25Smrg if (!hists[lcv]->e)
167679d7c25Smrg continue;
1688169e469Smrg
1698169e469Smrg /*
1708169e469Smrg * if the format is empty, go to the next entry
1718169e469Smrg * and retry.
1728169e469Smrg */
1738169e469Smrg if (hists[lcv]->e[cur[lcv]].fmt == NULL) {
1748169e469Smrg cur[lcv] = (cur[lcv] + 1) % (hists[lcv]->n);
1758169e469Smrg if (cur[lcv] == hists[lcv]->f)
1768169e469Smrg cur[lcv] = -1;
1778169e469Smrg goto restart;
1788169e469Smrg }
1798169e469Smrg
1808169e469Smrg /*
1818169e469Smrg * if the time hasn't been set yet, or this entry is
182c9b6361bSpgoyette * earlier than the current bt, set the time and history
1838169e469Smrg * index.
1848169e469Smrg */
185c9b6361bSpgoyette if (bt.sec == 0 ||
186c9b6361bSpgoyette bintimecmp(&hists[lcv]->e[cur[lcv]].bt, &bt, <)) {
187c9b6361bSpgoyette bt = hists[lcv]->e[cur[lcv]].bt;
1888169e469Smrg hi = lcv;
1898169e469Smrg }
1908169e469Smrg }
1918169e469Smrg
1928169e469Smrg /* if we didn't find any entries, we must be done */
1938169e469Smrg if (hi == -1)
1948169e469Smrg break;
1958169e469Smrg
1968169e469Smrg /* print and move to the next entry */
1971a04d8c9Sskrll kernhist_entry_print(&hists[hi]->e[cur[hi]], pr);
198ba0c19d4Smrg
1998169e469Smrg cur[hi] = (cur[hi] + 1) % (hists[hi]->n);
2008169e469Smrg if (cur[hi] == hists[hi]->f)
2018169e469Smrg cur[hi] = -1;
2028169e469Smrg }
2038169e469Smrg }
2048169e469Smrg
2058169e469Smrg /*
2068169e469Smrg * call this from ddb. `bitmask' is from <sys/kernhist.h>. it
2078169e469Smrg * merges the named histories.
2088169e469Smrg *
2098169e469Smrg * expects the system to be quiesced, no locking
2108169e469Smrg */
2118169e469Smrg void
kernhist_dumpmask(uint32_t bitmask)212c2efd8c9Spgoyette kernhist_dumpmask(uint32_t bitmask) /* XXX only support 32 hists */
2138169e469Smrg {
2148169e469Smrg struct kern_history *hists[MAXHISTS + 1];
2158169e469Smrg int i = 0;
2168169e469Smrg
2178169e469Smrg #ifdef UVMHIST
2188169e469Smrg if ((bitmask & KERNHIST_UVMMAPHIST) || bitmask == 0)
2198169e469Smrg hists[i++] = &maphist;
2208169e469Smrg
2218169e469Smrg if ((bitmask & KERNHIST_UVMPDHIST) || bitmask == 0)
2228169e469Smrg hists[i++] = &pdhist;
2238169e469Smrg
2248169e469Smrg if ((bitmask & KERNHIST_UVMUBCHIST) || bitmask == 0)
2258169e469Smrg hists[i++] = &ubchist;
2268169e469Smrg
2278169e469Smrg if ((bitmask & KERNHIST_UVMLOANHIST) || bitmask == 0)
2288169e469Smrg hists[i++] = &loanhist;
2298169e469Smrg #endif
2308169e469Smrg
231679d7c25Smrg #ifdef USB_DEBUG
232679d7c25Smrg if ((bitmask & KERNHIST_USBHIST) || bitmask == 0)
233679d7c25Smrg hists[i++] = &usbhist;
234679d7c25Smrg #endif
235679d7c25Smrg
236679d7c25Smrg #ifdef SYSCALL_DEBUG
237679d7c25Smrg if ((bitmask & KERNHIST_SCDEBUGHIST) || bitmask == 0)
238679d7c25Smrg hists[i++] = &scdebughist;
239679d7c25Smrg #endif
240679d7c25Smrg
2417f0851ceSpgoyette #ifdef BIOHIST
2427f0851ceSpgoyette if ((bitmask & KERNHIST_BIOHIST) || bitmask == 0)
2437f0851ceSpgoyette hists[i++] = &biohist;
2447f0851ceSpgoyette #endif
2457f0851ceSpgoyette
2468169e469Smrg hists[i] = NULL;
2478169e469Smrg
248ba0c19d4Smrg kernhist_dump_histories(hists, 0, printf);
2498169e469Smrg }
2508169e469Smrg
2518169e469Smrg /*
252ba0c19d4Smrg * kernhist_print: ddb hook to print kern history.
2538169e469Smrg */
2548169e469Smrg void
255ba0c19d4Smrg kernhist_print(void *addr, size_t count, const char *modif,
256ba0c19d4Smrg void (*pr)(const char *, ...) __printflike(1,2))
2578169e469Smrg {
2581a04d8c9Sskrll struct kern_history *h;
2591a04d8c9Sskrll
2601a04d8c9Sskrll LIST_FOREACH(h, &kern_histories, list) {
2611a04d8c9Sskrll if (h == addr)
2621a04d8c9Sskrll break;
2631a04d8c9Sskrll }
2641a04d8c9Sskrll
2651a04d8c9Sskrll if (h == NULL) {
2661a04d8c9Sskrll struct kern_history *hists[MAXHISTS + 1];
2671a04d8c9Sskrll int i = 0;
2681a04d8c9Sskrll #ifdef UVMHIST
2691a04d8c9Sskrll hists[i++] = &maphist;
2701a04d8c9Sskrll hists[i++] = &pdhist;
2711a04d8c9Sskrll hists[i++] = &ubchist;
2721a04d8c9Sskrll hists[i++] = &loanhist;
2731a04d8c9Sskrll #endif
2741a04d8c9Sskrll #ifdef USB_DEBUG
2751a04d8c9Sskrll hists[i++] = &usbhist;
2761a04d8c9Sskrll #endif
2771a04d8c9Sskrll
2781a04d8c9Sskrll #ifdef SYSCALL_DEBUG
2791a04d8c9Sskrll hists[i++] = &scdebughist;
2801a04d8c9Sskrll #endif
2817f0851ceSpgoyette #ifdef BIOHIST
2827f0851ceSpgoyette hists[i++] = &biohist;
2837f0851ceSpgoyette #endif
2841a04d8c9Sskrll hists[i] = NULL;
2851a04d8c9Sskrll
286ba0c19d4Smrg if (*modif == 'i') {
287ba0c19d4Smrg int lcv;
288ba0c19d4Smrg
289ba0c19d4Smrg for (lcv = 0; hists[lcv]; lcv++)
290ba0c19d4Smrg kernhist_info(hists[lcv], pr);
2911a04d8c9Sskrll } else {
292ba0c19d4Smrg kernhist_dump_histories(hists, count, pr);
293ba0c19d4Smrg }
294ba0c19d4Smrg } else {
295ba0c19d4Smrg if (*modif == 'i')
296ba0c19d4Smrg kernhist_info(h, pr);
297ba0c19d4Smrg else
298ba0c19d4Smrg kernhist_dump(h, count, pr);
2991a04d8c9Sskrll }
3008169e469Smrg }
3018169e469Smrg
3028169e469Smrg #endif
303c2efd8c9Spgoyette
304c2efd8c9Spgoyette /*
305c2efd8c9Spgoyette * sysctl interface
306c2efd8c9Spgoyette */
307c2efd8c9Spgoyette
308c2efd8c9Spgoyette /*
3095a30768dSpgoyette * sysctl_kernhist_new()
310c2efd8c9Spgoyette *
3115a30768dSpgoyette * If the specified history (or, if no history is specified, any
3125a30768dSpgoyette * history) does not already have a sysctl node (under kern.hist)
3135a30768dSpgoyette * we create a new one and record it's node number.
314c2efd8c9Spgoyette */
3155a30768dSpgoyette void
sysctl_kernhist_new(struct kern_history * hist)3165a30768dSpgoyette sysctl_kernhist_new(struct kern_history *hist)
317c2efd8c9Spgoyette {
318c2efd8c9Spgoyette int error;
319c2efd8c9Spgoyette struct kern_history *h;
320c2efd8c9Spgoyette const struct sysctlnode *rnode = NULL;
321c2efd8c9Spgoyette
3224869ce0aSpgoyette membar_consumer();
3235a30768dSpgoyette if (kernhist_sysctl_ready == 0)
3245a30768dSpgoyette return;
3255a30768dSpgoyette
326c2efd8c9Spgoyette LIST_FOREACH(h, &kern_histories, list) {
3275a30768dSpgoyette if (hist && h != hist)
3285a30768dSpgoyette continue;
329c2efd8c9Spgoyette if (h->s != 0)
330c2efd8c9Spgoyette continue;
331c2efd8c9Spgoyette error = sysctl_createv(NULL, 0, NULL, &rnode,
332c2efd8c9Spgoyette CTLFLAG_PERMANENT,
333c2efd8c9Spgoyette CTLTYPE_STRUCT, h->name,
334c2efd8c9Spgoyette SYSCTL_DESCR("history data"),
335c2efd8c9Spgoyette sysctl_kernhist_helper, 0, NULL, 0,
336c2efd8c9Spgoyette CTL_KERN, sysctl_hist_node, CTL_CREATE, CTL_EOL);
337c2efd8c9Spgoyette if (error == 0)
338c2efd8c9Spgoyette h->s = rnode->sysctl_num;
3395a30768dSpgoyette if (hist == h)
3405a30768dSpgoyette break;
341c2efd8c9Spgoyette }
342c2efd8c9Spgoyette }
343c2efd8c9Spgoyette
344c2efd8c9Spgoyette /*
345c2efd8c9Spgoyette * sysctl_kerhnist_init()
346c2efd8c9Spgoyette *
347c2efd8c9Spgoyette * Create the 2nd level "hw.hist" sysctl node
348c2efd8c9Spgoyette */
349c2efd8c9Spgoyette void
sysctl_kernhist_init(void)350c2efd8c9Spgoyette sysctl_kernhist_init(void)
351c2efd8c9Spgoyette {
352c2efd8c9Spgoyette const struct sysctlnode *rnode = NULL;
353c2efd8c9Spgoyette
354c2efd8c9Spgoyette sysctl_createv(NULL, 0, NULL, &rnode,
355c2efd8c9Spgoyette CTLFLAG_PERMANENT,
356c2efd8c9Spgoyette CTLTYPE_NODE, "hist",
357c2efd8c9Spgoyette SYSCTL_DESCR("kernel history tables"),
358c2efd8c9Spgoyette sysctl_kernhist_helper, 0, NULL, 0,
359c2efd8c9Spgoyette CTL_KERN, CTL_CREATE, CTL_EOL);
360c2efd8c9Spgoyette sysctl_hist_node = rnode->sysctl_num;
361c2efd8c9Spgoyette
3625a30768dSpgoyette kernhist_sysctl_ready = 1;
3634869ce0aSpgoyette membar_producer();
3645a30768dSpgoyette
3655a30768dSpgoyette sysctl_kernhist_new(NULL);
366c2efd8c9Spgoyette }
367c2efd8c9Spgoyette
368c2efd8c9Spgoyette /*
369c2efd8c9Spgoyette * find_string()
370c2efd8c9Spgoyette *
371c2efd8c9Spgoyette * Search the address-to-offset translation table for matching an
372c2efd8c9Spgoyette * address and len, and return the index of the entry we found. If
373c2efd8c9Spgoyette * not found, returns index 0 which points to the "?" entry. (We
374c2efd8c9Spgoyette * start matching at index 1, ignoring any matches of the "?" entry
375c2efd8c9Spgoyette * itself.)
376c2efd8c9Spgoyette */
377c2efd8c9Spgoyette static int
find_string(struct addr_xlt table[],size_t * count,const char * string,size_t len)378c2efd8c9Spgoyette find_string(struct addr_xlt table[], size_t *count, const char *string,
379c2efd8c9Spgoyette size_t len)
380c2efd8c9Spgoyette {
381c2efd8c9Spgoyette int i;
382c2efd8c9Spgoyette
383c2efd8c9Spgoyette for (i = 1; i < *count; i++)
384c2efd8c9Spgoyette if (string == table[i].addr && len == table[i].len)
385c2efd8c9Spgoyette return i;
386c2efd8c9Spgoyette
387c2efd8c9Spgoyette return 0;
388c2efd8c9Spgoyette }
389c2efd8c9Spgoyette
390c2efd8c9Spgoyette /*
391c2efd8c9Spgoyette * add_string()
392c2efd8c9Spgoyette *
393c2efd8c9Spgoyette * If the string and len are unique, add a new address-to-offset
394c2efd8c9Spgoyette * entry in the translation table and set the offset of the next
395c2efd8c9Spgoyette * entry.
396c2efd8c9Spgoyette */
397c2efd8c9Spgoyette static void
add_string(struct addr_xlt table[],size_t * count,const char * string,size_t len)398c2efd8c9Spgoyette add_string(struct addr_xlt table[], size_t *count, const char *string,
399c2efd8c9Spgoyette size_t len)
400c2efd8c9Spgoyette {
401c2efd8c9Spgoyette
402c2efd8c9Spgoyette if (find_string(table, count, string, len) == 0) {
403c2efd8c9Spgoyette table[*count].addr = string;
404c2efd8c9Spgoyette table[*count].len = len;
405c2efd8c9Spgoyette table[*count + 1].offset = table[*count].offset + len + 1;
406c2efd8c9Spgoyette (*count)++;
407c2efd8c9Spgoyette }
408c2efd8c9Spgoyette }
409c2efd8c9Spgoyette
410c2efd8c9Spgoyette /*
411c2efd8c9Spgoyette * sysctl_kernhist_helper
412c2efd8c9Spgoyette *
413c2efd8c9Spgoyette * This helper routine is called for all accesses to the kern.hist
414c2efd8c9Spgoyette * hierarchy.
415c2efd8c9Spgoyette */
416c2efd8c9Spgoyette static int
sysctl_kernhist_helper(SYSCTLFN_ARGS)417c2efd8c9Spgoyette sysctl_kernhist_helper(SYSCTLFN_ARGS)
418c2efd8c9Spgoyette {
419c2efd8c9Spgoyette struct kern_history *h;
420c2efd8c9Spgoyette struct kern_history_ent *in_evt;
421c2efd8c9Spgoyette struct sysctl_history_event *out_evt;
422c2efd8c9Spgoyette struct sysctl_history *buf;
423c2efd8c9Spgoyette struct addr_xlt *xlate_t, *xlt;
424c2efd8c9Spgoyette size_t bufsize, xlate_s;
425c2efd8c9Spgoyette size_t xlate_c;
426f8968117Schristos const char *strp __diagused;
427c2efd8c9Spgoyette char *next;
428c2efd8c9Spgoyette int i, j;
429c2efd8c9Spgoyette int error;
430c2efd8c9Spgoyette
431c2efd8c9Spgoyette if (namelen == 1 && name[0] == CTL_QUERY)
432c2efd8c9Spgoyette return sysctl_query(SYSCTLFN_CALL(rnode));
433c2efd8c9Spgoyette
434c2efd8c9Spgoyette /*
435c2efd8c9Spgoyette * Disallow userland updates, verify that we arrived at a
436c2efd8c9Spgoyette * valid history rnode
437c2efd8c9Spgoyette */
438c2efd8c9Spgoyette if (newp)
439c2efd8c9Spgoyette return EPERM;
440c2efd8c9Spgoyette if (namelen != 1 || name[0] != CTL_EOL)
441c2efd8c9Spgoyette return EINVAL;
442c2efd8c9Spgoyette
443c2efd8c9Spgoyette /* Find the correct kernhist for this sysctl node */
444c2efd8c9Spgoyette LIST_FOREACH(h, &kern_histories, list) {
445c2efd8c9Spgoyette if (h->s == rnode->sysctl_num)
446c2efd8c9Spgoyette break;
447c2efd8c9Spgoyette }
448c2efd8c9Spgoyette if (h == NULL)
449c2efd8c9Spgoyette return ENOENT;
450c2efd8c9Spgoyette
451c2efd8c9Spgoyette /*
452c2efd8c9Spgoyette * Worst case is two string pointers per history entry, plus
453c2efd8c9Spgoyette * two for the history name and "?" string; allocate an extra
454c2efd8c9Spgoyette * entry since we pre-set the "next" entry's offset member.
455c2efd8c9Spgoyette */
456c2efd8c9Spgoyette xlate_s = sizeof(struct addr_xlt) * h->n * 2 + 3;
457c2efd8c9Spgoyette xlate_t = kmem_alloc(xlate_s, KM_SLEEP);
458c2efd8c9Spgoyette xlate_c = 0;
459c2efd8c9Spgoyette
460c2efd8c9Spgoyette /* offset 0 reserved for NULL pointer, ie unused history entry */
461c2efd8c9Spgoyette xlate_t[0].offset = 1;
462c2efd8c9Spgoyette
463c2efd8c9Spgoyette /*
464c2efd8c9Spgoyette * If the history gets updated and an unexpected string is
465c2efd8c9Spgoyette * found later, we'll point it here. Otherwise, we'd have to
466c2efd8c9Spgoyette * repeat this process iteratively, and it could take multiple
467c2efd8c9Spgoyette * iterations before terminating.
468c2efd8c9Spgoyette */
469c2efd8c9Spgoyette add_string(xlate_t, &xlate_c, "?", 0);
470c2efd8c9Spgoyette
471c2efd8c9Spgoyette /* Copy the history name itself to the export structure */
472c2efd8c9Spgoyette add_string(xlate_t, &xlate_c, h->name, h->namelen);
473c2efd8c9Spgoyette
474c2efd8c9Spgoyette /*
475c2efd8c9Spgoyette * Loop through all used history entries to find the unique
476c2efd8c9Spgoyette * fn and fmt strings
477c2efd8c9Spgoyette */
478c2efd8c9Spgoyette for (i = 0, in_evt = h->e; i < h->n; i++, in_evt++) {
479c2efd8c9Spgoyette if (in_evt->fn == NULL)
480c2efd8c9Spgoyette continue;
481c2efd8c9Spgoyette add_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen);
482c2efd8c9Spgoyette add_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen);
483c2efd8c9Spgoyette }
484c2efd8c9Spgoyette
485c2efd8c9Spgoyette /* Total buffer size includes header, events, and string table */
486c2efd8c9Spgoyette bufsize = sizeof(struct sysctl_history) +
487c2efd8c9Spgoyette h->n * sizeof(struct sysctl_history_event) +
488c2efd8c9Spgoyette xlate_t[xlate_c].offset;
489c2efd8c9Spgoyette buf = kmem_alloc(bufsize, KM_SLEEP);
490c2efd8c9Spgoyette
491c2efd8c9Spgoyette /*
492c2efd8c9Spgoyette * Copy history header info to the export structure
493c2efd8c9Spgoyette */
494c2efd8c9Spgoyette j = find_string(xlate_t, &xlate_c, h->name, h->namelen);
495cb32a134Spgoyette buf->sh_nameoffset = xlate_t[j].offset;
496cb32a134Spgoyette buf->sh_numentries = h->n;
497cb32a134Spgoyette buf->sh_nextfree = h->f;
498c2efd8c9Spgoyette
499c2efd8c9Spgoyette /*
500c2efd8c9Spgoyette * Loop through the history events again, copying the data to
501c2efd8c9Spgoyette * the export structure
502c2efd8c9Spgoyette */
503c2efd8c9Spgoyette for (i = 0, in_evt = h->e, out_evt = buf->sh_events; i < h->n;
504c2efd8c9Spgoyette i++, in_evt++, out_evt++) {
505c2efd8c9Spgoyette if (in_evt->fn == NULL) { /* skip unused entries */
506c2efd8c9Spgoyette out_evt->she_funcoffset = 0;
507c2efd8c9Spgoyette out_evt->she_fmtoffset = 0;
508c2efd8c9Spgoyette continue;
509c2efd8c9Spgoyette }
510c9b6361bSpgoyette out_evt->she_bintime = in_evt->bt;
511c2efd8c9Spgoyette out_evt->she_callnumber = in_evt->call;
512c2efd8c9Spgoyette out_evt->she_cpunum = in_evt->cpunum;
513c2efd8c9Spgoyette out_evt->she_values[0] = in_evt->v[0];
514c2efd8c9Spgoyette out_evt->she_values[1] = in_evt->v[1];
515c2efd8c9Spgoyette out_evt->she_values[2] = in_evt->v[2];
516c2efd8c9Spgoyette out_evt->she_values[3] = in_evt->v[3];
517c2efd8c9Spgoyette j = find_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen);
518c2efd8c9Spgoyette out_evt->she_funcoffset = xlate_t[j].offset;
519c2efd8c9Spgoyette j = find_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen);
520c2efd8c9Spgoyette out_evt->she_fmtoffset = xlate_t[j].offset;
521c2efd8c9Spgoyette }
522c2efd8c9Spgoyette
523c2efd8c9Spgoyette /*
524c2efd8c9Spgoyette * Finally, fill the text string area with all the unique
525c2efd8c9Spgoyette * strings we found earlier.
526c2efd8c9Spgoyette *
527c2efd8c9Spgoyette * Skip the initial byte, since we use an offset of 0 to mean
528c2efd8c9Spgoyette * a NULL pointer (which means an unused history event).
529c2efd8c9Spgoyette */
530c2efd8c9Spgoyette strp = next = (char *)(&buf->sh_events[h->n]);
531c2efd8c9Spgoyette *next++ = '\0';
532c2efd8c9Spgoyette
533c2efd8c9Spgoyette /*
534c2efd8c9Spgoyette * Then copy each string into the export structure, making
535c2efd8c9Spgoyette * sure to terminate each string with a '\0' character
536c2efd8c9Spgoyette */
537c2efd8c9Spgoyette for (i = 0, xlt = xlate_t; i < xlate_c; i++, xlt++) {
538c2efd8c9Spgoyette KASSERTMSG((next - strp) == xlt->offset,
539c2efd8c9Spgoyette "entry %d at wrong offset %"PRIu32, i, xlt->offset);
540c2efd8c9Spgoyette memcpy(next, xlt->addr, xlt->len);
541c2efd8c9Spgoyette next += xlt->len;
542c2efd8c9Spgoyette *next++ = '\0';
543c2efd8c9Spgoyette }
544c2efd8c9Spgoyette
545c2efd8c9Spgoyette /* Copy data to userland */
546d1579b2dSriastradh error = copyout(buf, oldp, uimin(bufsize, *oldlenp));
547c2efd8c9Spgoyette
548c2efd8c9Spgoyette /* If copyout was successful but only partial, report ENOMEM */
549c2efd8c9Spgoyette if (error == 0 && *oldlenp < bufsize)
550c2efd8c9Spgoyette error = ENOMEM;
551c2efd8c9Spgoyette
552c2efd8c9Spgoyette *oldlenp = bufsize; /* inform userland of space requirements */
553c2efd8c9Spgoyette
554c2efd8c9Spgoyette /* Free up the stuff we allocated */
555c2efd8c9Spgoyette kmem_free(buf, bufsize);
556c2efd8c9Spgoyette kmem_free(xlate_t, xlate_s);
557c2efd8c9Spgoyette
558c2efd8c9Spgoyette return error;
559c2efd8c9Spgoyette }
560