| /* Copyright (C) 1999-2018 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); |