Blame sysdeps/unix/sysv/linux/ia64/ioperm.c

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