1794a9a6cSJohn Baldwin /*- 2ccd8bad0SRobert Watson * Copyright (c) 2010 Juniper Networks, Inc. 3ccd8bad0SRobert Watson * Copyright (c) 2009 Robert N. M. Watson 4ccd8bad0SRobert Watson * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org> 5794a9a6cSJohn Baldwin * Copyright (c) 2008 Yahoo!, Inc. 6794a9a6cSJohn Baldwin * All rights reserved. 7ccd8bad0SRobert Watson * 8794a9a6cSJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org> 9794a9a6cSJohn Baldwin * 10ccd8bad0SRobert Watson * This software was developed by Robert N. M. Watson under contract 11ccd8bad0SRobert Watson * to Juniper Networks, Inc. 12ccd8bad0SRobert Watson * 13794a9a6cSJohn Baldwin * Redistribution and use in source and binary forms, with or without 14794a9a6cSJohn Baldwin * modification, are permitted provided that the following conditions 15794a9a6cSJohn Baldwin * are met: 16794a9a6cSJohn Baldwin * 1. Redistributions of source code must retain the above copyright 17794a9a6cSJohn Baldwin * notice, this list of conditions and the following disclaimer. 18794a9a6cSJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 19794a9a6cSJohn Baldwin * notice, this list of conditions and the following disclaimer in the 20794a9a6cSJohn Baldwin * documentation and/or other materials provided with the distribution. 21794a9a6cSJohn Baldwin * 3. Neither the name of the author nor the names of any co-contributors 22794a9a6cSJohn Baldwin * may be used to endorse or promote products derived from this software 23794a9a6cSJohn Baldwin * without specific prior written permission. 24794a9a6cSJohn Baldwin * 25794a9a6cSJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26794a9a6cSJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27794a9a6cSJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28794a9a6cSJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29794a9a6cSJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30794a9a6cSJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31794a9a6cSJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32794a9a6cSJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33794a9a6cSJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34794a9a6cSJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35794a9a6cSJohn Baldwin * SUCH DAMAGE. 36794a9a6cSJohn Baldwin */ 37794a9a6cSJohn Baldwin 38794a9a6cSJohn Baldwin #include <sys/cdefs.h> 39794a9a6cSJohn Baldwin __FBSDID("$FreeBSD$"); 40794a9a6cSJohn Baldwin 41794a9a6cSJohn Baldwin #include <sys/param.h> 42794a9a6cSJohn Baldwin #include <sys/pcpu.h> 43794a9a6cSJohn Baldwin #include <sys/sysctl.h> 44794a9a6cSJohn Baldwin #include <kvm.h> 45794a9a6cSJohn Baldwin #include <limits.h> 46794a9a6cSJohn Baldwin #include <stdlib.h> 47794a9a6cSJohn Baldwin 48794a9a6cSJohn Baldwin #include "kvm_private.h" 49794a9a6cSJohn Baldwin 50794a9a6cSJohn Baldwin static struct nlist kvm_pcpu_nl[] = { 51*c10970ddSUlrich Spörlein { .n_name = "_cpuid_to_pcpu" }, 52*c10970ddSUlrich Spörlein { .n_name = "_mp_maxcpus" }, 53*c10970ddSUlrich Spörlein { .n_name = NULL }, 54794a9a6cSJohn Baldwin }; 55794a9a6cSJohn Baldwin 56794a9a6cSJohn Baldwin /* 57794a9a6cSJohn Baldwin * Kernel per-CPU data state. We cache this stuff on the first 58794a9a6cSJohn Baldwin * access. 59ccd8bad0SRobert Watson * 60ccd8bad0SRobert Watson * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the 61ccd8bad0SRobert Watson * consumer has multiple handles in flight to differently configured 62ccd8bad0SRobert Watson * kernels/crashdumps. 63794a9a6cSJohn Baldwin */ 64794a9a6cSJohn Baldwin static void **pcpu_data; 65794a9a6cSJohn Baldwin static int maxcpu; 66794a9a6cSJohn Baldwin 67794a9a6cSJohn Baldwin #define NL_CPUID_TO_PCPU 0 68794a9a6cSJohn Baldwin #define NL_MP_MAXCPUS 1 69794a9a6cSJohn Baldwin 70794a9a6cSJohn Baldwin static int 71794a9a6cSJohn Baldwin _kvm_pcpu_init(kvm_t *kd) 72794a9a6cSJohn Baldwin { 73794a9a6cSJohn Baldwin size_t len; 74794a9a6cSJohn Baldwin int max; 75794a9a6cSJohn Baldwin void *data; 76794a9a6cSJohn Baldwin 77794a9a6cSJohn Baldwin if (kvm_nlist(kd, kvm_pcpu_nl) < 0) 78794a9a6cSJohn Baldwin return (-1); 79794a9a6cSJohn Baldwin if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) { 80794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu"); 81794a9a6cSJohn Baldwin return (-1); 82794a9a6cSJohn Baldwin } 83794a9a6cSJohn Baldwin if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) { 84794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "unable to find mp_maxcpus"); 85794a9a6cSJohn Baldwin return (-1); 86794a9a6cSJohn Baldwin } 87794a9a6cSJohn Baldwin if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max, 88794a9a6cSJohn Baldwin sizeof(max)) != sizeof(max)) { 89794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "cannot read mp_maxcpus"); 90794a9a6cSJohn Baldwin return (-1); 91794a9a6cSJohn Baldwin } 92794a9a6cSJohn Baldwin len = max * sizeof(void *); 93794a9a6cSJohn Baldwin data = malloc(len); 94794a9a6cSJohn Baldwin if (data == NULL) { 95794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "out of memory"); 96794a9a6cSJohn Baldwin return (-1); 97794a9a6cSJohn Baldwin } 98794a9a6cSJohn Baldwin if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) != 99*c10970ddSUlrich Spörlein (ssize_t)len) { 100794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array"); 101794a9a6cSJohn Baldwin free(data); 102794a9a6cSJohn Baldwin return (-1); 103794a9a6cSJohn Baldwin } 104794a9a6cSJohn Baldwin pcpu_data = data; 105794a9a6cSJohn Baldwin maxcpu = max; 106794a9a6cSJohn Baldwin return (0); 107794a9a6cSJohn Baldwin } 108794a9a6cSJohn Baldwin 109794a9a6cSJohn Baldwin static void 110794a9a6cSJohn Baldwin _kvm_pcpu_clear(void) 111794a9a6cSJohn Baldwin { 112794a9a6cSJohn Baldwin 113794a9a6cSJohn Baldwin maxcpu = 0; 114794a9a6cSJohn Baldwin free(pcpu_data); 115794a9a6cSJohn Baldwin pcpu_data = NULL; 116794a9a6cSJohn Baldwin } 117794a9a6cSJohn Baldwin 118794a9a6cSJohn Baldwin void * 119794a9a6cSJohn Baldwin kvm_getpcpu(kvm_t *kd, int cpu) 120794a9a6cSJohn Baldwin { 121794a9a6cSJohn Baldwin char *buf; 122794a9a6cSJohn Baldwin 123794a9a6cSJohn Baldwin if (kd == NULL) { 124794a9a6cSJohn Baldwin _kvm_pcpu_clear(); 125794a9a6cSJohn Baldwin return (NULL); 126794a9a6cSJohn Baldwin } 127794a9a6cSJohn Baldwin 128794a9a6cSJohn Baldwin if (maxcpu == 0) 129794a9a6cSJohn Baldwin if (_kvm_pcpu_init(kd) < 0) 130794a9a6cSJohn Baldwin return ((void *)-1); 131794a9a6cSJohn Baldwin 132794a9a6cSJohn Baldwin if (cpu >= maxcpu || pcpu_data[cpu] == NULL) 133794a9a6cSJohn Baldwin return (NULL); 134794a9a6cSJohn Baldwin 135794a9a6cSJohn Baldwin buf = malloc(sizeof(struct pcpu)); 136794a9a6cSJohn Baldwin if (buf == NULL) { 137794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "out of memory"); 138794a9a6cSJohn Baldwin return ((void *)-1); 139794a9a6cSJohn Baldwin } 140794a9a6cSJohn Baldwin if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, sizeof(struct pcpu)) != 141794a9a6cSJohn Baldwin sizeof(struct pcpu)) { 142794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "unable to read per-CPU data"); 143794a9a6cSJohn Baldwin free(buf); 144794a9a6cSJohn Baldwin return ((void *)-1); 145794a9a6cSJohn Baldwin } 146794a9a6cSJohn Baldwin return (buf); 147794a9a6cSJohn Baldwin } 148794a9a6cSJohn Baldwin 149794a9a6cSJohn Baldwin int 150794a9a6cSJohn Baldwin kvm_getmaxcpu(kvm_t *kd) 151794a9a6cSJohn Baldwin { 152794a9a6cSJohn Baldwin 153794a9a6cSJohn Baldwin if (kd == NULL) { 154794a9a6cSJohn Baldwin _kvm_pcpu_clear(); 155794a9a6cSJohn Baldwin return (0); 156794a9a6cSJohn Baldwin } 157794a9a6cSJohn Baldwin 158794a9a6cSJohn Baldwin if (maxcpu == 0) 159794a9a6cSJohn Baldwin if (_kvm_pcpu_init(kd) < 0) 160794a9a6cSJohn Baldwin return (-1); 161794a9a6cSJohn Baldwin return (maxcpu); 162794a9a6cSJohn Baldwin } 163ccd8bad0SRobert Watson 164ccd8bad0SRobert Watson static int 165ccd8bad0SRobert Watson _kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) 166ccd8bad0SRobert Watson { 167ccd8bad0SRobert Watson 168ccd8bad0SRobert Watson if (!kd->dpcpu_initialized) { 169ccd8bad0SRobert Watson if (report_error) 170ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: not initialized", 171ccd8bad0SRobert Watson __func__); 172ccd8bad0SRobert Watson return (-1); 173ccd8bad0SRobert Watson } 174ccd8bad0SRobert Watson if (cpu >= kd->dpcpu_maxcpus) { 175ccd8bad0SRobert Watson if (report_error) 176ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: CPU %u too big", 177ccd8bad0SRobert Watson __func__, cpu); 178ccd8bad0SRobert Watson return (-1); 179ccd8bad0SRobert Watson } 180ccd8bad0SRobert Watson if (kd->dpcpu_off[cpu] == 0) { 181ccd8bad0SRobert Watson if (report_error) 182ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: CPU %u not found", 183ccd8bad0SRobert Watson __func__, cpu); 184ccd8bad0SRobert Watson return (-1); 185ccd8bad0SRobert Watson } 186ccd8bad0SRobert Watson kd->dpcpu_curcpu = cpu; 187ccd8bad0SRobert Watson kd->dpcpu_curoff = kd->dpcpu_off[cpu]; 188ccd8bad0SRobert Watson return (0); 189ccd8bad0SRobert Watson } 190ccd8bad0SRobert Watson 191ccd8bad0SRobert Watson /* 192ccd8bad0SRobert Watson * Set up libkvm to handle dynamic per-CPU memory. 193ccd8bad0SRobert Watson */ 194ccd8bad0SRobert Watson static int 195ccd8bad0SRobert Watson _kvm_dpcpu_init(kvm_t *kd) 196ccd8bad0SRobert Watson { 197ccd8bad0SRobert Watson struct nlist nl[] = { 198ccd8bad0SRobert Watson #define NLIST_START_SET_PCPU 0 199*c10970ddSUlrich Spörlein { .n_name = "___start_" DPCPU_SETNAME }, 200ccd8bad0SRobert Watson #define NLIST_STOP_SET_PCPU 1 201*c10970ddSUlrich Spörlein { .n_name = "___stop_" DPCPU_SETNAME }, 202ccd8bad0SRobert Watson #define NLIST_DPCPU_OFF 2 203*c10970ddSUlrich Spörlein { .n_name = "_dpcpu_off" }, 204ccd8bad0SRobert Watson #define NLIST_MP_MAXCPUS 3 205*c10970ddSUlrich Spörlein { .n_name = "_mp_maxcpus" }, 206*c10970ddSUlrich Spörlein { .n_name = NULL }, 207ccd8bad0SRobert Watson }; 208ccd8bad0SRobert Watson uintptr_t *dpcpu_off_buf; 209ccd8bad0SRobert Watson size_t len; 210ccd8bad0SRobert Watson u_int dpcpu_maxcpus; 211ccd8bad0SRobert Watson 212ccd8bad0SRobert Watson /* 213ccd8bad0SRobert Watson * Locate and cache locations of important symbols using the internal 214ccd8bad0SRobert Watson * version of _kvm_nlist, turning off initialization to avoid 215ccd8bad0SRobert Watson * recursion in case of unresolveable symbols. 216ccd8bad0SRobert Watson */ 217ccd8bad0SRobert Watson if (_kvm_nlist(kd, nl, 0) != 0) 218ccd8bad0SRobert Watson return (-1); 219ccd8bad0SRobert Watson if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, 220ccd8bad0SRobert Watson sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) 221ccd8bad0SRobert Watson return (-1); 222ccd8bad0SRobert Watson len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); 223ccd8bad0SRobert Watson dpcpu_off_buf = malloc(len); 224ccd8bad0SRobert Watson if (dpcpu_off_buf == NULL) 225ccd8bad0SRobert Watson return (-1); 226ccd8bad0SRobert Watson if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != 227*c10970ddSUlrich Spörlein (ssize_t)len) { 228ccd8bad0SRobert Watson free(dpcpu_off_buf); 229ccd8bad0SRobert Watson return (-1); 230ccd8bad0SRobert Watson } 231ccd8bad0SRobert Watson kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; 232ccd8bad0SRobert Watson kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; 233ccd8bad0SRobert Watson kd->dpcpu_maxcpus = dpcpu_maxcpus; 234ccd8bad0SRobert Watson kd->dpcpu_off = dpcpu_off_buf; 235ccd8bad0SRobert Watson kd->dpcpu_initialized = 1; 236ccd8bad0SRobert Watson (void)_kvm_dpcpu_setcpu(kd, 0, 0); 237ccd8bad0SRobert Watson return (0); 238ccd8bad0SRobert Watson } 239ccd8bad0SRobert Watson 240ccd8bad0SRobert Watson /* 241ccd8bad0SRobert Watson * Check whether the dpcpu module has been initialized sucessfully or not, 242ccd8bad0SRobert Watson * initialize it if permitted. 243ccd8bad0SRobert Watson */ 244ccd8bad0SRobert Watson int 245ccd8bad0SRobert Watson _kvm_dpcpu_initialized(kvm_t *kd, int intialize) 246ccd8bad0SRobert Watson { 247ccd8bad0SRobert Watson 248ccd8bad0SRobert Watson if (kd->dpcpu_initialized || !intialize) 249ccd8bad0SRobert Watson return (kd->dpcpu_initialized); 250ccd8bad0SRobert Watson 251ccd8bad0SRobert Watson (void)_kvm_dpcpu_init(kd); 252ccd8bad0SRobert Watson 253ccd8bad0SRobert Watson return (kd->dpcpu_initialized); 254ccd8bad0SRobert Watson } 255ccd8bad0SRobert Watson 256ccd8bad0SRobert Watson /* 257ccd8bad0SRobert Watson * Check whether the value is within the dpcpu symbol range and only if so 258ccd8bad0SRobert Watson * adjust the offset relative to the current offset. 259ccd8bad0SRobert Watson */ 260ccd8bad0SRobert Watson uintptr_t 261ccd8bad0SRobert Watson _kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value) 262ccd8bad0SRobert Watson { 263ccd8bad0SRobert Watson 264ccd8bad0SRobert Watson if (value == 0) 265ccd8bad0SRobert Watson return (value); 266ccd8bad0SRobert Watson 267ccd8bad0SRobert Watson if (!kd->dpcpu_initialized) 268ccd8bad0SRobert Watson return (value); 269ccd8bad0SRobert Watson 270ccd8bad0SRobert Watson if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) 271ccd8bad0SRobert Watson return (value); 272ccd8bad0SRobert Watson 273ccd8bad0SRobert Watson return (kd->dpcpu_curoff + value); 274ccd8bad0SRobert Watson } 275ccd8bad0SRobert Watson 276ccd8bad0SRobert Watson int 277ccd8bad0SRobert Watson kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) 278ccd8bad0SRobert Watson { 279ccd8bad0SRobert Watson int ret; 280ccd8bad0SRobert Watson 281ccd8bad0SRobert Watson if (!kd->dpcpu_initialized) { 282ccd8bad0SRobert Watson ret = _kvm_dpcpu_init(kd); 283ccd8bad0SRobert Watson if (ret != 0) { 284ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: init failed", 285ccd8bad0SRobert Watson __func__); 286ccd8bad0SRobert Watson return (ret); 287ccd8bad0SRobert Watson } 288ccd8bad0SRobert Watson } 289ccd8bad0SRobert Watson 290ccd8bad0SRobert Watson return (_kvm_dpcpu_setcpu(kd, cpu, 1)); 291ccd8bad0SRobert Watson } 292