1 /*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
5 * Copyright (C) 2022 secunet Security Networks AG
6 * (Written by Thomas Heijligen <[email protected])
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19 /*
20 * This file implements x86 I/O port permission and access handling.
21 *
22 * The first part of the file defines the platform dependend implementations to
23 * use on each platform. For getting I/O permissions set IO_PORT_PERMISSION to
24 * one of:
25 * - USE_DUMMY
26 * - USE_SUN
27 * - USE_DEVICE
28 * - USE_IOPERM
29 * - USE_IOPL
30 * For the IN[B/W/L] and OUT[B/W/L] functions set IO_PORT_FUNCTION to one of:
31 * - USE_LIBC_TARGET_LAST
32 * - USE_LIBC_TARGET_FIRST
33 * - USE_DOS
34 * - USE_ASM
35 *
36 * The platform specific code for getting I/O permissions consists of two
37 * functions. `platform_get_io_perms()` is called to get
38 * permissions and `platform_release_io_perms()` is called for releasing those.
39 */
40
41 #include <errno.h>
42 #include <string.h>
43
44 #include "flash.h"
45 #include "hwaccess_x86_io.h"
46
47 /* IO_PORT_FUNCTION */
48 #define USE_LIBC_TARGET_LAST 1
49 #define USE_LIBC_TARGET_FIRST 2
50 #define USE_ASM 3
51 #define USE_DOS 4
52
53 /* IO_PORT_PERMISSION */
54 #define USE_IOPL 5
55 #define USE_DEVICE 6
56 #define USE_DUMMY 7
57 #define USE_SUN 8
58 #define USE_IOPERM 9
59
60 #if defined(__ANDROID__)
61 #include <sys/io.h>
62 #include <unistd.h>
63
64 #define IO_PORT_PERMISSION USE_IOPERM
65 #define IO_PORT_FUNCTION USE_ASM
66 #endif
67
68 #if defined(__linux__) && !defined(__ANDROID__)
69 #include <sys/io.h>
70 #include <unistd.h>
71
72 #define IO_PORT_PERMISSION USE_IOPL
73 #define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
74 #endif
75
76 #if defined(__FreeBSD_kernel) && defined(__GLIBC__)
77 #include <sys/io.h>
78 #include <sys/types.h>
79 #include <fcntl.h>
80 #include <unistd.h>
81
82 #define IO_PORT_PERMISSION USE_DEVICE
83 #define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
84 #endif
85
86 #if defined(__FreeBSD__) || defined(__DragonFly__)
87 #include <sys/types.h>
88 #include <machine/cpufunc.h>
89 #include <fcntl.h>
90 #include <unistd.h>
91
92 #define IO_PORT_PERMISSION USE_DEVICE
93 #define IO_PORT_FUNCTION USE_LIBC_TARGET_FIRST
94 #endif
95
96 #if defined(__NetBSD__)
97 #include <sys/types.h>
98 #include <machine/sysarch.h>
99
100 #if defined(__i386__)
101 #define iopl i386_iopl
102 #elif defined(__x86_64__)
103 #define iopl x86_64_iopl
104 #endif
105
106 #define IO_PORT_PERMISSION USE_IOPL
107 #define IO_PORT_FUNCTION USE_ASM
108 #endif
109
110 #if defined(__OpenBSD__)
111 #include <sys/types.h>
112 #include <machine/sysarch.h>
113
114 #if defined(__i386__)
115 #define iopl i386_iopl
116 #elif defined(__amd64__)
117 #define iopl amd64_iopl
118 #endif
119
120 #define IO_PORT_PERMISSION USE_IOPL
121 #define IO_PORT_FUNCTION USE_ASM
122 #endif
123
124 #if defined(__MACH__) && defined(__APPLE__)
125 #include <DirectHW/DirectHW.h>
126
127 #define IO_PORT_PERMISSION USE_IOPL
128 #define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
129 #endif
130
131 #if defined(__DJGPP__)
132 #include <pc.h>
133
134 #define IO_PORT_PERMISSION USE_DUMMY
135 #define IO_PORT_FUNCTION USE_DOS
136 #endif
137
138 #if defined(__LIBPAYLOAD__)
139 #include <arch/io.h>
140
141 #define IO_PORT_PERMISSION USE_DUMMY
142 #define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
143 #endif
144
145 #if defined(__sun)
146 #include <sys/sysi86.h>
147 #include <sys/psw.h>
148 #include <asm/sunddi.h>
149
150 #define IO_PORT_PERMISSION USE_SUN
151 #define IO_PORT_FUNCTION USE_LIBC_TARGET_FIRST
152 #endif
153
154 #if defined(__gnu_hurd__)
155 #include <sys/io.h>
156
157 #define IO_PORT_PERMISSION USE_IOPERM
158 #define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
159 #endif
160
161 /* Make sure IO_PORT_PERMISSION and IO_PORT_FUNCTION are set */
162 #if IO_PORT_PERMISSION == 0 || IO_PORT_FUNCTION == 0
163 #error Unsupported or misconfigured platform.
164 #endif
165
166 /*
167 * USE_DUMMY
168 * This is for platforms which have no privilege levels.
169 */
170 #if IO_PORT_PERMISSION == USE_DUMMY
platform_get_io_perms(void)171 static int platform_get_io_perms(void)
172 {
173 return 0;
174 }
175
platform_release_io_perms(void * p)176 static int platform_release_io_perms(void *p)
177 {
178 return 0;
179 }
180 #endif
181
182 /*
183 * USE_SUN
184 * This implementation uses SunOS system call sysi86 to handle I/O port permissions.
185 */
186 #if IO_PORT_PERMISSION == USE_SUN
platform_get_io_perms(void)187 static int platform_get_io_perms(void)
188 {
189 return sysi86(SI86V86, V86SC_IOPL, PS_IOPL);
190 }
191
platform_release_io_perms(void * p)192 static int platform_release_io_perms(void *p)
193 {
194 sysi86(SI86V86, V86SC_IOPL, 0);
195 return 0;
196 }
197 #endif
198
199 /*
200 * USE_DEVICE
201 * This implementation uses the virtual /dev/io file to handle I/O Port permissions.
202 */
203 #if IO_PORT_PERMISSION == USE_DEVICE
204 int io_fd;
205
platform_get_io_perms(void)206 static int platform_get_io_perms(void)
207 {
208 io_fd = open("/dev/io", O_RDWR);
209 return (io_fd >= 0 ? 0 : -1);
210 }
211
platform_release_io_perms(void * p)212 static int platform_release_io_perms(void *p)
213 {
214 close(io_fd);
215 return 0;
216 }
217 #endif
218
219 /*
220 * USE_IOPERM
221 * This implementation uses the ioperm system call to handle I/O port permissions.
222 */
223 #if IO_PORT_PERMISSION == USE_IOPERM
platform_get_io_perms(void)224 static int platform_get_io_perms(void)
225 {
226 return ioperm(0, 65536, 1);
227 }
228
platform_release_io_perms(void * p)229 static int platform_release_io_perms(void *p)
230 {
231 ioperm(0, 65536, 0);
232 return 0;
233 }
234 #endif
235
236 /*
237 * USE_IOPL
238 * This implementation uses the iopl system call to handle I/O port permissions.
239 */
240 #if IO_PORT_PERMISSION == USE_IOPL
platform_get_io_perms(void)241 static int platform_get_io_perms(void)
242 {
243 return iopl(3);
244 }
245
platform_release_io_perms(void * p)246 static int platform_release_io_perms(void *p)
247 {
248 iopl(0);
249 return 0;
250 }
251 #endif
252
253 /**
254 * @brief Get access to the x86 I/O ports
255 *
256 * This function abstracts the platform dependent function to get access to the
257 * x86 I/O ports and musst be called before using IN[B/W/L] or OUT[B/W/L].
258 * It registers a shutdown function to release those privileges.
259 *
260 * @return 0 on success, platform specific number on failure
261 */
rget_io_perms(void)262 int rget_io_perms(void)
263 {
264 if (platform_get_io_perms() == 0) {
265 register_shutdown(platform_release_io_perms, NULL);
266 return 0;
267 }
268
269 msg_perr("ERROR: Could not get I/O privileges (%s).\n", strerror(errno));
270 #if defined(__linux__) && !defined(__ANDROID__)
271 if (getuid() != 0) {
272 msg_perr("Make sure you are running flashrom with root privileges.\n");
273 } else {
274 msg_perr("Your kernel may prevent access based on security policies.\n"
275 "Issue a 'dmesg | grep flashrom' for further information\n");
276 }
277 #elif defined(__OpenBSD__)
278 msg_perr("On OpenBSD set securelevel=-1 in /etc/rc.securelevel and\n"
279 "reboot, or reboot into single user mode.\n");
280 #elif defined(__NetBSD__)
281 msg_perr("On NetBSD reboot into single user mode or make sure\n"
282 "that your kernel configuration has the option INSECURE enabled.\n");
283 #else
284 msg_perr("Make sure you are running flashrom with root privileges.\n");
285 #endif
286 return 1;
287 }
288
289 /*
290 * USE_LIBC_LAST
291 * This implementation wraps the glibc functions with the port as 2nd argument.
292 */
293 #if IO_PORT_FUNCTION == USE_LIBC_TARGET_LAST
OUTB(uint8_t value,uint16_t port)294 void OUTB(uint8_t value, uint16_t port)
295 {
296 outb(value, port);
297 }
298
OUTW(uint16_t value,uint16_t port)299 void OUTW(uint16_t value, uint16_t port)
300 {
301 outw(value, port);
302 }
303
OUTL(uint32_t value,uint16_t port)304 void OUTL(uint32_t value, uint16_t port)
305 {
306 outl(value, port);
307 }
308 #endif
309
310 /*
311 * USE_LIBC_FIRST
312 * This implementation uses libc based functions with the port as first argument
313 * like on BSDs.
314 */
315 #if IO_PORT_FUNCTION == USE_LIBC_TARGET_FIRST
OUTB(uint8_t value,uint16_t port)316 void OUTB(uint8_t value, uint16_t port)
317 {
318 outb(port, value);
319 }
320
OUTW(uint16_t value,uint16_t port)321 void OUTW(uint16_t value, uint16_t port)
322 {
323 outw(port, value);
324 }
325
OUTL(uint32_t value,uint16_t port)326 void OUTL(uint32_t value, uint16_t port)
327 {
328 outl(port, value);
329 }
330 #endif
331
332 /*
333 * LIBC_xxx
334 * INx functions can be shared between _LAST and _FIRST
335 */
336 #if IO_PORT_FUNCTION == USE_LIBC_TARGET_LAST || IO_PORT_FUNCTION == USE_LIBC_TARGET_FIRST
INB(uint16_t port)337 uint8_t INB(uint16_t port)
338 {
339 return inb(port);
340 }
341
INW(uint16_t port)342 uint16_t INW(uint16_t port)
343 {
344 return inw(port);
345 }
346
INL(uint16_t port)347 uint32_t INL(uint16_t port)
348 {
349 return inl(port);
350 }
351 #endif
352
353 /*
354 * USE_DOS
355 * DOS provides the functions under a differnt name.
356 */
357 #if IO_PORT_FUNCTION == USE_DOS
OUTB(uint8_t value,uint16_t port)358 void OUTB(uint8_t value, uint16_t port)
359 {
360 outportb(port, value);
361 }
362
OUTW(uint16_t value,uint16_t port)363 void OUTW(uint16_t value, uint16_t port)
364 {
365 outportw(port, value);
366 }
367
OUTL(uint32_t value,uint16_t port)368 void OUTL(uint32_t value, uint16_t port)
369 {
370 outportl(port, value);
371 }
372
INB(uint16_t port)373 uint8_t INB(uint16_t port)
374 {
375 return inportb(port);
376 }
377
INW(uint16_t port)378 uint16_t INW(uint16_t port)
379 {
380 return inportw(port);
381 }
382
INL(uint16_t port)383 uint32_t INL(uint16_t port)
384 {
385 return inportl(port);
386 }
387 #endif
388
389 /*
390 * USE_ASM
391 * Pure assembly implementation.
392 */
393 #if IO_PORT_FUNCTION == USE_ASM
OUTB(uint8_t value,uint16_t port)394 void OUTB(uint8_t value, uint16_t port)
395 {
396 __asm__ volatile ("outb %b0,%w1": :"a" (value), "Nd" (port));
397 }
398
OUTW(uint16_t value,uint16_t port)399 void OUTW(uint16_t value, uint16_t port)
400 {
401 __asm__ volatile ("outw %w0,%w1": :"a" (value), "Nd" (port));
402 }
403
OUTL(uint32_t value,uint16_t port)404 void OUTL(uint32_t value, uint16_t port)
405 {
406 __asm__ volatile ("outl %0,%w1": :"a" (value), "Nd" (port));
407 }
408
INB(uint16_t port)409 uint8_t INB(uint16_t port)
410 {
411 uint8_t value;
412 __asm__ volatile ("inb %w1,%0":"=a" (value):"Nd" (port));
413 return value;
414 }
415
INW(uint16_t port)416 uint16_t INW(uint16_t port)
417 {
418 uint16_t value;
419 __asm__ volatile ("inw %w1,%0":"=a" (value):"Nd" (port));
420 return value;
421 }
422
INL(uint16_t port)423 uint32_t INL(uint16_t port)
424 {
425 uint32_t value;
426 __asm__ volatile ("inl %1,%0":"=a" (value):"Nd" (port));
427 return value;
428 }
429 #endif
430