amd10h.c revision 308218
1224135Sdim/*- 2224135Sdim * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>. 3224135Sdim * All rights reserved. 4224135Sdim * 5224135Sdim * Redistribution and use in source and binary forms, with or without 6224135Sdim * modification, are permitted provided that the following conditions 7224135Sdim * are met: 8224135Sdim * 1. Redistributions of source code must retain the above copyright 9224135Sdim * notice, this list of conditions and the following disclaimer. 10224135Sdim * 2. Redistributions in binary form must reproduce the above copyright 11224135Sdim * notice, this list of conditions and the following disclaimer in the 12224135Sdim * documentation and/or other materials provided with the distribution. 13224135Sdim * 14243830Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15224135Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16239462Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17243830Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18224135Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19224135Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20224135Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21224135Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22224135Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23224135Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24224135Sdim */ 25224135Sdim 26224135Sdim#include <sys/cdefs.h> 27224135Sdim__FBSDID("$FreeBSD: head/usr.sbin/cpucontrol/amd10h.c 308218 2016-11-02 16:15:49Z avg $"); 28224135Sdim 29224135Sdim#include <sys/types.h> 30224135Sdim#include <sys/stat.h> 31224135Sdim#include <sys/mman.h> 32226633Sdim#include <sys/ioctl.h> 33226633Sdim#include <sys/ioccom.h> 34224135Sdim#include <sys/cpuctl.h> 35226633Sdim 36224135Sdim#include <machine/cpufunc.h> 37224135Sdim#include <machine/specialreg.h> 38226633Sdim 39226633Sdim#include <assert.h> 40226633Sdim#include <stdio.h> 41226633Sdim#include <stdlib.h> 42224135Sdim#include <string.h> 43224135Sdim#include <unistd.h> 44226633Sdim#include <fcntl.h> 45226633Sdim#include <err.h> 46226633Sdim 47226633Sdim#include "cpucontrol.h" 48224135Sdim#include "amd.h" 49226633Sdim 50224135Sdimint 51226633Sdimamd10h_probe(int fd) 52224135Sdim{ 53224135Sdim char vendor[13]; 54224135Sdim cpuctl_cpuid_args_t idargs; 55226633Sdim uint32_t family; 56224135Sdim uint32_t signature; 57224135Sdim int error; 58224135Sdim 59224135Sdim idargs.level = 0; 60224135Sdim error = ioctl(fd, CPUCTL_CPUID, &idargs); 61224135Sdim if (error < 0) { 62224135Sdim WARN(0, "ioctl()"); 63226633Sdim return (1); 64226633Sdim } 65224135Sdim ((uint32_t *)vendor)[0] = idargs.data[1]; 66224135Sdim ((uint32_t *)vendor)[1] = idargs.data[3]; 67226633Sdim ((uint32_t *)vendor)[2] = idargs.data[2]; 68224135Sdim vendor[12] = '\0'; 69226633Sdim if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0) 70226633Sdim return (1); 71226633Sdim 72224135Sdim idargs.level = 1; 73224135Sdim error = ioctl(fd, CPUCTL_CPUID, &idargs); 74224135Sdim if (error < 0) { 75226633Sdim WARN(0, "ioctl()"); 76224135Sdim return (1); 77226633Sdim } 78224135Sdim signature = idargs.data[0]; 79224135Sdim family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff); 80224135Sdim if (family < 0x10) 81224135Sdim return (1); 82224135Sdim return (0); 83224135Sdim} 84224135Sdim 85224135Sdim/* 86224135Sdim * NB: the format of microcode update files is not documented by AMD. 87224135Sdim * It has been reverse engineered from studying Coreboot, illumos and Linux 88224135Sdim * source code. 89224135Sdim */ 90224135Sdimvoid 91224135Sdimamd10h_update(const char *dev, const char *path) 92224135Sdim{ 93224135Sdim struct stat st; 94224135Sdim cpuctl_cpuid_args_t idargs; 95224135Sdim cpuctl_msr_args_t msrargs; 96224135Sdim cpuctl_update_args_t args; 97224135Sdim const amd_10h_fw_header_t *fw_header; 98226633Sdim const amd_10h_fw_header_t *selected_fw; 99224135Sdim const equiv_cpu_entry_t *equiv_cpu_table; 100234353Sdim const section_header_t *section_header; 101234353Sdim const container_header_t *container_header; 102226633Sdim const uint8_t *fw_data; 103224135Sdim uint8_t *fw_image; 104224135Sdim size_t fw_size; 105226633Sdim size_t selected_size; 106226633Sdim uint32_t revision; 107224135Sdim uint32_t new_rev; 108224135Sdim uint32_t signature; 109224135Sdim uint16_t equiv_id; 110224135Sdim int fd, devfd; 111226633Sdim unsigned int i; 112224135Sdim int error; 113224135Sdim 114224135Sdim assert(path); 115224135Sdim assert(dev); 116224135Sdim 117224135Sdim fd = -1; 118224135Sdim fw_image = MAP_FAILED; 119224135Sdim devfd = open(dev, O_RDWR); 120224135Sdim if (devfd < 0) { 121224135Sdim WARN(0, "could not open %s for writing", dev); 122224135Sdim return; 123224135Sdim } 124224135Sdim idargs.level = 1; 125224135Sdim error = ioctl(devfd, CPUCTL_CPUID, &idargs); 126224135Sdim if (error < 0) { 127224135Sdim WARN(0, "ioctl()"); 128224135Sdim goto done; 129224135Sdim } 130224135Sdim signature = idargs.data[0]; 131224135Sdim 132224135Sdim msrargs.msr = 0x0000008b; 133224135Sdim error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 134224135Sdim if (error < 0) { 135224135Sdim WARN(0, "ioctl(%s)", dev); 136224135Sdim goto done; 137224135Sdim } 138224135Sdim revision = (uint32_t)msrargs.data; 139224135Sdim 140224135Sdim WARNX(1, "found cpu family %#x model %#x " 141224135Sdim "stepping %#x extfamily %#x extmodel %#x.", 142224135Sdim (signature >> 8) & 0x0f, (signature >> 4) & 0x0f, 143224135Sdim (signature >> 0) & 0x0f, (signature >> 20) & 0xff, 144224135Sdim (signature >> 16) & 0x0f); 145234353Sdim WARNX(1, "microcode revision %#x", revision); 146234353Sdim 147224135Sdim /* 148224135Sdim * Open the firmware file. 149249423Sdim */ 150224135Sdim fd = open(path, O_RDONLY, 0); 151249423Sdim if (fd < 0) { 152224135Sdim WARN(0, "open(%s)", path); 153234353Sdim goto done; 154234353Sdim } 155249423Sdim error = fstat(fd, &st); 156224135Sdim if (error != 0) { 157234353Sdim WARN(0, "fstat(%s)", path); 158249423Sdim goto done; 159234353Sdim } 160234353Sdim if (st.st_size < 0 || (size_t)st.st_size < 161249423Sdim (sizeof(*container_header) + sizeof(*section_header))) { 162249423Sdim WARNX(2, "file too short: %s", path); 163234353Sdim goto done; 164234353Sdim } 165234353Sdim fw_size = st.st_size; 166234353Sdim 167234353Sdim /* 168239462Sdim * mmap the whole image. 169239462Sdim */ 170224135Sdim fw_image = (uint8_t *)mmap(NULL, st.st_size, PROT_READ, 171224135Sdim MAP_PRIVATE, fd, 0); 172226633Sdim if (fw_image == MAP_FAILED) { 173224135Sdim WARN(0, "mmap(%s)", path); 174224135Sdim goto done; 175224135Sdim } 176224135Sdim 177224135Sdim fw_data = fw_image; 178224135Sdim container_header = (const container_header_t *)fw_data; 179224135Sdim if (container_header->magic != AMD_10H_MAGIC) { 180224135Sdim WARNX(2, "%s is not a valid amd firmware: bad magic", path); 181 goto done; 182 } 183 fw_data += sizeof(*container_header); 184 fw_size -= sizeof(*container_header); 185 186 section_header = (const section_header_t *)fw_data; 187 if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) { 188 WARNX(2, "%s is not a valid amd firmware: " 189 "first section is not CPU equivalence table", path); 190 goto done; 191 } 192 if (section_header->size == 0) { 193 WARNX(2, "%s is not a valid amd firmware: " 194 "first section is empty", path); 195 goto done; 196 } 197 fw_data += sizeof(*section_header); 198 fw_size -= sizeof(*section_header); 199 200 if (section_header->size > fw_size) { 201 WARNX(2, "%s is not a valid amd firmware: " 202 "file is truncated", path); 203 goto done; 204 } 205 if (section_header->size < sizeof(*equiv_cpu_table)) { 206 WARNX(2, "%s is not a valid amd firmware: " 207 "first section is too short", path); 208 goto done; 209 } 210 equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data; 211 fw_data += section_header->size; 212 fw_size -= section_header->size; 213 214 equiv_id = 0; 215 for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) { 216 if (signature == equiv_cpu_table[i].installed_cpu) { 217 equiv_id = equiv_cpu_table[i].equiv_cpu; 218 WARNX(3, "equiv_id: %x", equiv_id); 219 break; 220 } 221 } 222 if (equiv_id == 0) { 223 WARNX(2, "CPU is not found in the equivalence table"); 224 goto done; 225 } 226 227 selected_fw = NULL; 228 selected_size = 0; 229 while (fw_size >= sizeof(*section_header)) { 230 section_header = (const section_header_t *)fw_data; 231 fw_data += sizeof(*section_header); 232 fw_size -= sizeof(*section_header); 233 if (section_header->type != AMD_10H_uCODE_TYPE) { 234 WARNX(2, "%s is not a valid amd firmware: " 235 "section has incorret type", path); 236 goto done; 237 } 238 if (section_header->size > fw_size) { 239 WARNX(2, "%s is not a valid amd firmware: " 240 "file is truncated", path); 241 goto done; 242 } 243 if (section_header->size < sizeof(*fw_header)) { 244 WARNX(2, "%s is not a valid amd firmware: " 245 "section is too short", path); 246 goto done; 247 } 248 fw_header = (const amd_10h_fw_header_t *)fw_data; 249 fw_data += section_header->size; 250 fw_size -= section_header->size; 251 252 if (fw_header->processor_rev_id != equiv_id) 253 continue; /* different cpu */ 254 if (fw_header->patch_id <= revision) 255 continue; /* not newer revision */ 256 if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) { 257 WARNX(2, "Chipset-specific microcode is not supported"); 258 } 259 260 WARNX(3, "selecting revision: %x", fw_header->patch_id); 261 revision = fw_header->patch_id; 262 selected_fw = fw_header; 263 selected_size = section_header->size; 264 } 265 266 if (fw_size != 0) { 267 WARNX(2, "%s is not a valid amd firmware: " 268 "file is truncated", path); 269 goto done; 270 } 271 272 if (selected_fw != NULL) { 273 WARNX(1, "selected ucode size is %zu", selected_size); 274 fprintf(stderr, "%s: updating cpu %s to revision %#x... ", 275 path, dev, revision); 276 277 args.data = __DECONST(void *, selected_fw); 278 args.size = selected_size; 279 error = ioctl(devfd, CPUCTL_UPDATE, &args); 280 if (error < 0) { 281 fprintf(stderr, "failed.\n"); 282 warn("ioctl()"); 283 goto done; 284 } 285 fprintf(stderr, "done.\n"); 286 } 287 288 msrargs.msr = 0x0000008b; 289 error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 290 if (error < 0) { 291 WARN(0, "ioctl(%s)", dev); 292 goto done; 293 } 294 new_rev = (uint32_t)msrargs.data; 295 if (new_rev != revision) 296 WARNX(0, "revision after update %#x", new_rev); 297 298done: 299 if (fd >= 0) 300 close(fd); 301 if (devfd >= 0) 302 close(devfd); 303 if (fw_image != MAP_FAILED) 304 if (munmap(fw_image, st.st_size) != 0) 305 warn("munmap(%s)", path); 306 return; 307} 308