1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (C) 2018 The FreeBSD Foundation. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <err.h> 31 #include <fcntl.h> 32 #include <stdbool.h> 33 #include <stdint.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 static const size_t bufsize = 65536; 40 41 /* SDM vol 3 9.11.1 Intel microcode header. */ 42 struct microcode_update_header { 43 uint32_t header_version; 44 uint32_t update_revision; 45 uint32_t date; /* BCD mmddyyyy */ 46 uint32_t processor_signature; 47 uint32_t checksum; /* Over update data and header */ 48 uint32_t loader_revision; 49 uint32_t processor_flags; 50 uint32_t data_size; 51 uint32_t total_size; 52 uint32_t reserved[3]; 53 }; 54 55 /* 56 * SDM vol 2A CPUID EAX = 01h Returns Model, Family, Stepping Information. 57 * Caller must free the returned string. 58 */ 59 60 static char * 61 format_signature(uint32_t signature) 62 { 63 char *buf; 64 unsigned family, model, stepping; 65 66 family = (signature & 0xf00) >> 8; 67 model = (signature & 0xf0) >> 4; 68 stepping = signature & 0xf; 69 if (family == 0x06 || family == 0x0f) 70 model += (signature & 0xf0000) >> 12; 71 if (family == 0x0f) 72 family += (signature & 0xff00000) >> 20; 73 asprintf(&buf, "%02x-%02x-%02x", family, model, stepping); 74 if (buf == NULL) 75 err(1, "asprintf"); 76 return (buf); 77 } 78 79 static void 80 dump_header(const struct microcode_update_header *hdr) 81 { 82 char *sig_str; 83 int i; 84 bool platformid_printed; 85 86 sig_str = format_signature(hdr->processor_signature); 87 printf("header version\t0x%x\n", hdr->header_version); 88 printf("revision\t0x%x\n", hdr->update_revision); 89 printf("date\t\t0x%x\t%04x-%02x-%02x\n", hdr->date, 90 hdr->date & 0xffff, (hdr->date & 0xff000000) >> 24, 91 (hdr->date & 0xff0000) >> 16); 92 printf("signature\t0x%x\t\t%s\n", hdr->processor_signature, sig_str); 93 printf("checksum\t0x%x\n", hdr->checksum); 94 printf("loader revision\t0x%x\n", hdr->loader_revision); 95 printf("processor flags\t0x%x", hdr->processor_flags); 96 platformid_printed = false; 97 for (i = 0; i < 8; i++) { 98 if (hdr->processor_flags & 1 << i) { 99 printf("%s%d", platformid_printed ? ", " : "\t\t", i); 100 platformid_printed = true; 101 } 102 } 103 printf("\n"); 104 printf("datasize\t0x%x\t\t0x%x\n", hdr->data_size, 105 hdr->data_size != 0 ? hdr->data_size : 2000); 106 printf("size\t\t0x%x\t\t0x%x\n", hdr->total_size, 107 hdr->total_size != 0 ? hdr->total_size : 2048); 108 free(sig_str); 109 } 110 111 static void 112 usage(void) 113 { 114 115 printf("ucode-split [-nv] microcode_file\n"); 116 exit(1); 117 } 118 119 int 120 main(int argc, char *argv[]) 121 { 122 struct microcode_update_header hdr; 123 char *buf, *output_file, *sig_str; 124 size_t len, resid; 125 ssize_t rv; 126 int c, ifd, ofd; 127 bool nflag, vflag; 128 129 nflag = vflag = false; 130 while ((c = getopt(argc, argv, "nv")) != -1) { 131 switch (c) { 132 case 'n': 133 nflag = true; 134 break; 135 case 'v': 136 vflag = true; 137 break; 138 default: 139 usage(); 140 } 141 } 142 argc -= optind; 143 argv += optind; 144 145 if (argc != 1) 146 usage(); 147 148 ifd = open(argv[0], O_RDONLY); 149 if (ifd < 0) 150 err(1, "open"); 151 152 buf = malloc(bufsize); 153 if (buf == NULL) 154 err(1, "malloc"); 155 156 for (;;) { 157 /* Read header. */ 158 rv = read(ifd, &hdr, sizeof(hdr)); 159 if (rv < 0) { 160 err(1, "read"); 161 } else if (rv == 0) { 162 break; 163 } else if (rv < (ssize_t)sizeof(hdr)) { 164 errx(1, "invalid microcode header"); 165 } 166 if (hdr.header_version != 1) 167 errx(1, "invalid header version"); 168 169 if (vflag) 170 dump_header(&hdr); 171 172 resid = (hdr.total_size != 0 ? hdr.total_size : 2048) - 173 sizeof(hdr); 174 if (resid > 1 << 24) /* Arbitrary chosen maximum size. */ 175 errx(1, "header total_size too large"); 176 177 if (nflag) { 178 if (lseek(ifd, resid, SEEK_CUR) == -1) 179 err(1, "lseek"); 180 printf("\n"); 181 } else { 182 sig_str = format_signature(hdr.processor_signature); 183 asprintf(&output_file, "%s.%02x", sig_str, 184 hdr.processor_flags & 0xff); 185 free(sig_str); 186 if (output_file == NULL) 187 err(1, "asprintf"); 188 ofd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 189 0600); 190 if (ofd < 0) 191 err(1, "open"); 192 193 /* Write header. */ 194 rv = write(ofd, &hdr, sizeof(hdr)); 195 if (rv < (ssize_t)sizeof(hdr)) 196 err(1, "write"); 197 198 /* Copy data. */ 199 while (resid > 0) { 200 len = resid < bufsize ? resid : bufsize; 201 rv = read(ifd, buf, len); 202 if (rv < 0) 203 err(1, "read"); 204 else if (rv < (ssize_t)len) 205 errx(1, "truncated microcode data"); 206 if (write(ofd, buf, len) < (ssize_t)len) 207 err(1, "write"); 208 resid -= len; 209 } 210 if (vflag) 211 printf("written to %s\n\n", output_file); 212 close(ofd); 213 free(output_file); 214 } 215 } 216 } 217