137b1ce13SDoug Ambrisko /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 437b1ce13SDoug Ambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 537b1ce13SDoug Ambrisko * All rights reserved. 637b1ce13SDoug Ambrisko * 737b1ce13SDoug Ambrisko * Redistribution and use in source and binary forms, with or without 837b1ce13SDoug Ambrisko * modification, are permitted provided that the following conditions 937b1ce13SDoug Ambrisko * are met: 1037b1ce13SDoug Ambrisko * 1. Redistributions of source code must retain the above copyright 1137b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer. 1237b1ce13SDoug Ambrisko * 2. Redistributions in binary form must reproduce the above copyright 1337b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer in the 1437b1ce13SDoug Ambrisko * documentation and/or other materials provided with the distribution. 1537b1ce13SDoug Ambrisko * 1637b1ce13SDoug Ambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1737b1ce13SDoug Ambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1837b1ce13SDoug Ambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1937b1ce13SDoug Ambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2037b1ce13SDoug Ambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2137b1ce13SDoug Ambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2237b1ce13SDoug Ambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2337b1ce13SDoug Ambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2437b1ce13SDoug Ambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2537b1ce13SDoug Ambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2637b1ce13SDoug Ambrisko * SUCH DAMAGE. 2737b1ce13SDoug Ambrisko */ 2837b1ce13SDoug Ambrisko 2937b1ce13SDoug Ambrisko #include <sys/cdefs.h> 3037b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$"); 3137b1ce13SDoug Ambrisko 3237b1ce13SDoug Ambrisko #include <sys/param.h> 3337b1ce13SDoug Ambrisko #include <sys/systm.h> 34d72a0786SJohn Baldwin #include <sys/bus.h> 35d72a0786SJohn Baldwin #include <sys/condvar.h> 36d72a0786SJohn Baldwin #include <sys/eventhandler.h> 3737b1ce13SDoug Ambrisko #include <sys/kernel.h> 3837b1ce13SDoug Ambrisko #include <sys/selinfo.h> 3937b1ce13SDoug Ambrisko 4037b1ce13SDoug Ambrisko #include <vm/vm.h> 4137b1ce13SDoug Ambrisko #include <vm/pmap.h> 42d0673fe1SAllan Jude #if defined(__amd64__) || defined(__i386__) 4337b1ce13SDoug Ambrisko #include <machine/pc/bios.h> 44d0673fe1SAllan Jude #endif 45d0673fe1SAllan Jude #include <dev/smbios/smbios.h> 4637b1ce13SDoug Ambrisko 4737b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 4837b1ce13SDoug Ambrisko #include <ipmi.h> 4937b1ce13SDoug Ambrisko #include <ipmivars.h> 5037b1ce13SDoug Ambrisko #else 5137b1ce13SDoug Ambrisko #include <sys/ipmi.h> 5237b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 5337b1ce13SDoug Ambrisko #endif 5437b1ce13SDoug Ambrisko 55d72a0786SJohn Baldwin struct ipmi_entry { 5637b1ce13SDoug Ambrisko uint8_t type; 5737b1ce13SDoug Ambrisko uint8_t length; 5837b1ce13SDoug Ambrisko uint16_t handle; 5937b1ce13SDoug Ambrisko uint8_t interface_type; 6037b1ce13SDoug Ambrisko uint8_t spec_revision; 6137b1ce13SDoug Ambrisko uint8_t i2c_slave_address; 6237b1ce13SDoug Ambrisko uint8_t NV_storage_device_address; 6337b1ce13SDoug Ambrisko uint64_t base_address; 6437b1ce13SDoug Ambrisko uint8_t base_address_modifier; 6537b1ce13SDoug Ambrisko uint8_t interrupt_number; 6637b1ce13SDoug Ambrisko }; 6737b1ce13SDoug Ambrisko 68d72a0786SJohn Baldwin /* Fields in the base_address field of an IPMI entry. */ 69d72a0786SJohn Baldwin #define IPMI_BAR_MODE(ba) ((ba) & 0x0000000000000001) 70d72a0786SJohn Baldwin #define IPMI_BAR_ADDR(ba) ((ba) & 0xfffffffffffffffe) 71d72a0786SJohn Baldwin 72d72a0786SJohn Baldwin /* Fields in the base_address_modifier field of an IPMI entry. */ 73d72a0786SJohn Baldwin #define IPMI_BAM_IRQ_TRIGGER 0x01 74d72a0786SJohn Baldwin #define IPMI_BAM_IRQ_POLARITY 0x02 75d72a0786SJohn Baldwin #define IPMI_BAM_IRQ_VALID 0x08 76d72a0786SJohn Baldwin #define IPMI_BAM_ADDR_LSB(bam) (((bam) & 0x10) >> 4) 77d72a0786SJohn Baldwin #define IPMI_BAM_REG_SPACING(bam) (((bam) & 0xc0) >> 6) 78d72a0786SJohn Baldwin #define SPACING_8 0x0 79d72a0786SJohn Baldwin #define SPACING_32 0x1 80d72a0786SJohn Baldwin #define SPACING_16 0x2 81d72a0786SJohn Baldwin 82960b5a70SJohn Baldwin typedef void (*smbios_callback_t)(struct smbios_structure_header *, void *); 8337b1ce13SDoug Ambrisko 84d72a0786SJohn Baldwin static struct ipmi_get_info ipmi_info; 85d72a0786SJohn Baldwin static int ipmi_probed; 86d72a0786SJohn Baldwin static struct mtx ipmi_info_mtx; 87d72a0786SJohn Baldwin MTX_SYSINIT(ipmi_info, &ipmi_info_mtx, "ipmi info", MTX_DEF); 88d72a0786SJohn Baldwin 89d72a0786SJohn Baldwin static void ipmi_smbios_probe(struct ipmi_get_info *); 90*4a5dfdedSAllan Jude static int smbios_cksum(struct smbios_eps *); 91ba6e37e4SAllan Jude static void smbios_walk_table(uint8_t *, vm_size_t, smbios_callback_t, 92020e9fc3SJohn Baldwin void *); 93960b5a70SJohn Baldwin static void smbios_ipmi_info(struct smbios_structure_header *, void *); 9437b1ce13SDoug Ambrisko 9537b1ce13SDoug Ambrisko static void 96960b5a70SJohn Baldwin smbios_ipmi_info(struct smbios_structure_header *h, void *arg) 9737b1ce13SDoug Ambrisko { 98020e9fc3SJohn Baldwin struct ipmi_get_info *info; 99020e9fc3SJohn Baldwin struct ipmi_entry *s; 10037b1ce13SDoug Ambrisko 101020e9fc3SJohn Baldwin if (h->type != 38 || h->length < 102020e9fc3SJohn Baldwin offsetof(struct ipmi_entry, interrupt_number)) 103020e9fc3SJohn Baldwin return; 104020e9fc3SJohn Baldwin s = (struct ipmi_entry *)h; 105020e9fc3SJohn Baldwin info = arg; 10637b1ce13SDoug Ambrisko bzero(info, sizeof(struct ipmi_get_info)); 107d72a0786SJohn Baldwin switch (s->interface_type) { 108d72a0786SJohn Baldwin case KCS_MODE: 109d72a0786SJohn Baldwin case SMIC_MODE: 110d72a0786SJohn Baldwin info->address = IPMI_BAR_ADDR(s->base_address) | 111d72a0786SJohn Baldwin IPMI_BAM_ADDR_LSB(s->base_address_modifier); 112d72a0786SJohn Baldwin info->io_mode = IPMI_BAR_MODE(s->base_address); 113d72a0786SJohn Baldwin switch (IPMI_BAM_REG_SPACING(s->base_address_modifier)) { 114d72a0786SJohn Baldwin case SPACING_8: 11537b1ce13SDoug Ambrisko info->offset = 1; 11637b1ce13SDoug Ambrisko break; 117d72a0786SJohn Baldwin case SPACING_32: 11837b1ce13SDoug Ambrisko info->offset = 4; 11937b1ce13SDoug Ambrisko break; 120d72a0786SJohn Baldwin case SPACING_16: 12137b1ce13SDoug Ambrisko info->offset = 2; 12237b1ce13SDoug Ambrisko break; 123d72a0786SJohn Baldwin default: 124d72a0786SJohn Baldwin printf("SMBIOS: Invalid register spacing\n"); 125d72a0786SJohn Baldwin return; 126d72a0786SJohn Baldwin } 127d72a0786SJohn Baldwin break; 128d72a0786SJohn Baldwin case SSIF_MODE: 129d72a0786SJohn Baldwin if ((s->base_address & 0xffffffffffffff00) != 0) { 130d72a0786SJohn Baldwin printf("SMBIOS: Invalid SSIF SMBus address, using BMC I2C slave address instead\n"); 131bb6bb7feSJohn Baldwin info->address = s->i2c_slave_address; 13237b1ce13SDoug Ambrisko break; 13337b1ce13SDoug Ambrisko } 134bb6bb7feSJohn Baldwin info->address = IPMI_BAR_ADDR(s->base_address); 135d72a0786SJohn Baldwin break; 136d72a0786SJohn Baldwin default: 137d72a0786SJohn Baldwin return; 138d72a0786SJohn Baldwin } 139d72a0786SJohn Baldwin if (s->length > offsetof(struct ipmi_entry, interrupt_number)) { 140d72a0786SJohn Baldwin if (s->interrupt_number > 15) 141d72a0786SJohn Baldwin printf("SMBIOS: Non-ISA IRQ %d for IPMI\n", 142d72a0786SJohn Baldwin s->interrupt_number); 143d72a0786SJohn Baldwin else 144d72a0786SJohn Baldwin info->irq = s->interrupt_number; 145d72a0786SJohn Baldwin } 146d72a0786SJohn Baldwin info->iface_type = s->interface_type; 14737b1ce13SDoug Ambrisko } 14837b1ce13SDoug Ambrisko 14937b1ce13SDoug Ambrisko static void 150ba6e37e4SAllan Jude smbios_walk_table(uint8_t *table, vm_size_t size, smbios_callback_t cb, void *arg) 15137b1ce13SDoug Ambrisko { 152960b5a70SJohn Baldwin struct smbios_structure_header *s; 153ba6e37e4SAllan Jude uint8_t *p; 15437b1ce13SDoug Ambrisko 155ba6e37e4SAllan Jude for (p = table; p < table + size;) { 156960b5a70SJohn Baldwin s = (struct smbios_structure_header *)p; 157020e9fc3SJohn Baldwin cb(s, arg); 15837b1ce13SDoug Ambrisko 15937b1ce13SDoug Ambrisko /* 160020e9fc3SJohn Baldwin * Look for a double-nul after the end of the 161020e9fc3SJohn Baldwin * formatted area of this structure. 16237b1ce13SDoug Ambrisko */ 163020e9fc3SJohn Baldwin p += s->length; 164ba6e37e4SAllan Jude while (!(p[0] == 0 && p[1] == 0)) { 165020e9fc3SJohn Baldwin p++; 166ba6e37e4SAllan Jude if (p >= table + size) 167ba6e37e4SAllan Jude return; 168ba6e37e4SAllan Jude } 16937b1ce13SDoug Ambrisko 170020e9fc3SJohn Baldwin /* 171020e9fc3SJohn Baldwin * Skip over the double-nul to the start of the next 172020e9fc3SJohn Baldwin * structure. 173020e9fc3SJohn Baldwin */ 174020e9fc3SJohn Baldwin p += 2; 17537b1ce13SDoug Ambrisko } 17637b1ce13SDoug Ambrisko } 17737b1ce13SDoug Ambrisko 178d72a0786SJohn Baldwin /* 179d72a0786SJohn Baldwin * Walk the SMBIOS table looking for an IPMI (type 38) entry. If we find 180d72a0786SJohn Baldwin * one, return the parsed data in the passed in ipmi_get_info structure and 181d72a0786SJohn Baldwin * return true. If we don't find one, return false. 182d72a0786SJohn Baldwin */ 183d72a0786SJohn Baldwin static void 184d72a0786SJohn Baldwin ipmi_smbios_probe(struct ipmi_get_info *info) 18537b1ce13SDoug Ambrisko { 186d72a0786SJohn Baldwin void *table; 187ba6e37e4SAllan Jude vm_paddr_t table_paddr; 188ba6e37e4SAllan Jude vm_size_t table_size; 189ba6e37e4SAllan Jude int err; 19037b1ce13SDoug Ambrisko 191d72a0786SJohn Baldwin bzero(info, sizeof(struct ipmi_get_info)); 192d72a0786SJohn Baldwin 193ba6e37e4SAllan Jude err = smbios_get_structure_table(&table_paddr, &table_size); 194ba6e37e4SAllan Jude if (err != 0) 195d72a0786SJohn Baldwin return; 19637b1ce13SDoug Ambrisko 197ba6e37e4SAllan Jude table = pmap_mapbios(table_paddr, table_size); 19837b1ce13SDoug Ambrisko 199ba6e37e4SAllan Jude smbios_walk_table(table, table_size, smbios_ipmi_info, info); 20037b1ce13SDoug Ambrisko 201d72a0786SJohn Baldwin /* Unmap everything. */ 202ba6e37e4SAllan Jude pmap_unmapbios((vm_offset_t)table, table_size); 20337b1ce13SDoug Ambrisko } 20437b1ce13SDoug Ambrisko 20537b1ce13SDoug Ambrisko /* 206d72a0786SJohn Baldwin * Return the SMBIOS IPMI table entry info to the caller. If we haven't 207d72a0786SJohn Baldwin * searched the IPMI table yet, search it. Otherwise, return a cached 208d72a0786SJohn Baldwin * copy of the data. 20937b1ce13SDoug Ambrisko */ 210d72a0786SJohn Baldwin int 211d72a0786SJohn Baldwin ipmi_smbios_identify(struct ipmi_get_info *info) 212d72a0786SJohn Baldwin { 213d72a0786SJohn Baldwin 214d72a0786SJohn Baldwin mtx_lock(&ipmi_info_mtx); 215d72a0786SJohn Baldwin switch (ipmi_probed) { 216d72a0786SJohn Baldwin case 0: 217d72a0786SJohn Baldwin /* Need to probe the SMBIOS table. */ 218d72a0786SJohn Baldwin ipmi_probed++; 219d72a0786SJohn Baldwin mtx_unlock(&ipmi_info_mtx); 220d72a0786SJohn Baldwin ipmi_smbios_probe(&ipmi_info); 221d72a0786SJohn Baldwin mtx_lock(&ipmi_info_mtx); 222d72a0786SJohn Baldwin ipmi_probed++; 223d72a0786SJohn Baldwin wakeup(&ipmi_info); 224d72a0786SJohn Baldwin break; 225d72a0786SJohn Baldwin case 1: 226d72a0786SJohn Baldwin /* Another thread is currently probing the table, so wait. */ 227d72a0786SJohn Baldwin while (ipmi_probed == 1) 228d72a0786SJohn Baldwin msleep(&ipmi_info, &ipmi_info_mtx, 0, "ipmi info", 0); 229d72a0786SJohn Baldwin break; 230d72a0786SJohn Baldwin default: 231d72a0786SJohn Baldwin /* The cached data is available. */ 232d72a0786SJohn Baldwin break; 233d72a0786SJohn Baldwin } 234d72a0786SJohn Baldwin 235d72a0786SJohn Baldwin bcopy(&ipmi_info, info, sizeof(ipmi_info)); 236d72a0786SJohn Baldwin mtx_unlock(&ipmi_info_mtx); 237d72a0786SJohn Baldwin 238d72a0786SJohn Baldwin return (info->iface_type != 0); 23937b1ce13SDoug Ambrisko } 240*4a5dfdedSAllan Jude 241*4a5dfdedSAllan Jude static int 242*4a5dfdedSAllan Jude smbios_cksum(struct smbios_eps *e) 243*4a5dfdedSAllan Jude { 244*4a5dfdedSAllan Jude u_int8_t *ptr; 245*4a5dfdedSAllan Jude u_int8_t cksum; 246*4a5dfdedSAllan Jude int i; 247*4a5dfdedSAllan Jude 248*4a5dfdedSAllan Jude ptr = (u_int8_t *)e; 249*4a5dfdedSAllan Jude cksum = 0; 250*4a5dfdedSAllan Jude for (i = 0; i < e->length; i++) { 251*4a5dfdedSAllan Jude cksum += ptr[i]; 252*4a5dfdedSAllan Jude } 253*4a5dfdedSAllan Jude 254*4a5dfdedSAllan Jude return (cksum); 255*4a5dfdedSAllan Jude } 256