diff options
Diffstat (limited to 'sysdeps/unix/sysv/linux/ia64/ioperm.c')
-rw-r--r-- | sysdeps/unix/sysv/linux/ia64/ioperm.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/ia64/ioperm.c b/sysdeps/unix/sysv/linux/ia64/ioperm.c new file mode 100644 index 0000000000..f2e5418d54 --- /dev/null +++ b/sysdeps/unix/sysv/linux/ia64/ioperm.c @@ -0,0 +1,201 @@ +/* Copyright (C) 1999-2014 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* I/O access is restricted to ISA port space (ports 0..65535). + Modern devices hopefully are sane enough not to put any performance + critical registers in i/o space. + + On the first call to ioperm() or iopl(), the entire (E)ISA port + space is mapped into the virtual address space at address io.base. + mprotect() calls are then used to enable/disable access to ports. + Per 4KB page, there are 4 I/O ports. */ + +#include <errno.h> +#include <fcntl.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/mman.h> + +#define MAX_PORT 0x10000 + +/* + * Memory fence w/accept. This should never be used in code that is + * not IA-64 specific. + */ +#define __ia64_mf_a() __asm__ __volatile__ ("mf.a" ::: "memory") + +static struct + { + unsigned long int base; + unsigned long int page_mask; + } +io; + +__inline__ unsigned long int +io_offset (unsigned long int port) +{ + return ((port >> 2) << 12) | (port & 0xfff); +} + +int +_ioperm (unsigned long int from, unsigned long int num, int turn_on) +{ + unsigned long int base; + + /* this test isn't as silly as it may look like; consider overflows! */ + if (from >= MAX_PORT || from + num > MAX_PORT) + { + __set_errno (EINVAL); + return -1; + } + + if (turn_on) + { + if (!io.base) + { + unsigned long phys_io_base, len; + int fd; + + io.page_mask = ~(__getpagesize() - 1); + + /* get I/O base physical address from ar.k0 as per PRM: */ + __asm__ ("mov %0=ar.k0" : "=r"(phys_io_base)); + + /* The O_SYNC flag tells the /dev/mem driver to map the + memory uncached: */ + fd = __open ("/dev/mem", O_RDWR | O_SYNC); + if (fd < 0) + return -1; + + len = io_offset (MAX_PORT); + /* see comment below */ + base = (unsigned long int) __mmap (0, len, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, phys_io_base); + __close (fd); + + if ((long) base == -1) + return -1; + + io.base = base; + } + } + else + { + if (!io.base) + return 0; /* never was turned on... */ + } + + /* We can't do mprotect because that would cause us to lose the + uncached flag that the /dev/mem driver turned on. A MAP_UNCACHED + flag seems so much cleaner... + + See the history of this file for a version that tried mprotect. */ + return 0; +} + +int +_iopl (unsigned int level) +{ + if (level > 3) + { + __set_errno (EINVAL); + return -1; + } + if (level) + { + int retval = _ioperm (0, MAX_PORT, 1); + /* Match the documented error returns of the x86 version. */ + if (retval < 0 && errno == EACCES) + __set_errno (EPERM); + return retval; + } + return 0; +} + +unsigned int +_inb (unsigned long int port) +{ + volatile unsigned char *addr = (void *) io.base + io_offset (port); + unsigned char ret; + + ret = *addr; + __ia64_mf_a(); + return ret; +} + +unsigned int +_inw (unsigned long int port) +{ + volatile unsigned short *addr = (void *) io.base + io_offset (port); + unsigned short ret; + + ret = *addr; + __ia64_mf_a(); + return ret; +} + +unsigned int +_inl (unsigned long int port) +{ + volatile unsigned int *addr = (void *) io.base + io_offset (port); + unsigned int ret; + + ret = *addr; + __ia64_mf_a(); + return ret; +} + +void +_outb (unsigned char val, unsigned long int port) +{ + volatile unsigned char *addr = (void *) io.base + io_offset (port); + + *addr = val; + __ia64_mf_a(); +} + +void +_outw (unsigned short val, unsigned long int port) +{ + volatile unsigned short *addr = (void *) io.base + io_offset (port); + + *addr = val; + __ia64_mf_a(); +} + +void +_outl (unsigned int val, unsigned long int port) +{ + volatile unsigned int *addr = (void *) io.base + io_offset (port); + + *addr = val; + __ia64_mf_a(); +} + +weak_alias (_ioperm, ioperm); +weak_alias (_iopl, iopl); +weak_alias (_inb, inb); +weak_alias (_inw, inw); +weak_alias (_inl, inl); +weak_alias (_outb, outb); +weak_alias (_outw, outw); +weak_alias (_outl, outl); |