10112b52bSAndriy Gapon /*- 20112b52bSAndriy Gapon * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>. 30112b52bSAndriy Gapon * All rights reserved. 40112b52bSAndriy Gapon * 50112b52bSAndriy Gapon * Redistribution and use in source and binary forms, with or without 60112b52bSAndriy Gapon * modification, are permitted provided that the following conditions 70112b52bSAndriy Gapon * are met: 80112b52bSAndriy Gapon * 1. Redistributions of source code must retain the above copyright 90112b52bSAndriy Gapon * notice, this list of conditions and the following disclaimer. 100112b52bSAndriy Gapon * 2. Redistributions in binary form must reproduce the above copyright 110112b52bSAndriy Gapon * notice, this list of conditions and the following disclaimer in the 120112b52bSAndriy Gapon * documentation and/or other materials provided with the distribution. 130112b52bSAndriy Gapon * 140112b52bSAndriy Gapon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 150112b52bSAndriy Gapon * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 160112b52bSAndriy Gapon * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 170112b52bSAndriy Gapon * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 180112b52bSAndriy Gapon * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 190112b52bSAndriy Gapon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 200112b52bSAndriy Gapon * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 210112b52bSAndriy Gapon * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 220112b52bSAndriy Gapon * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 230112b52bSAndriy Gapon * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 240112b52bSAndriy Gapon */ 250112b52bSAndriy Gapon 260112b52bSAndriy Gapon #include <sys/cdefs.h> 270112b52bSAndriy Gapon __FBSDID("$FreeBSD$"); 280112b52bSAndriy Gapon 290112b52bSAndriy Gapon #include <sys/types.h> 300112b52bSAndriy Gapon #include <sys/stat.h> 310112b52bSAndriy Gapon #include <sys/mman.h> 320112b52bSAndriy Gapon #include <sys/ioctl.h> 330112b52bSAndriy Gapon #include <sys/ioccom.h> 340112b52bSAndriy Gapon #include <sys/cpuctl.h> 350112b52bSAndriy Gapon 360112b52bSAndriy Gapon #include <machine/cpufunc.h> 370112b52bSAndriy Gapon #include <machine/specialreg.h> 380112b52bSAndriy Gapon 390112b52bSAndriy Gapon #include <assert.h> 400112b52bSAndriy Gapon #include <stdio.h> 410112b52bSAndriy Gapon #include <stdlib.h> 420112b52bSAndriy Gapon #include <string.h> 430112b52bSAndriy Gapon #include <unistd.h> 440112b52bSAndriy Gapon #include <fcntl.h> 450112b52bSAndriy Gapon #include <err.h> 460112b52bSAndriy Gapon 470112b52bSAndriy Gapon #include "cpucontrol.h" 480112b52bSAndriy Gapon #include "amd.h" 490112b52bSAndriy Gapon 500112b52bSAndriy Gapon int 510112b52bSAndriy Gapon amd10h_probe(int fd) 520112b52bSAndriy Gapon { 530112b52bSAndriy Gapon char vendor[13]; 540112b52bSAndriy Gapon cpuctl_cpuid_args_t idargs; 550112b52bSAndriy Gapon uint32_t family; 560112b52bSAndriy Gapon uint32_t signature; 570112b52bSAndriy Gapon int error; 580112b52bSAndriy Gapon 590112b52bSAndriy Gapon idargs.level = 0; 600112b52bSAndriy Gapon error = ioctl(fd, CPUCTL_CPUID, &idargs); 610112b52bSAndriy Gapon if (error < 0) { 620112b52bSAndriy Gapon WARN(0, "ioctl()"); 630112b52bSAndriy Gapon return (1); 640112b52bSAndriy Gapon } 650112b52bSAndriy Gapon ((uint32_t *)vendor)[0] = idargs.data[1]; 660112b52bSAndriy Gapon ((uint32_t *)vendor)[1] = idargs.data[3]; 670112b52bSAndriy Gapon ((uint32_t *)vendor)[2] = idargs.data[2]; 680112b52bSAndriy Gapon vendor[12] = '\0'; 690112b52bSAndriy Gapon if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0) 700112b52bSAndriy Gapon return (1); 710112b52bSAndriy Gapon 720112b52bSAndriy Gapon idargs.level = 1; 730112b52bSAndriy Gapon error = ioctl(fd, CPUCTL_CPUID, &idargs); 740112b52bSAndriy Gapon if (error < 0) { 750112b52bSAndriy Gapon WARN(0, "ioctl()"); 760112b52bSAndriy Gapon return (1); 770112b52bSAndriy Gapon } 780112b52bSAndriy Gapon signature = idargs.data[0]; 790112b52bSAndriy Gapon family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff); 800112b52bSAndriy Gapon if (family < 0x10) 810112b52bSAndriy Gapon return (1); 820112b52bSAndriy Gapon return (0); 830112b52bSAndriy Gapon } 840112b52bSAndriy Gapon 850112b52bSAndriy Gapon /* 860112b52bSAndriy Gapon * NB: the format of microcode update files is not documented by AMD. 870112b52bSAndriy Gapon * It has been reverse engineered from studying Coreboot, illumos and Linux 880112b52bSAndriy Gapon * source code. 890112b52bSAndriy Gapon */ 900112b52bSAndriy Gapon void 910112b52bSAndriy Gapon amd10h_update(const char *dev, const char *path) 920112b52bSAndriy Gapon { 930112b52bSAndriy Gapon struct stat st; 940112b52bSAndriy Gapon cpuctl_cpuid_args_t idargs; 950112b52bSAndriy Gapon cpuctl_msr_args_t msrargs; 960112b52bSAndriy Gapon cpuctl_update_args_t args; 970112b52bSAndriy Gapon const amd_10h_fw_header_t *fw_header; 980112b52bSAndriy Gapon const amd_10h_fw_header_t *selected_fw; 990112b52bSAndriy Gapon const equiv_cpu_entry_t *equiv_cpu_table; 1000112b52bSAndriy Gapon const section_header_t *section_header; 1010112b52bSAndriy Gapon const container_header_t *container_header; 1020112b52bSAndriy Gapon const uint8_t *fw_data; 1030112b52bSAndriy Gapon uint8_t *fw_image; 1040112b52bSAndriy Gapon size_t fw_size; 1050112b52bSAndriy Gapon size_t selected_size; 1060112b52bSAndriy Gapon uint32_t revision; 1070112b52bSAndriy Gapon uint32_t new_rev; 1080112b52bSAndriy Gapon uint32_t signature; 1090112b52bSAndriy Gapon uint16_t equiv_id; 1100112b52bSAndriy Gapon int fd, devfd; 1110112b52bSAndriy Gapon unsigned int i; 1120112b52bSAndriy Gapon int error; 1130112b52bSAndriy Gapon 1140112b52bSAndriy Gapon assert(path); 1150112b52bSAndriy Gapon assert(dev); 1160112b52bSAndriy Gapon 1170112b52bSAndriy Gapon fd = -1; 1180112b52bSAndriy Gapon fw_image = MAP_FAILED; 1190112b52bSAndriy Gapon devfd = open(dev, O_RDWR); 1200112b52bSAndriy Gapon if (devfd < 0) { 1210112b52bSAndriy Gapon WARN(0, "could not open %s for writing", dev); 1220112b52bSAndriy Gapon return; 1230112b52bSAndriy Gapon } 1240112b52bSAndriy Gapon idargs.level = 1; 1250112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_CPUID, &idargs); 1260112b52bSAndriy Gapon if (error < 0) { 1270112b52bSAndriy Gapon WARN(0, "ioctl()"); 1280112b52bSAndriy Gapon goto done; 1290112b52bSAndriy Gapon } 1300112b52bSAndriy Gapon signature = idargs.data[0]; 1310112b52bSAndriy Gapon 132*a18e40aaSMark Johnston msrargs.msr = MSR_BIOS_SIGN; 1330112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 1340112b52bSAndriy Gapon if (error < 0) { 1350112b52bSAndriy Gapon WARN(0, "ioctl(%s)", dev); 1360112b52bSAndriy Gapon goto done; 1370112b52bSAndriy Gapon } 1380112b52bSAndriy Gapon revision = (uint32_t)msrargs.data; 1390112b52bSAndriy Gapon 1400112b52bSAndriy Gapon WARNX(1, "found cpu family %#x model %#x " 1410112b52bSAndriy Gapon "stepping %#x extfamily %#x extmodel %#x.", 1426e9d170dSSean Bruno ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff), 1436e9d170dSSean Bruno (signature >> 4) & 0x0f, 1440112b52bSAndriy Gapon (signature >> 0) & 0x0f, (signature >> 20) & 0xff, 1450112b52bSAndriy Gapon (signature >> 16) & 0x0f); 1460112b52bSAndriy Gapon WARNX(1, "microcode revision %#x", revision); 1470112b52bSAndriy Gapon 1480112b52bSAndriy Gapon /* 1490112b52bSAndriy Gapon * Open the firmware file. 1500112b52bSAndriy Gapon */ 1516e9d170dSSean Bruno WARNX(1, "checking %s for update.", path); 1520112b52bSAndriy Gapon fd = open(path, O_RDONLY, 0); 1530112b52bSAndriy Gapon if (fd < 0) { 1540112b52bSAndriy Gapon WARN(0, "open(%s)", path); 1550112b52bSAndriy Gapon goto done; 1560112b52bSAndriy Gapon } 1570112b52bSAndriy Gapon error = fstat(fd, &st); 1580112b52bSAndriy Gapon if (error != 0) { 1590112b52bSAndriy Gapon WARN(0, "fstat(%s)", path); 1600112b52bSAndriy Gapon goto done; 1610112b52bSAndriy Gapon } 1620112b52bSAndriy Gapon if (st.st_size < 0 || (size_t)st.st_size < 1630112b52bSAndriy Gapon (sizeof(*container_header) + sizeof(*section_header))) { 1640112b52bSAndriy Gapon WARNX(2, "file too short: %s", path); 1650112b52bSAndriy Gapon goto done; 1660112b52bSAndriy Gapon } 1670112b52bSAndriy Gapon fw_size = st.st_size; 1680112b52bSAndriy Gapon 1690112b52bSAndriy Gapon /* 1700112b52bSAndriy Gapon * mmap the whole image. 1710112b52bSAndriy Gapon */ 1720112b52bSAndriy Gapon fw_image = (uint8_t *)mmap(NULL, st.st_size, PROT_READ, 1730112b52bSAndriy Gapon MAP_PRIVATE, fd, 0); 1740112b52bSAndriy Gapon if (fw_image == MAP_FAILED) { 1750112b52bSAndriy Gapon WARN(0, "mmap(%s)", path); 1760112b52bSAndriy Gapon goto done; 1770112b52bSAndriy Gapon } 1780112b52bSAndriy Gapon 1790112b52bSAndriy Gapon fw_data = fw_image; 1800112b52bSAndriy Gapon container_header = (const container_header_t *)fw_data; 1810112b52bSAndriy Gapon if (container_header->magic != AMD_10H_MAGIC) { 1820112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: bad magic", path); 1830112b52bSAndriy Gapon goto done; 1840112b52bSAndriy Gapon } 1850112b52bSAndriy Gapon fw_data += sizeof(*container_header); 1860112b52bSAndriy Gapon fw_size -= sizeof(*container_header); 1870112b52bSAndriy Gapon 1880112b52bSAndriy Gapon section_header = (const section_header_t *)fw_data; 1890112b52bSAndriy Gapon if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) { 1900112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 1910112b52bSAndriy Gapon "first section is not CPU equivalence table", path); 1920112b52bSAndriy Gapon goto done; 1930112b52bSAndriy Gapon } 1940112b52bSAndriy Gapon if (section_header->size == 0) { 1950112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 1960112b52bSAndriy Gapon "first section is empty", path); 1970112b52bSAndriy Gapon goto done; 1980112b52bSAndriy Gapon } 1990112b52bSAndriy Gapon fw_data += sizeof(*section_header); 2000112b52bSAndriy Gapon fw_size -= sizeof(*section_header); 2010112b52bSAndriy Gapon 2020112b52bSAndriy Gapon if (section_header->size > fw_size) { 2030112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2040112b52bSAndriy Gapon "file is truncated", path); 2050112b52bSAndriy Gapon goto done; 2060112b52bSAndriy Gapon } 2070112b52bSAndriy Gapon if (section_header->size < sizeof(*equiv_cpu_table)) { 2080112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2090112b52bSAndriy Gapon "first section is too short", path); 2100112b52bSAndriy Gapon goto done; 2110112b52bSAndriy Gapon } 2120112b52bSAndriy Gapon equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data; 2130112b52bSAndriy Gapon fw_data += section_header->size; 2140112b52bSAndriy Gapon fw_size -= section_header->size; 2150112b52bSAndriy Gapon 2160112b52bSAndriy Gapon equiv_id = 0; 2170112b52bSAndriy Gapon for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) { 2180112b52bSAndriy Gapon if (signature == equiv_cpu_table[i].installed_cpu) { 2190112b52bSAndriy Gapon equiv_id = equiv_cpu_table[i].equiv_cpu; 2206e9d170dSSean Bruno WARNX(3, "equiv_id: %x, signature %8x," 2216e9d170dSSean Bruno " equiv_cpu_table[%d] %8x", equiv_id, signature, 2226e9d170dSSean Bruno i, equiv_cpu_table[i].installed_cpu); 2230112b52bSAndriy Gapon break; 2240112b52bSAndriy Gapon } 2250112b52bSAndriy Gapon } 2260112b52bSAndriy Gapon if (equiv_id == 0) { 2270112b52bSAndriy Gapon WARNX(2, "CPU is not found in the equivalence table"); 2280112b52bSAndriy Gapon goto done; 2290112b52bSAndriy Gapon } 2300112b52bSAndriy Gapon 2310112b52bSAndriy Gapon selected_fw = NULL; 2320112b52bSAndriy Gapon selected_size = 0; 2330112b52bSAndriy Gapon while (fw_size >= sizeof(*section_header)) { 2340112b52bSAndriy Gapon section_header = (const section_header_t *)fw_data; 2350112b52bSAndriy Gapon fw_data += sizeof(*section_header); 2360112b52bSAndriy Gapon fw_size -= sizeof(*section_header); 2370112b52bSAndriy Gapon if (section_header->type != AMD_10H_uCODE_TYPE) { 2380112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2390112b52bSAndriy Gapon "section has incorret type", path); 2400112b52bSAndriy Gapon goto done; 2410112b52bSAndriy Gapon } 2420112b52bSAndriy Gapon if (section_header->size > fw_size) { 2430112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2440112b52bSAndriy Gapon "file is truncated", path); 2450112b52bSAndriy Gapon goto done; 2460112b52bSAndriy Gapon } 2470112b52bSAndriy Gapon if (section_header->size < sizeof(*fw_header)) { 2480112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2490112b52bSAndriy Gapon "section is too short", path); 2500112b52bSAndriy Gapon goto done; 2510112b52bSAndriy Gapon } 2520112b52bSAndriy Gapon fw_header = (const amd_10h_fw_header_t *)fw_data; 2530112b52bSAndriy Gapon fw_data += section_header->size; 2540112b52bSAndriy Gapon fw_size -= section_header->size; 2550112b52bSAndriy Gapon 2566e9d170dSSean Bruno if (fw_header->processor_rev_id != equiv_id) { 2576e9d170dSSean Bruno WARNX(1, "firmware processort_rev_id %x, equiv_id %x", 2586e9d170dSSean Bruno fw_header->processor_rev_id, equiv_id); 2590112b52bSAndriy Gapon continue; /* different cpu */ 2606e9d170dSSean Bruno } 2616e9d170dSSean Bruno if (fw_header->patch_id <= revision) { 2626e9d170dSSean Bruno WARNX(1, "patch_id %x, revision %x", 2636e9d170dSSean Bruno fw_header->patch_id, revision); 2640112b52bSAndriy Gapon continue; /* not newer revision */ 2656e9d170dSSean Bruno } 2660112b52bSAndriy Gapon if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) { 2670112b52bSAndriy Gapon WARNX(2, "Chipset-specific microcode is not supported"); 2680112b52bSAndriy Gapon } 2690112b52bSAndriy Gapon 2700112b52bSAndriy Gapon WARNX(3, "selecting revision: %x", fw_header->patch_id); 2710112b52bSAndriy Gapon revision = fw_header->patch_id; 2720112b52bSAndriy Gapon selected_fw = fw_header; 2730112b52bSAndriy Gapon selected_size = section_header->size; 2740112b52bSAndriy Gapon } 2750112b52bSAndriy Gapon 2760112b52bSAndriy Gapon if (fw_size != 0) { 2770112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2780112b52bSAndriy Gapon "file is truncated", path); 2790112b52bSAndriy Gapon goto done; 2800112b52bSAndriy Gapon } 2810112b52bSAndriy Gapon 2820112b52bSAndriy Gapon if (selected_fw != NULL) { 2830112b52bSAndriy Gapon WARNX(1, "selected ucode size is %zu", selected_size); 2840112b52bSAndriy Gapon fprintf(stderr, "%s: updating cpu %s to revision %#x... ", 2850112b52bSAndriy Gapon path, dev, revision); 2860112b52bSAndriy Gapon 2870112b52bSAndriy Gapon args.data = __DECONST(void *, selected_fw); 2880112b52bSAndriy Gapon args.size = selected_size; 2890112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_UPDATE, &args); 2900112b52bSAndriy Gapon if (error < 0) { 2910112b52bSAndriy Gapon fprintf(stderr, "failed.\n"); 2920112b52bSAndriy Gapon warn("ioctl()"); 2930112b52bSAndriy Gapon goto done; 2940112b52bSAndriy Gapon } 2950112b52bSAndriy Gapon fprintf(stderr, "done.\n"); 2960112b52bSAndriy Gapon } 2970112b52bSAndriy Gapon 298*a18e40aaSMark Johnston msrargs.msr = MSR_BIOS_SIGN; 2990112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 3000112b52bSAndriy Gapon if (error < 0) { 3010112b52bSAndriy Gapon WARN(0, "ioctl(%s)", dev); 3020112b52bSAndriy Gapon goto done; 3030112b52bSAndriy Gapon } 3040112b52bSAndriy Gapon new_rev = (uint32_t)msrargs.data; 3050112b52bSAndriy Gapon if (new_rev != revision) 3060112b52bSAndriy Gapon WARNX(0, "revision after update %#x", new_rev); 3070112b52bSAndriy Gapon 3080112b52bSAndriy Gapon done: 3090112b52bSAndriy Gapon if (fd >= 0) 3100112b52bSAndriy Gapon close(fd); 3110112b52bSAndriy Gapon if (devfd >= 0) 3120112b52bSAndriy Gapon close(devfd); 3130112b52bSAndriy Gapon if (fw_image != MAP_FAILED) 3140112b52bSAndriy Gapon if (munmap(fw_image, st.st_size) != 0) 3150112b52bSAndriy Gapon warn("munmap(%s)", path); 3160112b52bSAndriy Gapon return; 3170112b52bSAndriy Gapon } 318