1794a9a6cSJohn Baldwin /*- 22a213404SGleb Smirnoff * Copyright (c) 2013 Gleb Smirnoff <glebius@FreeBSD.org> 3ccd8bad0SRobert Watson * Copyright (c) 2010 Juniper Networks, Inc. 4ccd8bad0SRobert Watson * Copyright (c) 2009 Robert N. M. Watson 5ccd8bad0SRobert Watson * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org> 6794a9a6cSJohn Baldwin * Copyright (c) 2008 Yahoo!, Inc. 7794a9a6cSJohn Baldwin * All rights reserved. 8ccd8bad0SRobert Watson * 9794a9a6cSJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org> 10794a9a6cSJohn Baldwin * 11ccd8bad0SRobert Watson * This software was developed by Robert N. M. Watson under contract 12ccd8bad0SRobert Watson * to Juniper Networks, Inc. 13ccd8bad0SRobert Watson * 14794a9a6cSJohn Baldwin * Redistribution and use in source and binary forms, with or without 15794a9a6cSJohn Baldwin * modification, are permitted provided that the following conditions 16794a9a6cSJohn Baldwin * are met: 17794a9a6cSJohn Baldwin * 1. Redistributions of source code must retain the above copyright 18794a9a6cSJohn Baldwin * notice, this list of conditions and the following disclaimer. 19794a9a6cSJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 20794a9a6cSJohn Baldwin * notice, this list of conditions and the following disclaimer in the 21794a9a6cSJohn Baldwin * documentation and/or other materials provided with the distribution. 22794a9a6cSJohn Baldwin * 3. Neither the name of the author nor the names of any co-contributors 23794a9a6cSJohn Baldwin * may be used to endorse or promote products derived from this software 24794a9a6cSJohn Baldwin * without specific prior written permission. 25794a9a6cSJohn Baldwin * 26794a9a6cSJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 27794a9a6cSJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28794a9a6cSJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29794a9a6cSJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 30794a9a6cSJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31794a9a6cSJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32794a9a6cSJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33794a9a6cSJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34794a9a6cSJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35794a9a6cSJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36794a9a6cSJohn Baldwin * SUCH DAMAGE. 37794a9a6cSJohn Baldwin */ 38794a9a6cSJohn Baldwin 39794a9a6cSJohn Baldwin #include <sys/cdefs.h> 40794a9a6cSJohn Baldwin __FBSDID("$FreeBSD$"); 41794a9a6cSJohn Baldwin 42794a9a6cSJohn Baldwin #include <sys/param.h> 43794a9a6cSJohn Baldwin #include <sys/pcpu.h> 44794a9a6cSJohn Baldwin #include <sys/sysctl.h> 45794a9a6cSJohn Baldwin #include <kvm.h> 46794a9a6cSJohn Baldwin #include <limits.h> 47794a9a6cSJohn Baldwin #include <stdlib.h> 48794a9a6cSJohn Baldwin 49794a9a6cSJohn Baldwin #include "kvm_private.h" 50794a9a6cSJohn Baldwin 51794a9a6cSJohn Baldwin static struct nlist kvm_pcpu_nl[] = { 52c10970ddSUlrich Spörlein { .n_name = "_cpuid_to_pcpu" }, 53c10970ddSUlrich Spörlein { .n_name = "_mp_maxcpus" }, 542a213404SGleb Smirnoff { .n_name = "_mp_ncpus" }, 55c10970ddSUlrich Spörlein { .n_name = NULL }, 56794a9a6cSJohn Baldwin }; 572a213404SGleb Smirnoff #define NL_CPUID_TO_PCPU 0 582a213404SGleb Smirnoff #define NL_MP_MAXCPUS 1 592a213404SGleb Smirnoff #define NL_MP_NCPUS 2 60794a9a6cSJohn Baldwin 61794a9a6cSJohn Baldwin /* 62794a9a6cSJohn Baldwin * Kernel per-CPU data state. We cache this stuff on the first 63794a9a6cSJohn Baldwin * access. 64ccd8bad0SRobert Watson * 65ccd8bad0SRobert Watson * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the 66ccd8bad0SRobert Watson * consumer has multiple handles in flight to differently configured 67ccd8bad0SRobert Watson * kernels/crashdumps. 68794a9a6cSJohn Baldwin */ 69794a9a6cSJohn Baldwin static void **pcpu_data; 70794a9a6cSJohn Baldwin static int maxcpu; 712a213404SGleb Smirnoff static int mp_ncpus; 72794a9a6cSJohn Baldwin 73794a9a6cSJohn Baldwin static int 74794a9a6cSJohn Baldwin _kvm_pcpu_init(kvm_t *kd) 75794a9a6cSJohn Baldwin { 76794a9a6cSJohn Baldwin size_t len; 77794a9a6cSJohn Baldwin int max; 78794a9a6cSJohn Baldwin void *data; 79794a9a6cSJohn Baldwin 80794a9a6cSJohn Baldwin if (kvm_nlist(kd, kvm_pcpu_nl) < 0) 81794a9a6cSJohn Baldwin return (-1); 82794a9a6cSJohn Baldwin if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) { 83794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu"); 84794a9a6cSJohn Baldwin return (-1); 85794a9a6cSJohn Baldwin } 86794a9a6cSJohn Baldwin if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) { 87794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "unable to find mp_maxcpus"); 88794a9a6cSJohn Baldwin return (-1); 89794a9a6cSJohn Baldwin } 90794a9a6cSJohn Baldwin if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max, 91794a9a6cSJohn Baldwin sizeof(max)) != sizeof(max)) { 92794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "cannot read mp_maxcpus"); 93794a9a6cSJohn Baldwin return (-1); 94794a9a6cSJohn Baldwin } 952a213404SGleb Smirnoff if (kvm_pcpu_nl[NL_MP_NCPUS].n_value == 0) { 962a213404SGleb Smirnoff _kvm_err(kd, kd->program, "unable to find mp_ncpus"); 972a213404SGleb Smirnoff return (-1); 982a213404SGleb Smirnoff } 992a213404SGleb Smirnoff if (kvm_read(kd, kvm_pcpu_nl[NL_MP_NCPUS].n_value, &mp_ncpus, 1002a213404SGleb Smirnoff sizeof(mp_ncpus)) != sizeof(mp_ncpus)) { 1012a213404SGleb Smirnoff _kvm_err(kd, kd->program, "cannot read mp_ncpus"); 1022a213404SGleb Smirnoff return (-1); 1032a213404SGleb Smirnoff } 104794a9a6cSJohn Baldwin len = max * sizeof(void *); 105794a9a6cSJohn Baldwin data = malloc(len); 106794a9a6cSJohn Baldwin if (data == NULL) { 107794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "out of memory"); 108794a9a6cSJohn Baldwin return (-1); 109794a9a6cSJohn Baldwin } 110794a9a6cSJohn Baldwin if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) != 111c10970ddSUlrich Spörlein (ssize_t)len) { 112794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array"); 113794a9a6cSJohn Baldwin free(data); 114794a9a6cSJohn Baldwin return (-1); 115794a9a6cSJohn Baldwin } 116794a9a6cSJohn Baldwin pcpu_data = data; 117794a9a6cSJohn Baldwin maxcpu = max; 118794a9a6cSJohn Baldwin return (0); 119794a9a6cSJohn Baldwin } 120794a9a6cSJohn Baldwin 121794a9a6cSJohn Baldwin static void 122794a9a6cSJohn Baldwin _kvm_pcpu_clear(void) 123794a9a6cSJohn Baldwin { 124794a9a6cSJohn Baldwin 125794a9a6cSJohn Baldwin maxcpu = 0; 126794a9a6cSJohn Baldwin free(pcpu_data); 127794a9a6cSJohn Baldwin pcpu_data = NULL; 128794a9a6cSJohn Baldwin } 129794a9a6cSJohn Baldwin 130794a9a6cSJohn Baldwin void * 131794a9a6cSJohn Baldwin kvm_getpcpu(kvm_t *kd, int cpu) 132794a9a6cSJohn Baldwin { 133794a9a6cSJohn Baldwin char *buf; 134794a9a6cSJohn Baldwin 135794a9a6cSJohn Baldwin if (kd == NULL) { 136794a9a6cSJohn Baldwin _kvm_pcpu_clear(); 137794a9a6cSJohn Baldwin return (NULL); 138794a9a6cSJohn Baldwin } 139794a9a6cSJohn Baldwin 140794a9a6cSJohn Baldwin if (maxcpu == 0) 141794a9a6cSJohn Baldwin if (_kvm_pcpu_init(kd) < 0) 142794a9a6cSJohn Baldwin return ((void *)-1); 143794a9a6cSJohn Baldwin 144794a9a6cSJohn Baldwin if (cpu >= maxcpu || pcpu_data[cpu] == NULL) 145794a9a6cSJohn Baldwin return (NULL); 146794a9a6cSJohn Baldwin 147794a9a6cSJohn Baldwin buf = malloc(sizeof(struct pcpu)); 148794a9a6cSJohn Baldwin if (buf == NULL) { 149794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "out of memory"); 150794a9a6cSJohn Baldwin return ((void *)-1); 151794a9a6cSJohn Baldwin } 152a2f4e284SAttilio Rao if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, 153a2f4e284SAttilio Rao sizeof(struct pcpu)) != sizeof(struct pcpu)) { 154794a9a6cSJohn Baldwin _kvm_err(kd, kd->program, "unable to read per-CPU data"); 155794a9a6cSJohn Baldwin free(buf); 156794a9a6cSJohn Baldwin return ((void *)-1); 157794a9a6cSJohn Baldwin } 158794a9a6cSJohn Baldwin return (buf); 159794a9a6cSJohn Baldwin } 160794a9a6cSJohn Baldwin 161794a9a6cSJohn Baldwin int 162794a9a6cSJohn Baldwin kvm_getmaxcpu(kvm_t *kd) 163794a9a6cSJohn Baldwin { 164794a9a6cSJohn Baldwin 165794a9a6cSJohn Baldwin if (kd == NULL) { 166794a9a6cSJohn Baldwin _kvm_pcpu_clear(); 167794a9a6cSJohn Baldwin return (0); 168794a9a6cSJohn Baldwin } 169794a9a6cSJohn Baldwin 170794a9a6cSJohn Baldwin if (maxcpu == 0) 171794a9a6cSJohn Baldwin if (_kvm_pcpu_init(kd) < 0) 172794a9a6cSJohn Baldwin return (-1); 173794a9a6cSJohn Baldwin return (maxcpu); 174794a9a6cSJohn Baldwin } 175ccd8bad0SRobert Watson 1769292aad4SGleb Smirnoff int 1779292aad4SGleb Smirnoff kvm_getncpus(kvm_t *kd) 1789292aad4SGleb Smirnoff { 1799292aad4SGleb Smirnoff 1809292aad4SGleb Smirnoff if (mp_ncpus == 0) 1819292aad4SGleb Smirnoff if (_kvm_pcpu_init(kd) < 0) 1829292aad4SGleb Smirnoff return (-1); 1839292aad4SGleb Smirnoff return (mp_ncpus); 1849292aad4SGleb Smirnoff } 1859292aad4SGleb Smirnoff 186ccd8bad0SRobert Watson static int 187ccd8bad0SRobert Watson _kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) 188ccd8bad0SRobert Watson { 189ccd8bad0SRobert Watson 190ccd8bad0SRobert Watson if (!kd->dpcpu_initialized) { 191ccd8bad0SRobert Watson if (report_error) 192ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: not initialized", 193ccd8bad0SRobert Watson __func__); 194ccd8bad0SRobert Watson return (-1); 195ccd8bad0SRobert Watson } 196ccd8bad0SRobert Watson if (cpu >= kd->dpcpu_maxcpus) { 197ccd8bad0SRobert Watson if (report_error) 198ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: CPU %u too big", 199ccd8bad0SRobert Watson __func__, cpu); 200ccd8bad0SRobert Watson return (-1); 201ccd8bad0SRobert Watson } 202ccd8bad0SRobert Watson if (kd->dpcpu_off[cpu] == 0) { 203ccd8bad0SRobert Watson if (report_error) 204ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: CPU %u not found", 205ccd8bad0SRobert Watson __func__, cpu); 206ccd8bad0SRobert Watson return (-1); 207ccd8bad0SRobert Watson } 208ccd8bad0SRobert Watson kd->dpcpu_curcpu = cpu; 209ccd8bad0SRobert Watson kd->dpcpu_curoff = kd->dpcpu_off[cpu]; 210ccd8bad0SRobert Watson return (0); 211ccd8bad0SRobert Watson } 212ccd8bad0SRobert Watson 213ccd8bad0SRobert Watson /* 214ccd8bad0SRobert Watson * Set up libkvm to handle dynamic per-CPU memory. 215ccd8bad0SRobert Watson */ 216ccd8bad0SRobert Watson static int 217ccd8bad0SRobert Watson _kvm_dpcpu_init(kvm_t *kd) 218ccd8bad0SRobert Watson { 2197f911abeSJohn Baldwin struct kvm_nlist nl[] = { 220ccd8bad0SRobert Watson #define NLIST_START_SET_PCPU 0 221c10970ddSUlrich Spörlein { .n_name = "___start_" DPCPU_SETNAME }, 222ccd8bad0SRobert Watson #define NLIST_STOP_SET_PCPU 1 223c10970ddSUlrich Spörlein { .n_name = "___stop_" DPCPU_SETNAME }, 224ccd8bad0SRobert Watson #define NLIST_DPCPU_OFF 2 225c10970ddSUlrich Spörlein { .n_name = "_dpcpu_off" }, 226ccd8bad0SRobert Watson #define NLIST_MP_MAXCPUS 3 227c10970ddSUlrich Spörlein { .n_name = "_mp_maxcpus" }, 228c10970ddSUlrich Spörlein { .n_name = NULL }, 229ccd8bad0SRobert Watson }; 230ccd8bad0SRobert Watson uintptr_t *dpcpu_off_buf; 231ccd8bad0SRobert Watson size_t len; 232ccd8bad0SRobert Watson u_int dpcpu_maxcpus; 233ccd8bad0SRobert Watson 234ccd8bad0SRobert Watson /* 2357f911abeSJohn Baldwin * XXX: This only works for native kernels for now. 2367f911abeSJohn Baldwin */ 2377f911abeSJohn Baldwin if (!kvm_native(kd)) 2387f911abeSJohn Baldwin return (-1); 2397f911abeSJohn Baldwin 2407f911abeSJohn Baldwin /* 241ccd8bad0SRobert Watson * Locate and cache locations of important symbols using the internal 242ccd8bad0SRobert Watson * version of _kvm_nlist, turning off initialization to avoid 243ccd8bad0SRobert Watson * recursion in case of unresolveable symbols. 244ccd8bad0SRobert Watson */ 245ccd8bad0SRobert Watson if (_kvm_nlist(kd, nl, 0) != 0) 246ccd8bad0SRobert Watson return (-1); 247ccd8bad0SRobert Watson if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, 248ccd8bad0SRobert Watson sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) 249ccd8bad0SRobert Watson return (-1); 250ccd8bad0SRobert Watson len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); 251ccd8bad0SRobert Watson dpcpu_off_buf = malloc(len); 252ccd8bad0SRobert Watson if (dpcpu_off_buf == NULL) 253ccd8bad0SRobert Watson return (-1); 254ccd8bad0SRobert Watson if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != 255c10970ddSUlrich Spörlein (ssize_t)len) { 256ccd8bad0SRobert Watson free(dpcpu_off_buf); 257ccd8bad0SRobert Watson return (-1); 258ccd8bad0SRobert Watson } 259ccd8bad0SRobert Watson kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; 260ccd8bad0SRobert Watson kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; 261ccd8bad0SRobert Watson kd->dpcpu_maxcpus = dpcpu_maxcpus; 262ccd8bad0SRobert Watson kd->dpcpu_off = dpcpu_off_buf; 263ccd8bad0SRobert Watson kd->dpcpu_initialized = 1; 264ccd8bad0SRobert Watson (void)_kvm_dpcpu_setcpu(kd, 0, 0); 265ccd8bad0SRobert Watson return (0); 266ccd8bad0SRobert Watson } 267ccd8bad0SRobert Watson 268ccd8bad0SRobert Watson /* 269*75f46cf6SPedro F. Giffuni * Check whether the dpcpu module has been initialized successfully or not, 270ccd8bad0SRobert Watson * initialize it if permitted. 271ccd8bad0SRobert Watson */ 272ccd8bad0SRobert Watson int 273ccd8bad0SRobert Watson _kvm_dpcpu_initialized(kvm_t *kd, int intialize) 274ccd8bad0SRobert Watson { 275ccd8bad0SRobert Watson 276ccd8bad0SRobert Watson if (kd->dpcpu_initialized || !intialize) 277ccd8bad0SRobert Watson return (kd->dpcpu_initialized); 278ccd8bad0SRobert Watson 279ccd8bad0SRobert Watson (void)_kvm_dpcpu_init(kd); 280ccd8bad0SRobert Watson 281ccd8bad0SRobert Watson return (kd->dpcpu_initialized); 282ccd8bad0SRobert Watson } 283ccd8bad0SRobert Watson 284ccd8bad0SRobert Watson /* 285ccd8bad0SRobert Watson * Check whether the value is within the dpcpu symbol range and only if so 286ccd8bad0SRobert Watson * adjust the offset relative to the current offset. 287ccd8bad0SRobert Watson */ 2887f911abeSJohn Baldwin kvaddr_t 2897f911abeSJohn Baldwin _kvm_dpcpu_validaddr(kvm_t *kd, kvaddr_t value) 290ccd8bad0SRobert Watson { 291ccd8bad0SRobert Watson 292ccd8bad0SRobert Watson if (value == 0) 293ccd8bad0SRobert Watson return (value); 294ccd8bad0SRobert Watson 295ccd8bad0SRobert Watson if (!kd->dpcpu_initialized) 296ccd8bad0SRobert Watson return (value); 297ccd8bad0SRobert Watson 298ccd8bad0SRobert Watson if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) 299ccd8bad0SRobert Watson return (value); 300ccd8bad0SRobert Watson 301ccd8bad0SRobert Watson return (kd->dpcpu_curoff + value); 302ccd8bad0SRobert Watson } 303ccd8bad0SRobert Watson 304ccd8bad0SRobert Watson int 305ccd8bad0SRobert Watson kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) 306ccd8bad0SRobert Watson { 307ccd8bad0SRobert Watson int ret; 308ccd8bad0SRobert Watson 309ccd8bad0SRobert Watson if (!kd->dpcpu_initialized) { 310ccd8bad0SRobert Watson ret = _kvm_dpcpu_init(kd); 311ccd8bad0SRobert Watson if (ret != 0) { 312ccd8bad0SRobert Watson _kvm_err(kd, kd->program, "%s: init failed", 313ccd8bad0SRobert Watson __func__); 314ccd8bad0SRobert Watson return (ret); 315ccd8bad0SRobert Watson } 316ccd8bad0SRobert Watson } 317ccd8bad0SRobert Watson 318ccd8bad0SRobert Watson return (_kvm_dpcpu_setcpu(kd, cpu, 1)); 319ccd8bad0SRobert Watson } 3202a213404SGleb Smirnoff 3212a213404SGleb Smirnoff /* 3222a213404SGleb Smirnoff * Obtain a per-CPU copy for given cpu from UMA_ZONE_PCPU allocation. 3232a213404SGleb Smirnoff */ 3242a213404SGleb Smirnoff ssize_t 32519b5cffeSGleb Smirnoff kvm_read_zpcpu(kvm_t *kd, u_long base, void *buf, size_t size, int cpu) 3262a213404SGleb Smirnoff { 3272a213404SGleb Smirnoff 3287f911abeSJohn Baldwin if (!kvm_native(kd)) 3297f911abeSJohn Baldwin return (-1); 3302a213404SGleb Smirnoff return (kvm_read(kd, (uintptr_t)(base + sizeof(struct pcpu) * cpu), 3312a213404SGleb Smirnoff buf, size)); 3322a213404SGleb Smirnoff } 3332a213404SGleb Smirnoff 3342a213404SGleb Smirnoff /* 3352a213404SGleb Smirnoff * Fetch value of a counter(9). 3362a213404SGleb Smirnoff */ 3372a213404SGleb Smirnoff uint64_t 3382a213404SGleb Smirnoff kvm_counter_u64_fetch(kvm_t *kd, u_long base) 3392a213404SGleb Smirnoff { 34033299737SGleb Smirnoff uint64_t r, c; 3412a213404SGleb Smirnoff 3422a213404SGleb Smirnoff if (mp_ncpus == 0) 3432a213404SGleb Smirnoff if (_kvm_pcpu_init(kd) < 0) 3442a213404SGleb Smirnoff return (0); 3452a213404SGleb Smirnoff 3462a213404SGleb Smirnoff r = 0; 3472a213404SGleb Smirnoff for (int i = 0; i < mp_ncpus; i++) { 34819b5cffeSGleb Smirnoff if (kvm_read_zpcpu(kd, base, &c, sizeof(c), i) != sizeof(c)) 3492a213404SGleb Smirnoff return (0); 3502a213404SGleb Smirnoff r += c; 3512a213404SGleb Smirnoff } 3522a213404SGleb Smirnoff 3532a213404SGleb Smirnoff return (r); 3542a213404SGleb Smirnoff } 355