xref: /aosp_15_r20/external/flashrom/hwaccess_x86_io.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
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