📄 ioperm.c
字号:
/* Copyright (C) 1992, 1996-1999, 2000 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by David Mosberger. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *//* 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, 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 page, there are PAGE_SIZE>>IO_SHIFT I/O ports (e.g., 256 ports on a Low Cost Alpha based system using 8KB pages). Keep in mind that this code should be able to run in a 32bit address space. It is therefore unreasonable to expect mmap'ing the entire sparse address space would work (e.g., the Low Cost Alpha chip has an I/O address space that's 512MB large!). *//* Make sure the ldbu/stb asms below are not expaneded to macros. */#ifndef __alpha_bwx__asm(".arch ev56");#endif#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <ctype.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/mman.h>#include <sys/io.h>#include <sysdep.h>#include <sys/syscall.h>#define PATH_ALPHA_SYSTYPE "/etc/alpha_systype"#define PATH_CPUINFO "/proc/cpuinfo"#define MAX_PORT 0x10000#define vip volatile int *#define vuip volatile unsigned int *#define vusp volatile unsigned short *#define vucp volatile unsigned char *#define JENSEN_IO_BASE (0x300000000UL)#define JENSEN_SPARSE_MEM (0x200000000UL)/* With respect to the I/O architecture, APECS and LCA are identical, so the following defines apply to LCA as well. */#define APECS_IO_BASE (0x1c0000000UL)#define APECS_SPARSE_MEM (0x200000000UL)#define APECS_DENSE_MEM (0x300000000UL)/* The same holds for CIA and PYXIS, except for PYXIS we prefer BWX. */#define CIA_IO_BASE (0x8580000000UL)#define CIA_SPARSE_MEM (0x8000000000UL)#define CIA_DENSE_MEM (0x8600000000UL)#define PYXIS_IO_BASE (0x8900000000UL)#define PYXIS_DENSE_MEM (0x8800000000UL)/* SABLE is EV4, GAMMA is EV5 */#define T2_IO_BASE (0x3a0000000UL)#define T2_SPARSE_MEM (0x200000000UL)#define T2_DENSE_MEM (0x3c0000000UL)#define GAMMA_IO_BASE (0x83a0000000UL)#define GAMMA_SPARSE_MEM (0x8200000000UL)#define GAMMA_DENSE_MEM (0x83c0000000UL)/* NOTE: these are hardwired to PCI bus 0 addresses!!! */#define MCPCIA_IO_BASE (0xf980000000UL)#define MCPCIA_SPARSE_MEM (0xf800000000UL)#define MCPCIA_DENSE_MEM (0xf900000000UL)/* Tsunami and Irongate use the same offsets, at least for hose 0. */#define TSUNAMI_IO_BASE (0x801fc000000UL)#define TSUNAMI_DENSE_MEM (0x80000000000UL)/* Polaris has SPARSE space, but we prefer to use only DENSE because of some idiosyncracies in actually using SPARSE. */#define POLARIS_IO_BASE (0xf9fc000000UL)#define POLARIS_DENSE_MEM (0xf900000000UL)typedef enum { IOSYS_UNKNOWN, IOSYS_JENSEN, IOSYS_APECS, IOSYS_CIA, IOSYS_PYXIS, IOSYS_T2, IOSYS_TSUNAMI, IOSYS_MCPCIA, IOSYS_GAMMA, IOSYS_POLARIS, IOSYS_CPUDEP, IOSYS_PCIDEP} iosys_t;typedef enum { IOSWIZZLE_JENSEN, IOSWIZZLE_SPARSE, IOSWIZZLE_DENSE} ioswizzle_t;static struct io_system { unsigned long int bus_memory_base; unsigned long int sparse_bus_mem_base; unsigned long int bus_io_base;} io_system[] = { /* NOTE! must match iosys_t enumeration *//* UNKNOWN */ {0, 0, 0},/* JENSEN */ {0, JENSEN_SPARSE_MEM, JENSEN_IO_BASE},/* APECS */ {APECS_DENSE_MEM, APECS_SPARSE_MEM, APECS_IO_BASE},/* CIA */ {CIA_DENSE_MEM, CIA_SPARSE_MEM, CIA_IO_BASE},/* PYXIS */ {PYXIS_DENSE_MEM, 0, PYXIS_IO_BASE},/* T2 */ {T2_DENSE_MEM, T2_SPARSE_MEM, T2_IO_BASE},/* TSUNAMI */ {TSUNAMI_DENSE_MEM, 0, TSUNAMI_IO_BASE},/* MCPCIA */ {MCPCIA_DENSE_MEM, MCPCIA_SPARSE_MEM, MCPCIA_IO_BASE},/* GAMMA */ {GAMMA_DENSE_MEM, GAMMA_SPARSE_MEM, GAMMA_IO_BASE},/* POLARIS */ {POLARIS_DENSE_MEM, 0, POLARIS_IO_BASE},/* CPUDEP */ {0, 0, 0}, /* for platforms dependent on CPU type *//* PCIDEP */ {0, 0, 0}, /* for platforms dependent on core logic */};static struct platform { const char *name; iosys_t io_sys;} platform[] = { {"Alcor", IOSYS_CIA}, {"Avanti", IOSYS_APECS}, {"Cabriolet", IOSYS_APECS}, {"EB164", IOSYS_PCIDEP}, {"EB64+", IOSYS_APECS}, {"EB66", IOSYS_APECS}, {"EB66P", IOSYS_APECS}, {"Jensen", IOSYS_JENSEN}, {"Miata", IOSYS_PYXIS}, {"Mikasa", IOSYS_CPUDEP}, {"Nautilus", IOSYS_TSUNAMI}, {"Noname", IOSYS_APECS}, {"Noritake", IOSYS_CPUDEP}, {"Rawhide", IOSYS_MCPCIA}, {"Ruffian", IOSYS_PYXIS}, {"Sable", IOSYS_CPUDEP}, {"Takara", IOSYS_CIA}, {"Tsunami", IOSYS_TSUNAMI}, {"XL", IOSYS_APECS},};struct ioswtch { void (*sethae)(unsigned long int addr); void (*outb)(unsigned char b, unsigned long int port); void (*outw)(unsigned short b, unsigned long int port); void (*outl)(unsigned int b, unsigned long int port); unsigned int (*inb)(unsigned long int port); unsigned int (*inw)(unsigned long int port); unsigned int (*inl)(unsigned long int port);};static struct { unsigned long int hae_cache; unsigned long int base; struct ioswtch * swp; unsigned long int bus_memory_base; unsigned long int sparse_bus_memory_base; unsigned long int io_base; ioswizzle_t swiz;} io;static inline voidstb_mb(unsigned char val, unsigned long addr){ __asm__("stb %1,%0; mb" : "=m"(*(vucp)addr) : "r"(val));}static inline voidstw_mb(unsigned short val, unsigned long addr){ __asm__("stw %1,%0; mb" : "=m"(*(vusp)addr) : "r"(val));}static inline voidstl_mb(unsigned int val, unsigned long addr){ __asm__("stl %1,%0; mb" : "=m"(*(vip)addr) : "r"(val));}/* No need to examine error -- sethae never fails. */static inline void__sethae(unsigned long value){ register unsigned long r16 __asm__("$16") = value; register unsigned long r0 __asm__("$0") = __NR_sethae; __asm__ __volatile__ ("callsys" : "=r"(r0) : "0"(r0), "r" (r16) : inline_syscall_clobbers, "$19");}extern long __pciconfig_iobase(enum __pciconfig_iobase_which __which, unsigned long int __bus, unsigned long int __dfn);static inline unsigned long intport_to_cpu_addr (unsigned long int port, ioswizzle_t ioswiz, int size){ if (ioswiz == IOSWIZZLE_SPARSE) return io.base + (port << 5) + ((size - 1) << 3); else if (ioswiz == IOSWIZZLE_DENSE) return port + io.base; else return io.base + (port << 7) + ((size - 1) << 5);}static inline voidinline_sethae (unsigned long int addr, ioswizzle_t ioswiz){ if (ioswiz == IOSWIZZLE_SPARSE) { unsigned long int msb; /* no need to set hae if msb is 0: */ msb = addr & 0xf8000000; if (msb && msb != io.hae_cache) { io.hae_cache = msb; __sethae (msb); } } else if (ioswiz == IOSWIZZLE_JENSEN) { /* HAE on the Jensen is bits 31:25 shifted right. */ addr >>= 25; if (addr != io.hae_cache) { io.hae_cache = addr; __sethae (addr); } }}static inline voidinline_outb (unsigned char b, unsigned long int port, ioswizzle_t ioswiz){ unsigned int w; unsigned long int addr = port_to_cpu_addr (port, ioswiz, 1); asm ("insbl %2,%1,%0" : "=r" (w) : "ri" (port & 0x3), "r" (b)); stl_mb(w, addr);}static inline voidinline_outw (unsigned short int b, unsigned long int port, ioswizzle_t ioswiz){ unsigned long w; unsigned long int addr = port_to_cpu_addr (port, ioswiz, 2); asm ("inswl %2,%1,%0" : "=r" (w) : "ri" (port & 0x3), "r" (b)); stl_mb(w, addr);}static inline voidinline_outl (unsigned int b, unsigned long int port, ioswizzle_t ioswiz){ unsigned long int addr = port_to_cpu_addr (port, ioswiz, 4); stl_mb(b, addr);}static inline unsigned intinline_inb (unsigned long int port, ioswizzle_t ioswiz){ unsigned long int addr = port_to_cpu_addr (port, ioswiz, 1); int result; result = *(vip) addr; result >>= (port & 3) * 8; return 0xffUL & result;}static inline unsigned intinline_inw (unsigned long int port, ioswizzle_t ioswiz){ unsigned long int addr = port_to_cpu_addr (port, ioswiz, 2); int result; result = *(vip) addr; result >>= (port & 3) * 8; return 0xffffUL & result;}static inline unsigned intinline_inl (unsigned long int port, ioswizzle_t ioswiz){ unsigned long int addr = port_to_cpu_addr (port, ioswiz, 4); return *(vuip) addr;}/* * Now define the inline functions for CPUs supporting byte/word insns, * and whose core logic supports I/O space accesses utilizing them. * * These routines could be used by MIATA, for example, because it has * and EV56 plus PYXIS, but it currently uses SPARSE anyway. This is * also true of RX164 which used POLARIS, but we will choose to use * these routines in that case instead of SPARSE. * * These routines are necessary for TSUNAMI/TYPHOON based platforms, * which will have (at least) EV6. */static inline unsigned long intdense_port_to_cpu_addr (unsigned long int port){ return port + io.base;}static inline voidinline_bwx_outb (unsigned char b, unsigned long int port){ unsigned long int addr = dense_port_to_cpu_addr (port); stb_mb (b, addr);}static inline voidinline_bwx_outw (unsigned short int b, unsigned long int port){ unsigned long int addr = dense_port_to_cpu_addr (port); stw_mb (b, addr);}static inline voidinline_bwx_outl (unsigned int b, unsigned long int port){ unsigned long int addr = dense_port_to_cpu_addr (port); stl_mb (b, addr);}static inline unsigned intinline_bwx_inb (unsigned long int port){ unsigned long int addr = dense_port_to_cpu_addr (port); unsigned char r; __asm__ ("ldbu %0,%1" : "=r"(r) : "m"(*(vucp)addr)); return r;}static inline unsigned intinline_bwx_inw (unsigned long int port){ unsigned long int addr = dense_port_to_cpu_addr (port); unsigned short r; __asm__ ("ldwu %0,%1" : "=r"(r) : "m"(*(vusp)addr)); return r;}static inline unsigned intinline_bwx_inl (unsigned long int port){ unsigned long int addr = dense_port_to_cpu_addr (port); return *(vuip) addr;}/* macros to define routines with appropriate names and functions *//* these do either SPARSE or JENSEN swizzle */#define DCL_SETHAE(name, ioswiz) \static void \name##_sethae (unsigned long int addr) \{ \ inline_sethae (addr, IOSWIZZLE_##ioswiz); \}#define DCL_OUT(name, func, type, ioswiz) \static void \name##_##func (unsigned type b, unsigned long int addr) \{ \ inline_##func (b, addr, IOSWIZZLE_##ioswiz); \}#define DCL_IN(name, func, ioswiz) \static unsigned int \name##_##func (unsigned long int addr) \{ \ return inline_##func (addr, IOSWIZZLE_##ioswiz); \}/* these do DENSE, so no swizzle is needed */#define DCL_OUT_BWX(name, func, type) \static void \name##_##func (unsigned type b, unsigned long int addr) \{ \ inline_bwx_##func (b, addr); \}#define DCL_IN_BWX(name, func) \static unsigned int \name##_##func (unsigned long int addr) \{ \ return inline_bwx_##func (addr); \}/* now declare/define the necessary routines */DCL_SETHAE(jensen, JENSEN)DCL_OUT(jensen, outb, char, JENSEN)DCL_OUT(jensen, outw, short int, JENSEN)DCL_OUT(jensen, outl, int, JENSEN)DCL_IN(jensen, inb, JENSEN)DCL_IN(jensen, inw, JENSEN)DCL_IN(jensen, inl, JENSEN)DCL_SETHAE(sparse, SPARSE)DCL_OUT(sparse, outb, char, SPARSE)DCL_OUT(sparse, outw, short int, SPARSE)DCL_OUT(sparse, outl, int, SPARSE)DCL_IN(sparse, inb, SPARSE)DCL_IN(sparse, inw, SPARSE)DCL_IN(sparse, inl, SPARSE)DCL_SETHAE(dense, DENSE)DCL_OUT_BWX(dense, outb, char)DCL_OUT_BWX(dense, outw, short int)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -