137b1ce13SDoug Ambrisko /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 3037b1ce13SDoug Ambrisko #include <sys/systm.h> 31d72a0786SJohn Baldwin #include <sys/bus.h> 32d72a0786SJohn Baldwin #include <sys/condvar.h> 33d72a0786SJohn Baldwin #include <sys/eventhandler.h> 3437b1ce13SDoug Ambrisko #include <sys/kernel.h> 3537b1ce13SDoug Ambrisko #include <sys/selinfo.h> 36ee8b757aSYinlong Lu #include <sys/efi.h> 3737b1ce13SDoug Ambrisko 3837b1ce13SDoug Ambrisko #include <vm/vm.h> 3937b1ce13SDoug Ambrisko #include <vm/pmap.h> 40d0673fe1SAllan Jude #if defined(__amd64__) || defined(__i386__) 4137b1ce13SDoug Ambrisko #include <machine/pc/bios.h> 42d0673fe1SAllan Jude #endif 43d0673fe1SAllan Jude #include <dev/smbios/smbios.h> 4437b1ce13SDoug Ambrisko 4537b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 4637b1ce13SDoug Ambrisko #include <ipmi.h> 4737b1ce13SDoug Ambrisko #include <ipmivars.h> 4837b1ce13SDoug Ambrisko #else 4937b1ce13SDoug Ambrisko #include <sys/ipmi.h> 5037b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 5137b1ce13SDoug Ambrisko #endif 5237b1ce13SDoug Ambrisko 53d72a0786SJohn Baldwin struct ipmi_entry { 5437b1ce13SDoug Ambrisko uint8_t type; 5537b1ce13SDoug Ambrisko uint8_t length; 5637b1ce13SDoug Ambrisko uint16_t handle; 5737b1ce13SDoug Ambrisko uint8_t interface_type; 5837b1ce13SDoug Ambrisko uint8_t spec_revision; 5937b1ce13SDoug Ambrisko uint8_t i2c_slave_address; 6037b1ce13SDoug Ambrisko uint8_t NV_storage_device_address; 6137b1ce13SDoug Ambrisko uint64_t base_address; 6237b1ce13SDoug Ambrisko uint8_t base_address_modifier; 6337b1ce13SDoug Ambrisko uint8_t interrupt_number; 6437b1ce13SDoug Ambrisko }; 6537b1ce13SDoug Ambrisko 66d72a0786SJohn Baldwin /* Fields in the base_address field of an IPMI entry. */ 67d72a0786SJohn Baldwin #define IPMI_BAR_MODE(ba) ((ba) & 0x0000000000000001) 68d72a0786SJohn Baldwin #define IPMI_BAR_ADDR(ba) ((ba) & 0xfffffffffffffffe) 69d72a0786SJohn Baldwin 70d72a0786SJohn Baldwin /* Fields in the base_address_modifier field of an IPMI entry. */ 71d72a0786SJohn Baldwin #define IPMI_BAM_IRQ_TRIGGER 0x01 72d72a0786SJohn Baldwin #define IPMI_BAM_IRQ_POLARITY 0x02 73d72a0786SJohn Baldwin #define IPMI_BAM_IRQ_VALID 0x08 74d72a0786SJohn Baldwin #define IPMI_BAM_ADDR_LSB(bam) (((bam) & 0x10) >> 4) 75d72a0786SJohn Baldwin #define IPMI_BAM_REG_SPACING(bam) (((bam) & 0xc0) >> 6) 76d72a0786SJohn Baldwin #define SPACING_8 0x0 77d72a0786SJohn Baldwin #define SPACING_32 0x1 78d72a0786SJohn Baldwin #define SPACING_16 0x2 79d72a0786SJohn Baldwin 80d72a0786SJohn Baldwin static struct ipmi_get_info ipmi_info; 81d72a0786SJohn Baldwin static int ipmi_probed; 82d72a0786SJohn Baldwin static struct mtx ipmi_info_mtx; 83d72a0786SJohn Baldwin MTX_SYSINIT(ipmi_info, &ipmi_info_mtx, "ipmi info", MTX_DEF); 84d72a0786SJohn Baldwin 85d72a0786SJohn Baldwin static void ipmi_smbios_probe(struct ipmi_get_info *); 864a5dfdedSAllan Jude static int smbios_cksum(struct smbios_eps *); 87960b5a70SJohn Baldwin static void smbios_ipmi_info(struct smbios_structure_header *, void *); 8837b1ce13SDoug Ambrisko 8937b1ce13SDoug Ambrisko static void 90960b5a70SJohn Baldwin smbios_ipmi_info(struct smbios_structure_header *h, void *arg) 9137b1ce13SDoug Ambrisko { 92020e9fc3SJohn Baldwin struct ipmi_get_info *info; 93020e9fc3SJohn Baldwin struct ipmi_entry *s; 9437b1ce13SDoug Ambrisko 95020e9fc3SJohn Baldwin if (h->type != 38 || h->length < 96020e9fc3SJohn Baldwin offsetof(struct ipmi_entry, interrupt_number)) 97020e9fc3SJohn Baldwin return; 98020e9fc3SJohn Baldwin s = (struct ipmi_entry *)h; 99020e9fc3SJohn Baldwin info = arg; 10037b1ce13SDoug Ambrisko bzero(info, sizeof(struct ipmi_get_info)); 101d72a0786SJohn Baldwin switch (s->interface_type) { 102d72a0786SJohn Baldwin case KCS_MODE: 103d72a0786SJohn Baldwin case SMIC_MODE: 1041f166509SAndrey V. Elsukov case BT_MODE: 105d72a0786SJohn Baldwin info->address = IPMI_BAR_ADDR(s->base_address) | 106d72a0786SJohn Baldwin IPMI_BAM_ADDR_LSB(s->base_address_modifier); 107d72a0786SJohn Baldwin info->io_mode = IPMI_BAR_MODE(s->base_address); 108d72a0786SJohn Baldwin switch (IPMI_BAM_REG_SPACING(s->base_address_modifier)) { 109d72a0786SJohn Baldwin case SPACING_8: 11037b1ce13SDoug Ambrisko info->offset = 1; 11137b1ce13SDoug Ambrisko break; 112d72a0786SJohn Baldwin case SPACING_32: 11337b1ce13SDoug Ambrisko info->offset = 4; 11437b1ce13SDoug Ambrisko break; 115d72a0786SJohn Baldwin case SPACING_16: 11637b1ce13SDoug Ambrisko info->offset = 2; 11737b1ce13SDoug Ambrisko break; 118d72a0786SJohn Baldwin default: 119d72a0786SJohn Baldwin printf("SMBIOS: Invalid register spacing\n"); 120d72a0786SJohn Baldwin return; 121d72a0786SJohn Baldwin } 122d72a0786SJohn Baldwin break; 123d72a0786SJohn Baldwin case SSIF_MODE: 124d72a0786SJohn Baldwin if ((s->base_address & 0xffffffffffffff00) != 0) { 125d72a0786SJohn Baldwin printf("SMBIOS: Invalid SSIF SMBus address, using BMC I2C slave address instead\n"); 126bb6bb7feSJohn Baldwin info->address = s->i2c_slave_address; 12737b1ce13SDoug Ambrisko break; 12837b1ce13SDoug Ambrisko } 129bb6bb7feSJohn Baldwin info->address = IPMI_BAR_ADDR(s->base_address); 130d72a0786SJohn Baldwin break; 131d72a0786SJohn Baldwin default: 132d72a0786SJohn Baldwin return; 133d72a0786SJohn Baldwin } 134d72a0786SJohn Baldwin if (s->length > offsetof(struct ipmi_entry, interrupt_number)) { 135d72a0786SJohn Baldwin if (s->interrupt_number > 15) 136d72a0786SJohn Baldwin printf("SMBIOS: Non-ISA IRQ %d for IPMI\n", 137d72a0786SJohn Baldwin s->interrupt_number); 138d72a0786SJohn Baldwin else 139d72a0786SJohn Baldwin info->irq = s->interrupt_number; 140d72a0786SJohn Baldwin } 141d72a0786SJohn Baldwin info->iface_type = s->interface_type; 14237b1ce13SDoug Ambrisko } 14337b1ce13SDoug Ambrisko 144d72a0786SJohn Baldwin /* 145d72a0786SJohn Baldwin * Walk the SMBIOS table looking for an IPMI (type 38) entry. If we find 146d72a0786SJohn Baldwin * one, return the parsed data in the passed in ipmi_get_info structure and 147d72a0786SJohn Baldwin * return true. If we don't find one, return false. 148d72a0786SJohn Baldwin */ 149d72a0786SJohn Baldwin static void 150d72a0786SJohn Baldwin ipmi_smbios_probe(struct ipmi_get_info *info) 15137b1ce13SDoug Ambrisko { 152ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI 153ee8b757aSYinlong Lu struct uuid efi_smbios; 154ee8b757aSYinlong Lu void *addr_efi; 155ee8b757aSYinlong Lu #endif 1566d67af5fSAllan Jude struct smbios_eps *header; 157d72a0786SJohn Baldwin void *table; 1586d67af5fSAllan Jude u_int32_t addr; 15937b1ce13SDoug Ambrisko 160ee8b757aSYinlong Lu addr = 0; 161d72a0786SJohn Baldwin bzero(info, sizeof(struct ipmi_get_info)); 162d72a0786SJohn Baldwin 163ee8b757aSYinlong Lu #ifdef ARCH_MAY_USE_EFI 164ee8b757aSYinlong Lu efi_smbios = (struct uuid)EFI_TABLE_SMBIOS; 165ee8b757aSYinlong Lu if (!efi_get_table(&efi_smbios, &addr_efi)) 166ee8b757aSYinlong Lu addr = (vm_paddr_t)addr_efi; 167ee8b757aSYinlong Lu #endif 168ee8b757aSYinlong Lu 169ce41bee0SWarner Losh #if defined(__amd64__) || defined(__i386__) 170ee8b757aSYinlong Lu if (addr == 0) 1716d67af5fSAllan Jude /* Find the SMBIOS table header. */ 1726d67af5fSAllan Jude addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, 1736d67af5fSAllan Jude SMBIOS_STEP, SMBIOS_OFF); 174ce41bee0SWarner Losh #endif 1756d67af5fSAllan Jude if (addr == 0) 176d72a0786SJohn Baldwin return; 17737b1ce13SDoug Ambrisko 1786d67af5fSAllan Jude /* 1796d67af5fSAllan Jude * Map the header. We first map a fixed size to get the actual 1806d67af5fSAllan Jude * length and then map it a second time with the actual length so 1816d67af5fSAllan Jude * we can verify the checksum. 1826d67af5fSAllan Jude */ 1836d67af5fSAllan Jude header = pmap_mapbios(addr, sizeof(struct smbios_eps)); 1846d67af5fSAllan Jude table = pmap_mapbios(addr, header->length); 1857ae99f80SJohn Baldwin pmap_unmapbios(header, sizeof(struct smbios_eps)); 1866d67af5fSAllan Jude header = table; 1876d67af5fSAllan Jude if (smbios_cksum(header) != 0) { 1887ae99f80SJohn Baldwin pmap_unmapbios(header, header->length); 1896d67af5fSAllan Jude return; 1906d67af5fSAllan Jude } 19137b1ce13SDoug Ambrisko 1926d67af5fSAllan Jude /* Now map the actual table and walk it looking for an IPMI entry. */ 1936d67af5fSAllan Jude table = pmap_mapbios(header->structure_table_address, 1946d67af5fSAllan Jude header->structure_table_length); 195*06326613SWarner Losh smbios_walk_table(table, header->number_structures, 196*06326613SWarner Losh header->structure_table_length, smbios_ipmi_info, info); 19737b1ce13SDoug Ambrisko 198d72a0786SJohn Baldwin /* Unmap everything. */ 1997ae99f80SJohn Baldwin pmap_unmapbios(table, header->structure_table_length); 2007ae99f80SJohn Baldwin pmap_unmapbios(header, header->length); 20137b1ce13SDoug Ambrisko } 20237b1ce13SDoug Ambrisko 20337b1ce13SDoug Ambrisko /* 204d72a0786SJohn Baldwin * Return the SMBIOS IPMI table entry info to the caller. If we haven't 205d72a0786SJohn Baldwin * searched the IPMI table yet, search it. Otherwise, return a cached 206d72a0786SJohn Baldwin * copy of the data. 20737b1ce13SDoug Ambrisko */ 208d72a0786SJohn Baldwin int 209d72a0786SJohn Baldwin ipmi_smbios_identify(struct ipmi_get_info *info) 210d72a0786SJohn Baldwin { 211d72a0786SJohn Baldwin 212d72a0786SJohn Baldwin mtx_lock(&ipmi_info_mtx); 213d72a0786SJohn Baldwin switch (ipmi_probed) { 214d72a0786SJohn Baldwin case 0: 215d72a0786SJohn Baldwin /* Need to probe the SMBIOS table. */ 216d72a0786SJohn Baldwin ipmi_probed++; 217d72a0786SJohn Baldwin mtx_unlock(&ipmi_info_mtx); 218d72a0786SJohn Baldwin ipmi_smbios_probe(&ipmi_info); 219d72a0786SJohn Baldwin mtx_lock(&ipmi_info_mtx); 220d72a0786SJohn Baldwin ipmi_probed++; 221d72a0786SJohn Baldwin wakeup(&ipmi_info); 222d72a0786SJohn Baldwin break; 223d72a0786SJohn Baldwin case 1: 224d72a0786SJohn Baldwin /* Another thread is currently probing the table, so wait. */ 225d72a0786SJohn Baldwin while (ipmi_probed == 1) 226d72a0786SJohn Baldwin msleep(&ipmi_info, &ipmi_info_mtx, 0, "ipmi info", 0); 227d72a0786SJohn Baldwin break; 228d72a0786SJohn Baldwin default: 229d72a0786SJohn Baldwin /* The cached data is available. */ 230d72a0786SJohn Baldwin break; 231d72a0786SJohn Baldwin } 232d72a0786SJohn Baldwin 233d72a0786SJohn Baldwin bcopy(&ipmi_info, info, sizeof(ipmi_info)); 234d72a0786SJohn Baldwin mtx_unlock(&ipmi_info_mtx); 235d72a0786SJohn Baldwin 236d72a0786SJohn Baldwin return (info->iface_type != 0); 23737b1ce13SDoug Ambrisko } 2384a5dfdedSAllan Jude 2394a5dfdedSAllan Jude static int 2404a5dfdedSAllan Jude smbios_cksum(struct smbios_eps *e) 2414a5dfdedSAllan Jude { 2424a5dfdedSAllan Jude u_int8_t *ptr; 2434a5dfdedSAllan Jude u_int8_t cksum; 2444a5dfdedSAllan Jude int i; 2454a5dfdedSAllan Jude 2464a5dfdedSAllan Jude ptr = (u_int8_t *)e; 2474a5dfdedSAllan Jude cksum = 0; 2484a5dfdedSAllan Jude for (i = 0; i < e->length; i++) { 2494a5dfdedSAllan Jude cksum += ptr[i]; 2504a5dfdedSAllan Jude } 2514a5dfdedSAllan Jude 2524a5dfdedSAllan Jude return (cksum); 2534a5dfdedSAllan Jude } 254