blob: 3144da29106e2698a9eaafb7dd527020638a5d19 [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__)
137#define IO_PORT_PERMISSION USE_DUMMY
138#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
139#endif
140
141#if defined(__sun)
142#include <sys/sysi86.h>
143#include <sys/psw.h>
144#include <asm/sunddi.h>
145
146#define IO_PORT_PERMISSION USE_SUN
147#define IO_PORT_FUNCTION USE_LIBC_TARGET_FIRST
148#endif
149
150#if defined(__gnu_hurd__)
151#include <sys/io.h>
152
153#define IO_PORT_PERMISSION USE_IOPERM
154#define IO_PORT_FUNCTION USE_LIBC_TARGET_LAST
155#endif
156
157/* Make sure IO_PORT_PERMISSION and IO_PORT_FUNCTION are set */
158#if IO_PORT_PERMISSION == 0 || IO_PORT_FUNCTION == 0
159#error Unsupported or misconfigured platform.
160#endif
161
162/*
163 * USE_DUMMY
164 * This is for platforms which have no privilege levels.
165 */
166#if IO_PORT_PERMISSION == USE_DUMMY
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100167static int platform_get_io_perms(void)
Thomas Heijligena0655202021-12-14 16:36:05 +0100168{
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100169 return 0;
170}
171
172static int platform_release_io_perms(void *p)
173{
174 return 0;
175}
176#endif
177
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100178/*
179 * USE_SUN
180 * This implementation uses SunOS system call sysi86 to handle I/O port permissions.
181 */
182#if IO_PORT_PERMISSION == USE_SUN
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100183static int platform_get_io_perms(void)
184{
185 return sysi86(SI86V86, V86SC_IOPL, PS_IOPL);
186}
187
188static int platform_release_io_perms(void *p)
189{
Thomas Heijligena0655202021-12-14 16:36:05 +0100190 sysi86(SI86V86, V86SC_IOPL, 0);
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100191 return 0;
192}
193#endif
194
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100195/*
196 * USE_DEVICE
197 * This implementation uses the virtual /dev/io file to handle I/O Port permissions.
198 */
199#if IO_PORT_PERMISSION == USE_DEVICE
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100200int io_fd;
201
202static int platform_get_io_perms(void)
203{
204 io_fd = open("/dev/io", O_RDWR);
205 return (io_fd >= 0 ? 0 : -1);
206}
207
208static int platform_release_io_perms(void *p)
209{
Thomas Heijligena0655202021-12-14 16:36:05 +0100210 close(io_fd);
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100211 return 0;
212}
213#endif
214
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100215/*
216 * USE_IOPERM
217 * This implementation uses the ioperm system call to handle I/O port permissions.
218 */
219#if IO_PORT_PERMISSION == USE_IOPERM
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100220static int platform_get_io_perms(void)
221{
222 return ioperm(0, 65536, 1);
223}
224
225static int platform_release_io_perms(void *p)
226{
Thomas Heijligena0655202021-12-14 16:36:05 +0100227 ioperm(0, 65536, 0);
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100228 return 0;
229}
230#endif
231
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100232/*
233 * USE_IOPL
234 * This implementation uses the iopl system call to handle I/O port permissions.
235 */
236#if IO_PORT_PERMISSION == USE_IOPL
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100237static int platform_get_io_perms(void)
238{
239 return iopl(3);
240}
241
242static int platform_release_io_perms(void *p)
243{
Thomas Heijligena0655202021-12-14 16:36:05 +0100244 iopl(0);
Thomas Heijligena0655202021-12-14 16:36:05 +0100245 return 0;
246}
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100247#endif
Thomas Heijligena0655202021-12-14 16:36:05 +0100248
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100249/**
250 * @brief Get access to the x86 I/O ports
251 *
252 * This function abstracts the platform dependent function to get access to the
253 * x86 I/O ports and musst be called before using IN[B/W/L] or OUT[B/W/L].
254 * It registers a shutdown function to release those privileges.
255 *
256 * @return 0 on success, platform specific number on failure
257 */
Thomas Heijligena0655202021-12-14 16:36:05 +0100258int rget_io_perms(void)
259{
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100260 if (platform_get_io_perms() == 0) {
261 register_shutdown(platform_release_io_perms, NULL);
262 return 0;
Thomas Heijligena0655202021-12-14 16:36:05 +0100263 }
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100264 msg_perr("ERROR: Could not get I/O privileges (%s).\n", strerror(errno));
265 msg_perr("Make sure you are root.\n");
266#if defined (__OpenBSD__)
267 msg_perr("If you are root already please set securelevel=-1 in /etc/rc.securelevel and\n"
268 "reboot, or reboot into single user mode.\n");
269#elif defined(__NetBSD__)
270 msg_perr("If you are root already please reboot into single user mode or make sure\n"
271 "that your kernel configuration has the option INSECURE enabled.\n");
Thomas Heijligena0655202021-12-14 16:36:05 +0100272#else
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100273 msg_perr("If you are root, your kernel may still prevent access based on\n"
274 "security policies.\n");
Thomas Heijligena0655202021-12-14 16:36:05 +0100275#endif
Thomas Heijligenad4f27f2022-01-20 14:33:56 +0100276 return 1;
277}
Thomas Heijligen4c39c432022-01-20 16:45:14 +0100278
279/*
280 * USE_LIBC_LAST
281 * This implementation wraps the glibc functions with the port as 2nd argument.
282 */
283#if IO_PORT_FUNCTION == USE_LIBC_TARGET_LAST
284void OUTB(uint8_t value, uint16_t port)
285{
286 outb(value, port);
287}
288
289void OUTW(uint16_t value, uint16_t port)
290{
291 outw(value, port);
292}
293
294void OUTL(uint32_t value, uint16_t port)
295{
296 outl(value, port);
297}
298#endif
299
300/*
301 * USE_LIBC_FIRST
302 * This implementation uses libc based functions with the port as first argument
303 * like on BSDs.
304 */
305#if IO_PORT_FUNCTION == USE_LIBC_TARGET_FIRST
306void OUTB(uint8_t value, uint16_t port)
307{
308 outb(port, value);
309}
310
311void OUTW(uint16_t value, uint16_t port)
312{
313 outw(port, value);
314}
315
316void OUTL(uint32_t value, uint16_t port)
317{
318 outl(port, value);
319}
320#endif
321
322/*
323 * LIBC_xxx
324 * INx functions can be shared between _LAST and _FIRST
325 */
326#if IO_PORT_FUNCTION == USE_LIBC_TARGET_LAST || IO_PORT_FUNCTION == USE_LIBC_TARGET_FIRST
327uint8_t INB(uint16_t port)
328{
329 return inb(port);
330}
331
332uint16_t INW(uint16_t port)
333{
334 return inw(port);
335}
336
337uint32_t INL(uint16_t port)
338{
339 return inl(port);
340}
341#endif
342
343/*
344 * USE_DOS
345 * DOS provides the functions under a differnt name.
346 */
347#if IO_PORT_FUNCTION == USE_DOS
348void OUTB(uint8_t value, uint16_t port)
349{
350 outportb(port, value);
351}
352
353void OUTW(uint16_t value, uint16_t port)
354{
355 outportw(port, value);
356}
357
358void OUTL(uint32_t value, uint16_t port)
359{
360 outportl(port, value);
361}
362
363uint8_t INB(uint16_t port)
364{
365 return inportb(port);
366}
367
368uint16_t INW(uint16_t port)
369{
370 return inportw(port);
371}
372
373uint32_t INL(uint16_t port)
374{
375 return inportl(port);
376}
377#endif
378
379/*
380 * USE_ASM
381 * Pure assembly implementation.
382 */
383#if IO_PORT_FUNCTION == USE_ASM
384void OUTB(uint8_t value, uint16_t port)
385{
386 __asm__ volatile ("outb %b0,%w1": :"a" (value), "Nd" (port));
387}
388
389void OUTW(uint16_t value, uint16_t port)
390{
391 __asm__ volatile ("outw %w0,%w1": :"a" (value), "Nd" (port));
392}
393
394void OUTL(uint32_t value, uint16_t port)
395{
396 __asm__ volatile ("outl %0,%w1": :"a" (value), "Nd" (port));
397}
398
399uint8_t INB(uint16_t port)
400{
401 uint8_t value;
402 __asm__ volatile ("inb %w1,%0":"=a" (value):"Nd" (port));
403 return value;
404}
405
406uint16_t INW(uint16_t port)
407{
408 uint16_t value;
409 __asm__ volatile ("inw %w1,%0":"=a" (value):"Nd" (port));
410 return value;
411}
412
413uint32_t INL(uint16_t port)
414{
415 uint32_t value;
416 __asm__ volatile ("inl %1,%0":"=a" (value):"Nd" (port));
417 return value;
418}
419#endif