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 91*dee401e8SConrad Meyer amd10h_update(const struct ucode_update_params *params) 920112b52bSAndriy Gapon { 930112b52bSAndriy Gapon cpuctl_cpuid_args_t idargs; 940112b52bSAndriy Gapon cpuctl_msr_args_t msrargs; 950112b52bSAndriy Gapon cpuctl_update_args_t args; 960112b52bSAndriy Gapon const amd_10h_fw_header_t *fw_header; 970112b52bSAndriy Gapon const amd_10h_fw_header_t *selected_fw; 980112b52bSAndriy Gapon const equiv_cpu_entry_t *equiv_cpu_table; 990112b52bSAndriy Gapon const section_header_t *section_header; 1000112b52bSAndriy Gapon const container_header_t *container_header; 1010112b52bSAndriy Gapon const uint8_t *fw_data; 102*dee401e8SConrad Meyer const uint8_t *fw_image; 103*dee401e8SConrad Meyer const char *dev, *path; 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; 110*dee401e8SConrad Meyer int devfd; 1110112b52bSAndriy Gapon unsigned int i; 1120112b52bSAndriy Gapon int error; 1130112b52bSAndriy Gapon 114*dee401e8SConrad Meyer dev = params->dev_path; 115*dee401e8SConrad Meyer path = params->fw_path; 116*dee401e8SConrad Meyer devfd = params->devfd; 117*dee401e8SConrad Meyer fw_image = params->fwimage; 118*dee401e8SConrad Meyer fw_size = params->fwsize; 119*dee401e8SConrad Meyer 1200112b52bSAndriy Gapon assert(path); 1210112b52bSAndriy Gapon assert(dev); 1220112b52bSAndriy Gapon 1230112b52bSAndriy Gapon idargs.level = 1; 1240112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_CPUID, &idargs); 1250112b52bSAndriy Gapon if (error < 0) { 1260112b52bSAndriy Gapon WARN(0, "ioctl()"); 1270112b52bSAndriy Gapon goto done; 1280112b52bSAndriy Gapon } 1290112b52bSAndriy Gapon signature = idargs.data[0]; 1300112b52bSAndriy Gapon 131a18e40aaSMark Johnston msrargs.msr = MSR_BIOS_SIGN; 1320112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 1330112b52bSAndriy Gapon if (error < 0) { 1340112b52bSAndriy Gapon WARN(0, "ioctl(%s)", dev); 1350112b52bSAndriy Gapon goto done; 1360112b52bSAndriy Gapon } 1370112b52bSAndriy Gapon revision = (uint32_t)msrargs.data; 1380112b52bSAndriy Gapon 1390112b52bSAndriy Gapon WARNX(1, "found cpu family %#x model %#x " 1400112b52bSAndriy Gapon "stepping %#x extfamily %#x extmodel %#x.", 1416e9d170dSSean Bruno ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff), 1426e9d170dSSean Bruno (signature >> 4) & 0x0f, 1430112b52bSAndriy Gapon (signature >> 0) & 0x0f, (signature >> 20) & 0xff, 1440112b52bSAndriy Gapon (signature >> 16) & 0x0f); 1450112b52bSAndriy Gapon WARNX(1, "microcode revision %#x", revision); 1460112b52bSAndriy Gapon 1470112b52bSAndriy Gapon /* 1480112b52bSAndriy Gapon * Open the firmware file. 1490112b52bSAndriy Gapon */ 1506e9d170dSSean Bruno WARNX(1, "checking %s for update.", path); 151*dee401e8SConrad Meyer if (fw_size < 1520112b52bSAndriy Gapon (sizeof(*container_header) + sizeof(*section_header))) { 1530112b52bSAndriy Gapon WARNX(2, "file too short: %s", path); 1540112b52bSAndriy Gapon goto done; 1550112b52bSAndriy Gapon } 1560112b52bSAndriy Gapon 1570112b52bSAndriy Gapon /* 1580112b52bSAndriy Gapon * mmap the whole image. 1590112b52bSAndriy Gapon */ 1600112b52bSAndriy Gapon fw_data = fw_image; 1610112b52bSAndriy Gapon container_header = (const container_header_t *)fw_data; 1620112b52bSAndriy Gapon if (container_header->magic != AMD_10H_MAGIC) { 1630112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: bad magic", path); 1640112b52bSAndriy Gapon goto done; 1650112b52bSAndriy Gapon } 1660112b52bSAndriy Gapon fw_data += sizeof(*container_header); 1670112b52bSAndriy Gapon fw_size -= sizeof(*container_header); 1680112b52bSAndriy Gapon 1690112b52bSAndriy Gapon section_header = (const section_header_t *)fw_data; 1700112b52bSAndriy Gapon if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) { 1710112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 1720112b52bSAndriy Gapon "first section is not CPU equivalence table", path); 1730112b52bSAndriy Gapon goto done; 1740112b52bSAndriy Gapon } 1750112b52bSAndriy Gapon if (section_header->size == 0) { 1760112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 1770112b52bSAndriy Gapon "first section is empty", path); 1780112b52bSAndriy Gapon goto done; 1790112b52bSAndriy Gapon } 1800112b52bSAndriy Gapon fw_data += sizeof(*section_header); 1810112b52bSAndriy Gapon fw_size -= sizeof(*section_header); 1820112b52bSAndriy Gapon 1830112b52bSAndriy Gapon if (section_header->size > fw_size) { 1840112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 1850112b52bSAndriy Gapon "file is truncated", path); 1860112b52bSAndriy Gapon goto done; 1870112b52bSAndriy Gapon } 1880112b52bSAndriy Gapon if (section_header->size < sizeof(*equiv_cpu_table)) { 1890112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 1900112b52bSAndriy Gapon "first section is too short", path); 1910112b52bSAndriy Gapon goto done; 1920112b52bSAndriy Gapon } 1930112b52bSAndriy Gapon equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data; 1940112b52bSAndriy Gapon fw_data += section_header->size; 1950112b52bSAndriy Gapon fw_size -= section_header->size; 1960112b52bSAndriy Gapon 1970112b52bSAndriy Gapon equiv_id = 0; 1980112b52bSAndriy Gapon for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) { 1990112b52bSAndriy Gapon if (signature == equiv_cpu_table[i].installed_cpu) { 2000112b52bSAndriy Gapon equiv_id = equiv_cpu_table[i].equiv_cpu; 2016e9d170dSSean Bruno WARNX(3, "equiv_id: %x, signature %8x," 2026e9d170dSSean Bruno " equiv_cpu_table[%d] %8x", equiv_id, signature, 2036e9d170dSSean Bruno i, equiv_cpu_table[i].installed_cpu); 2040112b52bSAndriy Gapon break; 2050112b52bSAndriy Gapon } 2060112b52bSAndriy Gapon } 2070112b52bSAndriy Gapon if (equiv_id == 0) { 2080112b52bSAndriy Gapon WARNX(2, "CPU is not found in the equivalence table"); 2090112b52bSAndriy Gapon goto done; 2100112b52bSAndriy Gapon } 2110112b52bSAndriy Gapon 2120112b52bSAndriy Gapon selected_fw = NULL; 2130112b52bSAndriy Gapon selected_size = 0; 2140112b52bSAndriy Gapon while (fw_size >= sizeof(*section_header)) { 2150112b52bSAndriy Gapon section_header = (const section_header_t *)fw_data; 2160112b52bSAndriy Gapon fw_data += sizeof(*section_header); 2170112b52bSAndriy Gapon fw_size -= sizeof(*section_header); 2180112b52bSAndriy Gapon if (section_header->type != AMD_10H_uCODE_TYPE) { 2190112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2200112b52bSAndriy Gapon "section has incorret type", path); 2210112b52bSAndriy Gapon goto done; 2220112b52bSAndriy Gapon } 2230112b52bSAndriy Gapon if (section_header->size > fw_size) { 2240112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2250112b52bSAndriy Gapon "file is truncated", path); 2260112b52bSAndriy Gapon goto done; 2270112b52bSAndriy Gapon } 2280112b52bSAndriy Gapon if (section_header->size < sizeof(*fw_header)) { 2290112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2300112b52bSAndriy Gapon "section is too short", path); 2310112b52bSAndriy Gapon goto done; 2320112b52bSAndriy Gapon } 2330112b52bSAndriy Gapon fw_header = (const amd_10h_fw_header_t *)fw_data; 2340112b52bSAndriy Gapon fw_data += section_header->size; 2350112b52bSAndriy Gapon fw_size -= section_header->size; 2360112b52bSAndriy Gapon 2376e9d170dSSean Bruno if (fw_header->processor_rev_id != equiv_id) { 23826dfa867SEd Maste WARNX(1, "firmware processor_rev_id %x, equiv_id %x", 2396e9d170dSSean Bruno fw_header->processor_rev_id, equiv_id); 2400112b52bSAndriy Gapon continue; /* different cpu */ 2416e9d170dSSean Bruno } 2426e9d170dSSean Bruno if (fw_header->patch_id <= revision) { 2436e9d170dSSean Bruno WARNX(1, "patch_id %x, revision %x", 2446e9d170dSSean Bruno fw_header->patch_id, revision); 2450112b52bSAndriy Gapon continue; /* not newer revision */ 2466e9d170dSSean Bruno } 2470112b52bSAndriy Gapon if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) { 2480112b52bSAndriy Gapon WARNX(2, "Chipset-specific microcode is not supported"); 2490112b52bSAndriy Gapon } 2500112b52bSAndriy Gapon 2510112b52bSAndriy Gapon WARNX(3, "selecting revision: %x", fw_header->patch_id); 2520112b52bSAndriy Gapon revision = fw_header->patch_id; 2530112b52bSAndriy Gapon selected_fw = fw_header; 2540112b52bSAndriy Gapon selected_size = section_header->size; 2550112b52bSAndriy Gapon } 2560112b52bSAndriy Gapon 2570112b52bSAndriy Gapon if (fw_size != 0) { 2580112b52bSAndriy Gapon WARNX(2, "%s is not a valid amd firmware: " 2590112b52bSAndriy Gapon "file is truncated", path); 2600112b52bSAndriy Gapon goto done; 2610112b52bSAndriy Gapon } 2620112b52bSAndriy Gapon 2630112b52bSAndriy Gapon if (selected_fw != NULL) { 2640112b52bSAndriy Gapon WARNX(1, "selected ucode size is %zu", selected_size); 2650112b52bSAndriy Gapon fprintf(stderr, "%s: updating cpu %s to revision %#x... ", 2660112b52bSAndriy Gapon path, dev, revision); 2670112b52bSAndriy Gapon 2680112b52bSAndriy Gapon args.data = __DECONST(void *, selected_fw); 2690112b52bSAndriy Gapon args.size = selected_size; 2700112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_UPDATE, &args); 2710112b52bSAndriy Gapon if (error < 0) { 2720112b52bSAndriy Gapon fprintf(stderr, "failed.\n"); 2730112b52bSAndriy Gapon warn("ioctl()"); 2740112b52bSAndriy Gapon goto done; 2750112b52bSAndriy Gapon } 2760112b52bSAndriy Gapon fprintf(stderr, "done.\n"); 2770112b52bSAndriy Gapon } 2780112b52bSAndriy Gapon 279a18e40aaSMark Johnston msrargs.msr = MSR_BIOS_SIGN; 2800112b52bSAndriy Gapon error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 2810112b52bSAndriy Gapon if (error < 0) { 2820112b52bSAndriy Gapon WARN(0, "ioctl(%s)", dev); 2830112b52bSAndriy Gapon goto done; 2840112b52bSAndriy Gapon } 2850112b52bSAndriy Gapon new_rev = (uint32_t)msrargs.data; 2860112b52bSAndriy Gapon if (new_rev != revision) 2870112b52bSAndriy Gapon WARNX(0, "revision after update %#x", new_rev); 2880112b52bSAndriy Gapon 2890112b52bSAndriy Gapon done: 2900112b52bSAndriy Gapon return; 2910112b52bSAndriy Gapon } 292