1308218Savg/*- 2308218Savg * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>. 3308218Savg * All rights reserved. 4308218Savg * 5308218Savg * Redistribution and use in source and binary forms, with or without 6308218Savg * modification, are permitted provided that the following conditions 7308218Savg * are met: 8308218Savg * 1. Redistributions of source code must retain the above copyright 9308218Savg * notice, this list of conditions and the following disclaimer. 10308218Savg * 2. Redistributions in binary form must reproduce the above copyright 11308218Savg * notice, this list of conditions and the following disclaimer in the 12308218Savg * documentation and/or other materials provided with the distribution. 13308218Savg * 14308218Savg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15308218Savg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16308218Savg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17308218Savg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18308218Savg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19308218Savg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20308218Savg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21308218Savg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22308218Savg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23308218Savg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24308218Savg */ 25308218Savg 26308218Savg#include <sys/cdefs.h> 27308218Savg__FBSDID("$FreeBSD: stable/10/usr.sbin/cpucontrol/amd10h.c 308761 2016-11-17 15:17:01Z avg $"); 28308218Savg 29308218Savg#include <sys/types.h> 30308218Savg#include <sys/stat.h> 31308218Savg#include <sys/mman.h> 32308218Savg#include <sys/ioctl.h> 33308218Savg#include <sys/ioccom.h> 34308218Savg#include <sys/cpuctl.h> 35308218Savg 36308218Savg#include <machine/cpufunc.h> 37308218Savg#include <machine/specialreg.h> 38308218Savg 39308218Savg#include <assert.h> 40308218Savg#include <stdio.h> 41308218Savg#include <stdlib.h> 42308218Savg#include <string.h> 43308218Savg#include <unistd.h> 44308218Savg#include <fcntl.h> 45308218Savg#include <err.h> 46308218Savg 47308218Savg#include "cpucontrol.h" 48308218Savg#include "amd.h" 49308218Savg 50308218Savgint 51308218Savgamd10h_probe(int fd) 52308218Savg{ 53308218Savg char vendor[13]; 54308218Savg cpuctl_cpuid_args_t idargs; 55308218Savg uint32_t family; 56308218Savg uint32_t signature; 57308218Savg int error; 58308218Savg 59308218Savg idargs.level = 0; 60308218Savg error = ioctl(fd, CPUCTL_CPUID, &idargs); 61308218Savg if (error < 0) { 62308218Savg WARN(0, "ioctl()"); 63308218Savg return (1); 64308218Savg } 65308218Savg ((uint32_t *)vendor)[0] = idargs.data[1]; 66308218Savg ((uint32_t *)vendor)[1] = idargs.data[3]; 67308218Savg ((uint32_t *)vendor)[2] = idargs.data[2]; 68308218Savg vendor[12] = '\0'; 69308218Savg if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0) 70308218Savg return (1); 71308218Savg 72308218Savg idargs.level = 1; 73308218Savg error = ioctl(fd, CPUCTL_CPUID, &idargs); 74308218Savg if (error < 0) { 75308218Savg WARN(0, "ioctl()"); 76308218Savg return (1); 77308218Savg } 78308218Savg signature = idargs.data[0]; 79308218Savg family = ((signature >> 8) & 0x0f) + ((signature >> 20) & 0xff); 80308218Savg if (family < 0x10) 81308218Savg return (1); 82308218Savg return (0); 83308218Savg} 84308218Savg 85308218Savg/* 86308218Savg * NB: the format of microcode update files is not documented by AMD. 87308218Savg * It has been reverse engineered from studying Coreboot, illumos and Linux 88308218Savg * source code. 89308218Savg */ 90308218Savgvoid 91308218Savgamd10h_update(const char *dev, const char *path) 92308218Savg{ 93308218Savg struct stat st; 94308218Savg cpuctl_cpuid_args_t idargs; 95308218Savg cpuctl_msr_args_t msrargs; 96308218Savg cpuctl_update_args_t args; 97308218Savg const amd_10h_fw_header_t *fw_header; 98308218Savg const amd_10h_fw_header_t *selected_fw; 99308218Savg const equiv_cpu_entry_t *equiv_cpu_table; 100308218Savg const section_header_t *section_header; 101308218Savg const container_header_t *container_header; 102308218Savg const uint8_t *fw_data; 103308218Savg uint8_t *fw_image; 104308218Savg size_t fw_size; 105308218Savg size_t selected_size; 106308218Savg uint32_t revision; 107308218Savg uint32_t new_rev; 108308218Savg uint32_t signature; 109308218Savg uint16_t equiv_id; 110308218Savg int fd, devfd; 111308218Savg unsigned int i; 112308218Savg int error; 113308218Savg 114308218Savg assert(path); 115308218Savg assert(dev); 116308218Savg 117308218Savg fd = -1; 118308218Savg fw_image = MAP_FAILED; 119308218Savg devfd = open(dev, O_RDWR); 120308218Savg if (devfd < 0) { 121308218Savg WARN(0, "could not open %s for writing", dev); 122308218Savg return; 123308218Savg } 124308218Savg idargs.level = 1; 125308218Savg error = ioctl(devfd, CPUCTL_CPUID, &idargs); 126308218Savg if (error < 0) { 127308218Savg WARN(0, "ioctl()"); 128308218Savg goto done; 129308218Savg } 130308218Savg signature = idargs.data[0]; 131308218Savg 132308218Savg msrargs.msr = 0x0000008b; 133308218Savg error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 134308218Savg if (error < 0) { 135308218Savg WARN(0, "ioctl(%s)", dev); 136308218Savg goto done; 137308218Savg } 138308218Savg revision = (uint32_t)msrargs.data; 139308218Savg 140308218Savg WARNX(1, "found cpu family %#x model %#x " 141308218Savg "stepping %#x extfamily %#x extmodel %#x.", 142308218Savg (signature >> 8) & 0x0f, (signature >> 4) & 0x0f, 143308218Savg (signature >> 0) & 0x0f, (signature >> 20) & 0xff, 144308218Savg (signature >> 16) & 0x0f); 145308218Savg WARNX(1, "microcode revision %#x", revision); 146308218Savg 147308218Savg /* 148308218Savg * Open the firmware file. 149308218Savg */ 150308218Savg fd = open(path, O_RDONLY, 0); 151308218Savg if (fd < 0) { 152308218Savg WARN(0, "open(%s)", path); 153308218Savg goto done; 154308218Savg } 155308218Savg error = fstat(fd, &st); 156308218Savg if (error != 0) { 157308218Savg WARN(0, "fstat(%s)", path); 158308218Savg goto done; 159308218Savg } 160308218Savg if (st.st_size < 0 || (size_t)st.st_size < 161308218Savg (sizeof(*container_header) + sizeof(*section_header))) { 162308218Savg WARNX(2, "file too short: %s", path); 163308218Savg goto done; 164308218Savg } 165308218Savg fw_size = st.st_size; 166308218Savg 167308218Savg /* 168308218Savg * mmap the whole image. 169308218Savg */ 170308218Savg fw_image = (uint8_t *)mmap(NULL, st.st_size, PROT_READ, 171308218Savg MAP_PRIVATE, fd, 0); 172308218Savg if (fw_image == MAP_FAILED) { 173308218Savg WARN(0, "mmap(%s)", path); 174308218Savg goto done; 175308218Savg } 176308218Savg 177308218Savg fw_data = fw_image; 178308218Savg container_header = (const container_header_t *)fw_data; 179308218Savg if (container_header->magic != AMD_10H_MAGIC) { 180308218Savg WARNX(2, "%s is not a valid amd firmware: bad magic", path); 181308218Savg goto done; 182308218Savg } 183308218Savg fw_data += sizeof(*container_header); 184308218Savg fw_size -= sizeof(*container_header); 185308218Savg 186308218Savg section_header = (const section_header_t *)fw_data; 187308218Savg if (section_header->type != AMD_10H_EQUIV_TABLE_TYPE) { 188308218Savg WARNX(2, "%s is not a valid amd firmware: " 189308218Savg "first section is not CPU equivalence table", path); 190308218Savg goto done; 191308218Savg } 192308218Savg if (section_header->size == 0) { 193308218Savg WARNX(2, "%s is not a valid amd firmware: " 194308218Savg "first section is empty", path); 195308218Savg goto done; 196308218Savg } 197308218Savg fw_data += sizeof(*section_header); 198308218Savg fw_size -= sizeof(*section_header); 199308218Savg 200308218Savg if (section_header->size > fw_size) { 201308218Savg WARNX(2, "%s is not a valid amd firmware: " 202308218Savg "file is truncated", path); 203308218Savg goto done; 204308218Savg } 205308218Savg if (section_header->size < sizeof(*equiv_cpu_table)) { 206308218Savg WARNX(2, "%s is not a valid amd firmware: " 207308218Savg "first section is too short", path); 208308218Savg goto done; 209308218Savg } 210308218Savg equiv_cpu_table = (const equiv_cpu_entry_t *)fw_data; 211308218Savg fw_data += section_header->size; 212308218Savg fw_size -= section_header->size; 213308218Savg 214308218Savg equiv_id = 0; 215308218Savg for (i = 0; equiv_cpu_table[i].installed_cpu != 0; i++) { 216308218Savg if (signature == equiv_cpu_table[i].installed_cpu) { 217308218Savg equiv_id = equiv_cpu_table[i].equiv_cpu; 218308218Savg WARNX(3, "equiv_id: %x", equiv_id); 219308218Savg break; 220308218Savg } 221308218Savg } 222308218Savg if (equiv_id == 0) { 223308218Savg WARNX(2, "CPU is not found in the equivalence table"); 224308218Savg goto done; 225308218Savg } 226308218Savg 227308218Savg selected_fw = NULL; 228308218Savg selected_size = 0; 229308218Savg while (fw_size >= sizeof(*section_header)) { 230308218Savg section_header = (const section_header_t *)fw_data; 231308218Savg fw_data += sizeof(*section_header); 232308218Savg fw_size -= sizeof(*section_header); 233308218Savg if (section_header->type != AMD_10H_uCODE_TYPE) { 234308218Savg WARNX(2, "%s is not a valid amd firmware: " 235308218Savg "section has incorret type", path); 236308218Savg goto done; 237308218Savg } 238308218Savg if (section_header->size > fw_size) { 239308218Savg WARNX(2, "%s is not a valid amd firmware: " 240308218Savg "file is truncated", path); 241308218Savg goto done; 242308218Savg } 243308218Savg if (section_header->size < sizeof(*fw_header)) { 244308218Savg WARNX(2, "%s is not a valid amd firmware: " 245308218Savg "section is too short", path); 246308218Savg goto done; 247308218Savg } 248308218Savg fw_header = (const amd_10h_fw_header_t *)fw_data; 249308218Savg fw_data += section_header->size; 250308218Savg fw_size -= section_header->size; 251308218Savg 252308218Savg if (fw_header->processor_rev_id != equiv_id) 253308218Savg continue; /* different cpu */ 254308218Savg if (fw_header->patch_id <= revision) 255308218Savg continue; /* not newer revision */ 256308218Savg if (fw_header->nb_dev_id != 0 || fw_header->sb_dev_id != 0) { 257308218Savg WARNX(2, "Chipset-specific microcode is not supported"); 258308218Savg } 259308218Savg 260308218Savg WARNX(3, "selecting revision: %x", fw_header->patch_id); 261308218Savg revision = fw_header->patch_id; 262308218Savg selected_fw = fw_header; 263308218Savg selected_size = section_header->size; 264308218Savg } 265308218Savg 266308218Savg if (fw_size != 0) { 267308218Savg WARNX(2, "%s is not a valid amd firmware: " 268308218Savg "file is truncated", path); 269308218Savg goto done; 270308218Savg } 271308218Savg 272308218Savg if (selected_fw != NULL) { 273308218Savg WARNX(1, "selected ucode size is %zu", selected_size); 274308218Savg fprintf(stderr, "%s: updating cpu %s to revision %#x... ", 275308218Savg path, dev, revision); 276308218Savg 277308218Savg args.data = __DECONST(void *, selected_fw); 278308218Savg args.size = selected_size; 279308218Savg error = ioctl(devfd, CPUCTL_UPDATE, &args); 280308218Savg if (error < 0) { 281308218Savg fprintf(stderr, "failed.\n"); 282308218Savg warn("ioctl()"); 283308218Savg goto done; 284308218Savg } 285308218Savg fprintf(stderr, "done.\n"); 286308218Savg } 287308218Savg 288308218Savg msrargs.msr = 0x0000008b; 289308218Savg error = ioctl(devfd, CPUCTL_RDMSR, &msrargs); 290308218Savg if (error < 0) { 291308218Savg WARN(0, "ioctl(%s)", dev); 292308218Savg goto done; 293308218Savg } 294308218Savg new_rev = (uint32_t)msrargs.data; 295308218Savg if (new_rev != revision) 296308218Savg WARNX(0, "revision after update %#x", new_rev); 297308218Savg 298308218Savgdone: 299308218Savg if (fd >= 0) 300308218Savg close(fd); 301308218Savg if (devfd >= 0) 302308218Savg close(devfd); 303308218Savg if (fw_image != MAP_FAILED) 304308218Savg if (munmap(fw_image, st.st_size) != 0) 305308218Savg warn("munmap(%s)", path); 306308218Savg return; 307308218Savg} 308