blob: 9d1dc8f4148af8b84356f7cf5fad8ed27b980b4d [file] [log] [blame]
Thomas Heijligena0655202021-12-14 16:36:05 +01001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
Thomas Heijligenad4f27f2022-01-20 14:33:56 +01005 * Copyright (C) 2022 secunet Security Networks AG
6 * (Written by Thomas Heijligen <thomas.heijligen@secunet.com)
Thomas Heijligena0655202021-12-14 16:36:05 +01007 *
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
Thomas Heijligenad4f27f2022-01-20 14:33:56 +010019/*
Thomas Heijligen4c39c432022-01-20 16:45:14 +010020 * This file implements x86 I/O port permission and access handling.
Thomas Heijligenad4f27f2022-01-20 14:33:56 +010021 *
Thomas Heijligen4c39c432022-01-20 16:45:14 +010022 * 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.
Thomas Heijligenad4f27f2022-01-20 14:33:56 +010039 */
40
Thomas Heijligena0655202021-12-14 16:36:05 +010041#include <errno.h>
42#include <string.h>
Thomas Heijligena0655202021-12-14 16:36:05 +010043
Thomas Heijligena0655202021-12-14 16:36:05 +010044#include "flash.h"
Thomas Heijligenad4f27f2022-01-20 14:33:56 +010045#include "hwaccess_x86_io.h"
Thomas Heijligena0655202021-12-14 16:36:05 +010046
Thomas Heijligen4c39c432022-01-20 16:45:14 +010047/* 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/glibc-syscalls.h>
62
63#define IO_PORT_PERMISSION USE_IOPL
64#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
65#endif
66
67#if defined(__linux__) && !defined(__ANDROID__)
68#include <sys/io.h>
69
70#define IO_PORT_PERMISSION USE_IOPL
71#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
72#endif
73
74#if defined(__FreeBSD_kernel) && defined(__GLIBC__)
75#include <sys/io.h>
76#include <sys/types.h>
77#include <fcntl.h>
78#include <unistd.h>
79
80#define IO_PORT_PERMISSION USE_DEVICE
81#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
82#endif
83
84#if defined(__FreeBSD__) || defined(__DragonFly__)
85#include <sys/types.h>
86#include <machine/cpufunc.h>
87#include <fcntl.h>
88#include <unistd.h>
89
90#define IO_PORT_PERMISSION USE_DEVICE
91#define IO_PORT_FUNCTION USE_LIBC_TARGET_FIRST
92#endif
93
94#if defined(__NetBSD__)
95#include <sys/types.h>
96#include <machine/sysarch.h>
97
98#if defined(__i386__)
99#define iopl i386_iopl
100#elif defined(__x86_64__)
101#define iopl x86_64_iopl
102#endif
103
104#define IO_PORT_PERMISSION USE_IOPL
105#define IO_PORT_FUNCTION USE_ASM
106#endif
107
108#if defined(__OpenBSD__)
109#include <sys/types.h>
110#include <machine/sysarch.h>
111
112#if defined(__i386__)
113#define iopl i386_iopl
114#elif defined(__amd64__)
115#define iopl amd64_iopl
116#endif
117
118#define IO_PORT_PERMISSION USE_IOPL
119#define IO_PORT_FUNCTION USE_ASM
120#endif
121
122#if defined(__MACH__) && defined(__APPLE__)
123#include <DirectHW/DirectHW.h>
124
125#define IO_PORT_PERMISSION USE_IOPL
126#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
127#endif
128
129#if defined(__DJGPP__)
130#include <pc.h>
131
132#define IO_PORT_PERMISSION USE_DUMMY
133#define IO_PORT_FUNCTION USE_DOS
134#endif
135
136#if defined(__LIBPAYLOAD__)
Thomas Heijligend4390882022-11-18 22:53:56 +0100137#include <arch/io.h>
138
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100139#define IO_PORT_PERMISSION USE_DUMMY
140#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
141#endif
142
143#if defined(__sun)
144#include <sys/sysi86.h>
145#include <sys/psw.h>
146#include <asm/sunddi.h>
147
148#define IO_PORT_PERMISSION USE_SUN
149#define IO_PORT_FUNCTION USE_LIBC_TARGET_FIRST
150#endif
151
152#if defined(__gnu_hurd__)
153#include <sys/io.h>
154
155#define IO_PORT_PERMISSION USE_IOPERM
156#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
157#endif
158
159/* Make sure IO_PORT_PERMISSION and IO_PORT_FUNCTION are set */
160#if IO_PORT_PERMISSION == 0 || IO_PORT_FUNCTION == 0
161#error Unsupported or misconfigured platform.
162#endif
163
164/*
165 * USE_DUMMY
166 * This is for platforms which have no privilege levels.
167 */
168#if IO_PORT_PERMISSION == USE_DUMMY
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100169static int platform_get_io_perms(void)
Thomas Heijligena0655202021-12-14 16:36:05 +0100170{
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100171 return 0;
172}
173
174static int platform_release_io_perms(void *p)
175{
176 return 0;
177}
178#endif
179
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100180/*
181 * USE_SUN
182 * This implementation uses SunOS system call sysi86 to handle I/O port permissions.
183 */
184#if IO_PORT_PERMISSION == USE_SUN
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100185static int platform_get_io_perms(void)
186{
187 return sysi86(SI86V86, V86SC_IOPL, PS_IOPL);
188}
189
190static int platform_release_io_perms(void *p)
191{
Thomas Heijligena0655202021-12-14 16:36:05 +0100192 sysi86(SI86V86, V86SC_IOPL, 0);
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100193 return 0;
194}
195#endif
196
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100197/*
198 * USE_DEVICE
199 * This implementation uses the virtual /dev/io file to handle I/O Port permissions.
200 */
201#if IO_PORT_PERMISSION == USE_DEVICE
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100202int io_fd;
203
204static int platform_get_io_perms(void)
205{
206 io_fd = open("/dev/io", O_RDWR);
207 return (io_fd >= 0 ? 0 : -1);
208}
209
210static int platform_release_io_perms(void *p)
211{
Thomas Heijligena0655202021-12-14 16:36:05 +0100212 close(io_fd);
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100213 return 0;
214}
215#endif
216
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100217/*
218 * USE_IOPERM
219 * This implementation uses the ioperm system call to handle I/O port permissions.
220 */
221#if IO_PORT_PERMISSION == USE_IOPERM
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100222static int platform_get_io_perms(void)
223{
224 return ioperm(0, 65536, 1);
225}
226
227static int platform_release_io_perms(void *p)
228{
Thomas Heijligena0655202021-12-14 16:36:05 +0100229 ioperm(0, 65536, 0);
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100230 return 0;
231}
232#endif
233
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100234/*
235 * USE_IOPL
236 * This implementation uses the iopl system call to handle I/O port permissions.
237 */
238#if IO_PORT_PERMISSION == USE_IOPL
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100239static int platform_get_io_perms(void)
240{
241 return iopl(3);
242}
243
244static int platform_release_io_perms(void *p)
245{
Thomas Heijligena0655202021-12-14 16:36:05 +0100246 iopl(0);
Thomas Heijligena0655202021-12-14 16:36:05 +0100247 return 0;
248}
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100249#endif
Thomas Heijligena0655202021-12-14 16:36:05 +0100250
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100251/**
252 * @brief Get access to the x86 I/O ports
253 *
254 * This function abstracts the platform dependent function to get access to the
255 * x86 I/O ports and musst be called before using IN[B/W/L] or OUT[B/W/L].
256 * It registers a shutdown function to release those privileges.
257 *
258 * @return 0 on success, platform specific number on failure
259 */
Thomas Heijligena0655202021-12-14 16:36:05 +0100260int rget_io_perms(void)
261{
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100262 if (platform_get_io_perms() == 0) {
263 register_shutdown(platform_release_io_perms, NULL);
264 return 0;
Thomas Heijligena0655202021-12-14 16:36:05 +0100265 }
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100266 msg_perr("ERROR: Could not get I/O privileges (%s).\n", strerror(errno));
267 msg_perr("Make sure you are root.\n");
268#if defined (__OpenBSD__)
269 msg_perr("If you are root already please set securelevel=-1 in /etc/rc.securelevel and\n"
270 "reboot, or reboot into single user mode.\n");
271#elif defined(__NetBSD__)
272 msg_perr("If you are root already please reboot into single user mode or make sure\n"
273 "that your kernel configuration has the option INSECURE enabled.\n");
Thomas Heijligena0655202021-12-14 16:36:05 +0100274#else
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100275 msg_perr("If you are root, your kernel may still prevent access based on\n"
276 "security policies.\n");
Thomas Heijligena0655202021-12-14 16:36:05 +0100277#endif
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100278 return 1;
279}
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100280
281/*
282 * USE_LIBC_LAST
283 * This implementation wraps the glibc functions with the port as 2nd argument.
284 */
285#if IO_PORT_FUNCTION == USE_LIBC_TARGET_LAST
286void OUTB(uint8_t value, uint16_t port)
287{
288 outb(value, port);
289}
290
291void OUTW(uint16_t value, uint16_t port)
292{
293 outw(value, port);
294}
295
296void OUTL(uint32_t value, uint16_t port)
297{
298 outl(value, port);
299}
300#endif
301
302/*
303 * USE_LIBC_FIRST
304 * This implementation uses libc based functions with the port as first argument
305 * like on BSDs.
306 */
307#if IO_PORT_FUNCTION == USE_LIBC_TARGET_FIRST
308void OUTB(uint8_t value, uint16_t port)
309{
310 outb(port, value);
311}
312
313void OUTW(uint16_t value, uint16_t port)
314{
315 outw(port, value);
316}
317
318void OUTL(uint32_t value, uint16_t port)
319{
320 outl(port, value);
321}
322#endif
323
324/*
325 * LIBC_xxx
326 * INx functions can be shared between _LAST and _FIRST
327 */
328#if IO_PORT_FUNCTION == USE_LIBC_TARGET_LAST || IO_PORT_FUNCTION == USE_LIBC_TARGET_FIRST
329uint8_t INB(uint16_t port)
330{
331 return inb(port);
332}
333
334uint16_t INW(uint16_t port)
335{
336 return inw(port);
337}
338
339uint32_t INL(uint16_t port)
340{
341 return inl(port);
342}
343#endif
344
345/*
346 * USE_DOS
347 * DOS provides the functions under a differnt name.
348 */
349#if IO_PORT_FUNCTION == USE_DOS
350void OUTB(uint8_t value, uint16_t port)
351{
352 outportb(port, value);
353}
354
355void OUTW(uint16_t value, uint16_t port)
356{
357 outportw(port, value);
358}
359
360void OUTL(uint32_t value, uint16_t port)
361{
362 outportl(port, value);
363}
364
365uint8_t INB(uint16_t port)
366{
367 return inportb(port);
368}
369
370uint16_t INW(uint16_t port)
371{
372 return inportw(port);
373}
374
375uint32_t INL(uint16_t port)
376{
377 return inportl(port);
378}
379#endif
380
381/*
382 * USE_ASM
383 * Pure assembly implementation.
384 */
385#if IO_PORT_FUNCTION == USE_ASM
386void OUTB(uint8_t value, uint16_t port)
387{
388 __asm__ volatile ("outb %b0,%w1": :"a" (value), "Nd" (port));
389}
390
391void OUTW(uint16_t value, uint16_t port)
392{
393 __asm__ volatile ("outw %w0,%w1": :"a" (value), "Nd" (port));
394}
395
396void OUTL(uint32_t value, uint16_t port)
397{
398 __asm__ volatile ("outl %0,%w1": :"a" (value), "Nd" (port));
399}
400
401uint8_t INB(uint16_t port)
402{
403 uint8_t value;
404 __asm__ volatile ("inb %w1,%0":"=a" (value):"Nd" (port));
405 return value;
406}
407
408uint16_t INW(uint16_t port)
409{
410 uint16_t value;
411 __asm__ volatile ("inw %w1,%0":"=a" (value):"Nd" (port));
412 return value;
413}
414
415uint32_t INL(uint16_t port)
416{
417 uint32_t value;
418 __asm__ volatile ("inl %1,%0":"=a" (value):"Nd" (port));
419 return value;
420}
421#endif