1181876Sjhb/*- 2249344Sglebius * Copyright (c) 2013 Gleb Smirnoff <glebius@FreeBSD.org> 3204494Srwatson * Copyright (c) 2010 Juniper Networks, Inc. 4204494Srwatson * Copyright (c) 2009 Robert N. M. Watson 5204494Srwatson * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org> 6181876Sjhb * Copyright (c) 2008 Yahoo!, Inc. 7181876Sjhb * All rights reserved. 8204494Srwatson * 9181876Sjhb * Written by: John Baldwin <jhb@FreeBSD.org> 10181876Sjhb * 11204494Srwatson * This software was developed by Robert N. M. Watson under contract 12204494Srwatson * to Juniper Networks, Inc. 13204494Srwatson * 14181876Sjhb * Redistribution and use in source and binary forms, with or without 15181876Sjhb * modification, are permitted provided that the following conditions 16181876Sjhb * are met: 17181876Sjhb * 1. Redistributions of source code must retain the above copyright 18181876Sjhb * notice, this list of conditions and the following disclaimer. 19181876Sjhb * 2. Redistributions in binary form must reproduce the above copyright 20181876Sjhb * notice, this list of conditions and the following disclaimer in the 21181876Sjhb * documentation and/or other materials provided with the distribution. 22181876Sjhb * 3. Neither the name of the author nor the names of any co-contributors 23181876Sjhb * may be used to endorse or promote products derived from this software 24181876Sjhb * without specific prior written permission. 25181876Sjhb * 26181876Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 27181876Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28181876Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29181876Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 30181876Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31181876Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32181876Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33181876Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34181876Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35181876Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36181876Sjhb * SUCH DAMAGE. 37181876Sjhb */ 38181876Sjhb 39181876Sjhb#include <sys/cdefs.h> 40181876Sjhb__FBSDID("$FreeBSD: releng/10.2/lib/libkvm/kvm_pcpu.c 262740 2014-03-04 14:49:05Z glebius $"); 41181876Sjhb 42181876Sjhb#include <sys/param.h> 43181876Sjhb#include <sys/pcpu.h> 44181876Sjhb#include <sys/sysctl.h> 45181876Sjhb#include <kvm.h> 46181876Sjhb#include <limits.h> 47181876Sjhb#include <stdlib.h> 48181876Sjhb 49181876Sjhb#include "kvm_private.h" 50181876Sjhb 51181876Sjhbstatic struct nlist kvm_pcpu_nl[] = { 52217744Suqs { .n_name = "_cpuid_to_pcpu" }, 53217744Suqs { .n_name = "_mp_maxcpus" }, 54249344Sglebius { .n_name = "_mp_ncpus" }, 55217744Suqs { .n_name = NULL }, 56181876Sjhb}; 57249344Sglebius#define NL_CPUID_TO_PCPU 0 58249344Sglebius#define NL_MP_MAXCPUS 1 59249344Sglebius#define NL_MP_NCPUS 2 60181876Sjhb 61181876Sjhb/* 62181876Sjhb * Kernel per-CPU data state. We cache this stuff on the first 63181876Sjhb * access. 64204494Srwatson * 65204494Srwatson * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the 66204494Srwatson * consumer has multiple handles in flight to differently configured 67204494Srwatson * kernels/crashdumps. 68181876Sjhb */ 69181876Sjhbstatic void **pcpu_data; 70181876Sjhbstatic int maxcpu; 71249344Sglebiusstatic int mp_ncpus; 72181876Sjhb 73181876Sjhbstatic int 74181876Sjhb_kvm_pcpu_init(kvm_t *kd) 75181876Sjhb{ 76181876Sjhb size_t len; 77181876Sjhb int max; 78181876Sjhb void *data; 79181876Sjhb 80181876Sjhb if (kvm_nlist(kd, kvm_pcpu_nl) < 0) 81181876Sjhb return (-1); 82181876Sjhb if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) { 83181876Sjhb _kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu"); 84181876Sjhb return (-1); 85181876Sjhb } 86181876Sjhb if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) { 87181876Sjhb _kvm_err(kd, kd->program, "unable to find mp_maxcpus"); 88181876Sjhb return (-1); 89181876Sjhb } 90181876Sjhb if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max, 91181876Sjhb sizeof(max)) != sizeof(max)) { 92181876Sjhb _kvm_err(kd, kd->program, "cannot read mp_maxcpus"); 93181876Sjhb return (-1); 94181876Sjhb } 95249344Sglebius if (kvm_pcpu_nl[NL_MP_NCPUS].n_value == 0) { 96249344Sglebius _kvm_err(kd, kd->program, "unable to find mp_ncpus"); 97249344Sglebius return (-1); 98249344Sglebius } 99249344Sglebius if (kvm_read(kd, kvm_pcpu_nl[NL_MP_NCPUS].n_value, &mp_ncpus, 100249344Sglebius sizeof(mp_ncpus)) != sizeof(mp_ncpus)) { 101249344Sglebius _kvm_err(kd, kd->program, "cannot read mp_ncpus"); 102249344Sglebius return (-1); 103249344Sglebius } 104181876Sjhb len = max * sizeof(void *); 105181876Sjhb data = malloc(len); 106181876Sjhb if (data == NULL) { 107181876Sjhb _kvm_err(kd, kd->program, "out of memory"); 108181876Sjhb return (-1); 109181876Sjhb } 110181876Sjhb if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) != 111217744Suqs (ssize_t)len) { 112181876Sjhb _kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array"); 113181876Sjhb free(data); 114181876Sjhb return (-1); 115181876Sjhb } 116181876Sjhb pcpu_data = data; 117181876Sjhb maxcpu = max; 118181876Sjhb return (0); 119181876Sjhb} 120181876Sjhb 121181876Sjhbstatic void 122181876Sjhb_kvm_pcpu_clear(void) 123181876Sjhb{ 124181876Sjhb 125181876Sjhb maxcpu = 0; 126181876Sjhb free(pcpu_data); 127181876Sjhb pcpu_data = NULL; 128181876Sjhb} 129181876Sjhb 130181876Sjhbvoid * 131181876Sjhbkvm_getpcpu(kvm_t *kd, int cpu) 132181876Sjhb{ 133181876Sjhb char *buf; 134181876Sjhb 135181876Sjhb if (kd == NULL) { 136181876Sjhb _kvm_pcpu_clear(); 137181876Sjhb return (NULL); 138181876Sjhb } 139181876Sjhb 140181876Sjhb if (maxcpu == 0) 141181876Sjhb if (_kvm_pcpu_init(kd) < 0) 142181876Sjhb return ((void *)-1); 143181876Sjhb 144181876Sjhb if (cpu >= maxcpu || pcpu_data[cpu] == NULL) 145181876Sjhb return (NULL); 146181876Sjhb 147181876Sjhb buf = malloc(sizeof(struct pcpu)); 148181876Sjhb if (buf == NULL) { 149181876Sjhb _kvm_err(kd, kd->program, "out of memory"); 150181876Sjhb return ((void *)-1); 151181876Sjhb } 152223758Sattilio if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, 153223758Sattilio sizeof(struct pcpu)) != sizeof(struct pcpu)) { 154181876Sjhb _kvm_err(kd, kd->program, "unable to read per-CPU data"); 155181876Sjhb free(buf); 156181876Sjhb return ((void *)-1); 157181876Sjhb } 158181876Sjhb return (buf); 159181876Sjhb} 160181876Sjhb 161181876Sjhbint 162181876Sjhbkvm_getmaxcpu(kvm_t *kd) 163181876Sjhb{ 164181876Sjhb 165181876Sjhb if (kd == NULL) { 166181876Sjhb _kvm_pcpu_clear(); 167181876Sjhb return (0); 168181876Sjhb } 169181876Sjhb 170181876Sjhb if (maxcpu == 0) 171181876Sjhb if (_kvm_pcpu_init(kd) < 0) 172181876Sjhb return (-1); 173181876Sjhb return (maxcpu); 174181876Sjhb} 175204494Srwatson 176262740Sglebiusint 177262740Sglebiuskvm_getncpus(kvm_t *kd) 178262740Sglebius{ 179262740Sglebius 180262740Sglebius if (mp_ncpus == 0) 181262740Sglebius if (_kvm_pcpu_init(kd) < 0) 182262740Sglebius return (-1); 183262740Sglebius return (mp_ncpus); 184262740Sglebius} 185262740Sglebius 186204494Srwatsonstatic int 187204494Srwatson_kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) 188204494Srwatson{ 189204494Srwatson 190204494Srwatson if (!kd->dpcpu_initialized) { 191204494Srwatson if (report_error) 192204494Srwatson _kvm_err(kd, kd->program, "%s: not initialized", 193204494Srwatson __func__); 194204494Srwatson return (-1); 195204494Srwatson } 196204494Srwatson if (cpu >= kd->dpcpu_maxcpus) { 197204494Srwatson if (report_error) 198204494Srwatson _kvm_err(kd, kd->program, "%s: CPU %u too big", 199204494Srwatson __func__, cpu); 200204494Srwatson return (-1); 201204494Srwatson } 202204494Srwatson if (kd->dpcpu_off[cpu] == 0) { 203204494Srwatson if (report_error) 204204494Srwatson _kvm_err(kd, kd->program, "%s: CPU %u not found", 205204494Srwatson __func__, cpu); 206204494Srwatson return (-1); 207204494Srwatson } 208204494Srwatson kd->dpcpu_curcpu = cpu; 209204494Srwatson kd->dpcpu_curoff = kd->dpcpu_off[cpu]; 210204494Srwatson return (0); 211204494Srwatson} 212204494Srwatson 213204494Srwatson/* 214204494Srwatson * Set up libkvm to handle dynamic per-CPU memory. 215204494Srwatson */ 216204494Srwatsonstatic int 217204494Srwatson_kvm_dpcpu_init(kvm_t *kd) 218204494Srwatson{ 219204494Srwatson struct nlist nl[] = { 220204494Srwatson#define NLIST_START_SET_PCPU 0 221217744Suqs { .n_name = "___start_" DPCPU_SETNAME }, 222204494Srwatson#define NLIST_STOP_SET_PCPU 1 223217744Suqs { .n_name = "___stop_" DPCPU_SETNAME }, 224204494Srwatson#define NLIST_DPCPU_OFF 2 225217744Suqs { .n_name = "_dpcpu_off" }, 226204494Srwatson#define NLIST_MP_MAXCPUS 3 227217744Suqs { .n_name = "_mp_maxcpus" }, 228217744Suqs { .n_name = NULL }, 229204494Srwatson }; 230204494Srwatson uintptr_t *dpcpu_off_buf; 231204494Srwatson size_t len; 232204494Srwatson u_int dpcpu_maxcpus; 233204494Srwatson 234204494Srwatson /* 235204494Srwatson * Locate and cache locations of important symbols using the internal 236204494Srwatson * version of _kvm_nlist, turning off initialization to avoid 237204494Srwatson * recursion in case of unresolveable symbols. 238204494Srwatson */ 239204494Srwatson if (_kvm_nlist(kd, nl, 0) != 0) 240204494Srwatson return (-1); 241204494Srwatson if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, 242204494Srwatson sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) 243204494Srwatson return (-1); 244204494Srwatson len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); 245204494Srwatson dpcpu_off_buf = malloc(len); 246204494Srwatson if (dpcpu_off_buf == NULL) 247204494Srwatson return (-1); 248204494Srwatson if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != 249217744Suqs (ssize_t)len) { 250204494Srwatson free(dpcpu_off_buf); 251204494Srwatson return (-1); 252204494Srwatson } 253204494Srwatson kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; 254204494Srwatson kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; 255204494Srwatson kd->dpcpu_maxcpus = dpcpu_maxcpus; 256204494Srwatson kd->dpcpu_off = dpcpu_off_buf; 257204494Srwatson kd->dpcpu_initialized = 1; 258204494Srwatson (void)_kvm_dpcpu_setcpu(kd, 0, 0); 259204494Srwatson return (0); 260204494Srwatson} 261204494Srwatson 262204494Srwatson/* 263204494Srwatson * Check whether the dpcpu module has been initialized sucessfully or not, 264204494Srwatson * initialize it if permitted. 265204494Srwatson */ 266204494Srwatsonint 267204494Srwatson_kvm_dpcpu_initialized(kvm_t *kd, int intialize) 268204494Srwatson{ 269204494Srwatson 270204494Srwatson if (kd->dpcpu_initialized || !intialize) 271204494Srwatson return (kd->dpcpu_initialized); 272204494Srwatson 273204494Srwatson (void)_kvm_dpcpu_init(kd); 274204494Srwatson 275204494Srwatson return (kd->dpcpu_initialized); 276204494Srwatson} 277204494Srwatson 278204494Srwatson/* 279204494Srwatson * Check whether the value is within the dpcpu symbol range and only if so 280204494Srwatson * adjust the offset relative to the current offset. 281204494Srwatson */ 282204494Srwatsonuintptr_t 283204494Srwatson_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value) 284204494Srwatson{ 285204494Srwatson 286204494Srwatson if (value == 0) 287204494Srwatson return (value); 288204494Srwatson 289204494Srwatson if (!kd->dpcpu_initialized) 290204494Srwatson return (value); 291204494Srwatson 292204494Srwatson if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) 293204494Srwatson return (value); 294204494Srwatson 295204494Srwatson return (kd->dpcpu_curoff + value); 296204494Srwatson} 297204494Srwatson 298204494Srwatsonint 299204494Srwatsonkvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) 300204494Srwatson{ 301204494Srwatson int ret; 302204494Srwatson 303204494Srwatson if (!kd->dpcpu_initialized) { 304204494Srwatson ret = _kvm_dpcpu_init(kd); 305204494Srwatson if (ret != 0) { 306204494Srwatson _kvm_err(kd, kd->program, "%s: init failed", 307204494Srwatson __func__); 308204494Srwatson return (ret); 309204494Srwatson } 310204494Srwatson } 311204494Srwatson 312204494Srwatson return (_kvm_dpcpu_setcpu(kd, cpu, 1)); 313204494Srwatson} 314249344Sglebius 315249344Sglebius/* 316249344Sglebius * Obtain a per-CPU copy for given cpu from UMA_ZONE_PCPU allocation. 317249344Sglebius */ 318249344Sglebiusssize_t 319262740Sglebiuskvm_read_zpcpu(kvm_t *kd, u_long base, void *buf, size_t size, int cpu) 320249344Sglebius{ 321249344Sglebius 322249344Sglebius return (kvm_read(kd, (uintptr_t)(base + sizeof(struct pcpu) * cpu), 323249344Sglebius buf, size)); 324249344Sglebius} 325249344Sglebius 326249344Sglebius/* 327249344Sglebius * Fetch value of a counter(9). 328249344Sglebius */ 329249344Sglebiusuint64_t 330249344Sglebiuskvm_counter_u64_fetch(kvm_t *kd, u_long base) 331249344Sglebius{ 332249344Sglebius uint64_t r, c; 333249344Sglebius 334249344Sglebius if (mp_ncpus == 0) 335249344Sglebius if (_kvm_pcpu_init(kd) < 0) 336249344Sglebius return (0); 337249344Sglebius 338249344Sglebius r = 0; 339249344Sglebius for (int i = 0; i < mp_ncpus; i++) { 340262740Sglebius if (kvm_read_zpcpu(kd, base, &c, sizeof(c), i) != sizeof(c)) 341249344Sglebius return (0); 342249344Sglebius r += c; 343249344Sglebius } 344249344Sglebius 345249344Sglebius return (r); 346249344Sglebius} 347