1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
/* Copyright (C) 1999-2016 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);
|