1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
27 */
28
29 #include <sys/types.h>
30 #include <sys/ucode.h>
31 #ifdef _KERNEL
32 #include <sys/systm.h>
33 #else
34 #include <strings.h>
35 #endif
36
37 /*
38 * Refer to
39 * Intel 64 and IA-32 Architectures Software Developers's Manual
40 * Chapter 9.11 Microcode Update Facilities [1]
41 * for details.
42 */
43
44 /*
45 * Validates the microcode header.
46 * Returns EM_OK on success, EM_HEADER on failure.
47 */
48 ucode_errno_t
ucode_header_validate_intel(ucode_header_intel_t * uhp)49 ucode_header_validate_intel(ucode_header_intel_t *uhp)
50 {
51 uint32_t header_size, body_size, total_size;
52
53 if (uhp == NULL)
54 return (EM_HEADER);
55
56 /*
57 * The only header version number supported is 1.
58 */
59 if (uhp->uh_header_ver != 0x1)
60 return (EM_HEADER);
61
62 header_size = UCODE_HEADER_SIZE_INTEL;
63 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
64 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
65
66 /*
67 * The body size field of the microcode code header specifies the size
68 * of the encrypted data section, its value must be a multiple of the
69 * size of DWORD. The total size field must be in multiples of 1K
70 * bytes.
71 */
72 if ((body_size % sizeof (int)) ||
73 (total_size < (header_size + body_size)) ||
74 (total_size % UCODE_KB(1)))
75
76 return (EM_HEADER);
77
78 /*
79 * Sanity check to avoid reading bogus files
80 */
81 if (total_size < UCODE_MIN_SIZE || total_size > UCODE_MAX_SIZE)
82 return (EM_HEADER);
83
84 /*
85 * If there is extended signature table, total_size is the sum of
86 * header_size
87 * body_size
88 * sizeof (struct ucode_ext_table)
89 * n * sizeof (struct ucode_ext_sig)
90 * where n is indicated by uet_count in struct ucode_ext_table.
91 */
92 if (total_size > (header_size + body_size)) {
93 if ((total_size - body_size - header_size -
94 UCODE_EXT_TABLE_SIZE_INTEL) % UCODE_EXT_SIG_SIZE_INTEL) {
95
96 return (EM_HEADER);
97 }
98 }
99
100 return (EM_OK);
101 }
102
103 /*
104 * Returns checksum.
105 */
106 uint32_t
ucode_checksum_intel(uint32_t sum,uint32_t size,uint8_t * code)107 ucode_checksum_intel(uint32_t sum, uint32_t size, uint8_t *code)
108 {
109 int i;
110 uint32_t *lcode = (uint32_t *)(intptr_t)code;
111
112 i = size >> 2;
113 while (i--)
114 sum += lcode[i];
115
116 return (sum);
117 }
118
119 uint32_t
ucode_checksum_intel_extsig(ucode_header_intel_t * uhp,ucode_ext_sig_intel_t * uesp)120 ucode_checksum_intel_extsig(ucode_header_intel_t *uhp,
121 ucode_ext_sig_intel_t *uesp)
122 {
123 /*
124 * From [1], table 9-7, the checksum field contained in the extended
125 * patch signature is the checksum across the entire update which would
126 * result if the primary processor signature and processor flags were
127 * replaced with the values from this entry.
128 *
129 * We can therefore just calculate the difference in the checksum
130 * between the old and new values and return that to the caller. If the
131 * difference is zero then the checksum for the patch is valid.
132 */
133 uint32_t diff;
134
135 diff = uesp->ues_signature +
136 uesp->ues_proc_flags + uesp->ues_checksum;
137 diff -= uhp->uh_signature + uhp->uh_proc_flags +
138 uhp->uh_checksum;
139
140 return (diff);
141 }
142
143
144 ucode_errno_t
ucode_validate_amd(uint8_t * ucodep,int size)145 ucode_validate_amd(uint8_t *ucodep, int size)
146 {
147 uint32_t *ptr = (uint32_t *)ucodep;
148 uint32_t count;
149
150 if (ucodep == NULL || size <= 0)
151 return (EM_INVALIDARG);
152
153 /* Magic Number: "AMD\0" */
154 size -= 4;
155 if (*ptr++ != 0x00414d44)
156 return (EM_FILEFORMAT);
157
158 /* equivalence table */
159 size -= 4;
160 if (*ptr++)
161 return (EM_FILEFORMAT);
162
163 size -= 4;
164 if (((count = *ptr++) > size) || (count % 16))
165 return (EM_FILEFORMAT);
166
167 ptr = (uint32_t *)(((uint8_t *)ptr) + count);
168 size -= count;
169
170 while (size > 8) {
171 /* microcode patch */
172 size -= 4;
173 if (*ptr++ != 1)
174 return (EM_FILEFORMAT);
175
176 size -= 4;
177 if (((count = *ptr++) > size))
178 return (EM_FILEFORMAT);
179
180 ptr = (uint32_t *)(((uint8_t *)ptr) + count);
181 size -= count;
182 }
183
184 if (size)
185 return (EM_FILEFORMAT);
186
187 return (EM_OK);
188 }
189
190 ucode_errno_t
ucode_validate_intel(uint8_t * ucodep,int size)191 ucode_validate_intel(uint8_t *ucodep, int size)
192 {
193 uint32_t header_size = UCODE_HEADER_SIZE_INTEL;
194 int remaining;
195
196 if (ucodep == NULL || size <= 0)
197 return (EM_INVALIDARG);
198
199 for (remaining = size; remaining > 0; ) {
200 uint32_t total_size, body_size, ext_size;
201 ucode_header_intel_t *uhp;
202 uint8_t *curbuf = &ucodep[size - remaining];
203 ucode_errno_t rc;
204
205 uhp = (ucode_header_intel_t *)curbuf;
206
207 if ((rc = ucode_header_validate_intel(uhp)) != EM_OK)
208 return (rc);
209
210 total_size = UCODE_TOTAL_SIZE_INTEL(uhp->uh_total_size);
211
212 if (ucode_checksum_intel(0, total_size, curbuf) != 0)
213 return (EM_CHECKSUM);
214
215 body_size = UCODE_BODY_SIZE_INTEL(uhp->uh_body_size);
216 ext_size = total_size - (header_size + body_size);
217
218 if (ext_size > 0) {
219 ucode_ext_table_intel_t *ext;
220 uint32_t i;
221
222 ext = (ucode_ext_table_intel_t *)
223 &curbuf[header_size + body_size];
224
225 if (ucode_checksum_intel(0, ext_size, (uint8_t *)ext))
226 return (EM_EXTCHECKSUM);
227
228 for (i = 0; i < ext->uet_count; i++) {
229 ucode_ext_sig_intel_t *sig =
230 &ext->uet_ext_sig[i];
231
232 if (ucode_checksum_intel_extsig(uhp, sig) != 0)
233 return (EM_SIGCHECKSUM);
234 }
235 }
236
237 remaining -= total_size;
238 }
239 return (EM_OK);
240 }
241