NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
port-amd64/43688: ioperm() is missing on NetBSD/amd64 (patch provided)
>Number: 43688
>Category: port-amd64
>Synopsis: ioperm() is missing on NetBSD/amd64 (patch provided)
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: port-amd64-maintainer
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Jul 31 00:25:00 +0000 2010
>Originator: Pierre Pronchery
>Release: NetBSD 5.1_RC2
>Organization:
>Environment:
System: NetBSD kwarx.defora.lan 5.1_RC2 NetBSD 5.1_RC2 (GENERIC) #2: Fri Jun 11
21:15:51 CEST 2010
khorben%kwarx.defora.lan@localhost:/usr/obj/sys/arch/amd64/compile/GENERIC amd64
Architecture: x86_64
Machine: amd64
>Description:
On x86-based platforms, the ioperm() allows fine-grained access to "hardware
ports", typically serial and parallel ports managed by the BIOS. These are then
referenced by a port number up to 0x4000, otherwise requiring a call to iopl()
instead (which basically grants or denies access to all the ports at once).
Interaction with the actual device can then be performed with the inb/outb
assembly calls.
These two interfaces are inherently platform-specific, and found on fewer
architectures besides x86. In fact, NetBSD's amd64 port only supports iopl()
nowadays; the patch supplied here adds support for ioperm().
This patch applies on NetBSD 5.1_RC3, I hope it will also apply on -current. In
any case, it would also need a manual page for the exact interfaces to
ioperm(): x86_64_get_ioperm() and x86_64_set_ioperm(), as illustrated below in
psx.c.
>How-To-Repeat:
Try to use ioperm() (or a direct equivalent) on NetBSD/amd64: iopl() has to be
used instead.
I have used the following program to demonstrate the issue, and test my changes;
it is a userland adaptation of a Linux kernel driver, gamecon, supporting NES,
SNES, N64, Multi1, Multi2, and PSX gamepads through the parallel port.
/* $Id: psx.c,v 1.2 2010/07/31 00:04:12 khorben Exp $ */
/* Copyright (c) 2010 Pierre Pronchery <khorben%defora.org@localhost> */
/*
* Based on the work of Vojtech Pavlik
* $Id: gamecon.c,v 1.14 2001/04/29 22:42:14 vojtech Exp $
* In turn based on the work of:
* Andree Borrmann John Dahlstrom
* David Kuder Nathan Hand
*
* Sponsored by SuSE
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact the original author, you can do so either by
* e-mail - mail your message to <vojtech%suse.cz@localhost>, or by paper mail:
* Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <machine/sysarch.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
/* ioperm */
#ifdef __NetBSD__
int ioperm(unsigned long from, unsigned long num, int permit);
# if defined(__amd64__)
# define get_ioperm(ports) x86_64_get_ioperm(ports)
# define set_ioperm(ports) x86_64_set_ioperm(ports)
int x86_64_get_ioperm(unsigned long * ports);
int x86_64_set_ioperm(unsigned long * ports);
# elif defined(__i386__)
# define get_ioperm(ports) i386_get_ioperm(ports)
# define set_ioperm(ports) i386_set_ioperm(ports)
int i386_get_ioperm(unsigned long * ports);
int i386_set_ioperm(unsigned long * ports);
# endif
#else
# include <sys/io.h>
#endif
/* constants */
#define D0 0x01
#define D1 0x02
#define D2 0x04
#define D3 0x08
#define D4 0x10
#define D5 0x20
#define D6 0x40
#define D7 0x80
/* PSX */
#define PSX_CLOCK D2
#define PSX_COMMAND D0
#define PSX_DELAY 60
#define PSX_ID(x) ((x) >> 4)
#define PSX_LENGTH(x) ((x) & 0x0f)
#define PSX_SELECT D1
#define PSX_POWER (D3 | D4 | D5 | D6 | D7)
/* prototypes */
/* assembly routines */
extern unsigned char inb(unsigned short int port);
extern void outb(unsigned short int port, unsigned char value);
/* useful */
static void _udelay(int delay);
static int _psx_command(unsigned short int port, unsigned char * pads, int b);
static unsigned char _read_data(unsigned short int port);
static unsigned char _read_status(unsigned short int port);
static void _write_control(unsigned short int port, unsigned char value);
static void _write_data(unsigned short int port, unsigned char value);
static int _error(char const * message, int ret);
static int _usage(void);
/* functions */
static void _udelay(int delay)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = delay * 1000;
nanosleep(&ts, NULL);
}
static int _psx_command(unsigned short int port, unsigned char * pads, int b)
{
size_t i;
int cmd;
int data = 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(0x%x, 0x%x)\n", __func__, port, b);
#endif
for(i = 0; i < 8; i++)
{
cmd = (b & 1) ? PSX_COMMAND : 0;
_write_data(port, cmd | PSX_POWER);
_udelay(PSX_DELAY);
data |= (_read_status(port) ^ 0x80) & pads[7] ? (1 << i) : 0;
#ifdef DEBUG
fprintf(stderr, "DEBUG: data=0x%02x\n", data);
#endif
_write_data(port, cmd | PSX_CLOCK | PSX_POWER);
_udelay(PSX_DELAY);
b >>= 1;
}
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(0x%x, 0x%x) => 0x%04x\n", __func__, port, b,
data);
#endif
return data;
}
static unsigned char _read_data(unsigned short int port)
{
unsigned char ret;
ret = inb(port);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(0x%x) => 0x%02x\n", __func__, port, ret);
#endif
return ret;
}
static unsigned char _read_status(unsigned short int port)
{
unsigned char ret;
ret = inb(port + 0x01);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(0x%x) => 0x%02x, 0x%02x\n", __func__, port,
ret, ret ^ 0x80);
#endif
return ret;
}
static int _psx_read_packet(unsigned short int port, unsigned char * pads,
unsigned char * data)
{
int i;
int id;
_write_data(port, PSX_CLOCK | PSX_SELECT | PSX_POWER);
_udelay(PSX_DELAY * 2);
_write_data(port, PSX_CLOCK | PSX_POWER);
_udelay(PSX_DELAY * 2);
_psx_command(port, pads, 0x01);
id = _psx_command(port, pads, 0x42);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() id=0x%02x\n", __func__, id);
#endif
if(_psx_command(port, pads, 0) == 0x5a)
for(i = 0; i < PSX_LENGTH(id) * 2; i++)
data[i] = _psx_command(port, pads, 0x00);
else
id = 0;
_write_data(port, PSX_CLOCK | PSX_SELECT | PSX_POWER);
return PSX_ID(id);
}
static void _write_control(unsigned short int port, unsigned char value)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(0x%x, 0x%02x)\n", __func__, port, value);
#endif
/* FIXME verify if the value is tolerated */
outb(port + 0x02, value);
}
static void _write_data(unsigned short int port, unsigned char value)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(0x%x, 0x%02x)\n", __func__, port, value);
#endif
outb(port, value);
}
/* error */
static int _error(char const * message, int ret)
{
perror(message);
return ret;
}
/* usage */
static int _usage(void)
{
fputs("Usage: psx -p port\n", stderr);
return 1;
}
/* main */
int main(int argc, char * argv[])
{
int o;
int port = -1;
/* unsigned char c; */
unsigned char pads[8];
int i;
unsigned char data[32];
int id;
int config[6] = { 0, 7, 0, 0, 0, 0 };
int gc_status_bits[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
while((o = getopt(argc, argv, "p:")) != -1)
switch(o)
{
case 'p':
port = strtoul(optarg, NULL, 0);
break;
default:
return _usage();
}
if(port < 0)
return _usage();
if(x86_64_iopl(2) != 0)
return _error("iopl", 2);
printf("Port: 0x%x\n", port);
_write_control(port, 0x04);
memset(&pads, 0, sizeof(pads));
for(i = 0; i < 5; i++)
{
if(!config[i + 1])
continue;
if(config[i + 1] < 1 || config[i + 1] > 7)
exit(2);
pads[0] |= gc_status_bits[i];
pads[config[i + 1]] |= gc_status_bits[i];
if(pads[7])
{
id = _psx_read_packet(port, pads, data);
printf("ID: 0x%x\n", id);
}
}
/* exit */
_write_control(port, 0x00);
return 0;
}
/* ioperm */
#ifdef __NetBSD__
int ioperm(unsigned long from, unsigned long num, int permit)
{
unsigned long ports[32];
unsigned long i;
if(get_ioperm(ports) == -1)
return -1;
for(i = from; i < (from + num); i++)
if(permit)
ports[i / 32] &= ~(1 << (i % 32));
else
ports[i / 32] |= (1 << (i % 32));
if(set_ioperm(ports) == -1)
return -1;
return 0;
}
#endif
>Fix:
Index: lib/libarch/x86_64/Makefile.inc
===================================================================
RCS file: /cvsroot/src/lib/libarch/x86_64/Makefile.inc,v
retrieving revision 1.3
diff -p -u -r1.3 Makefile.inc
--- lib/libarch/x86_64/Makefile.inc 26 Oct 2008 07:05:33 -0000 1.3
+++ lib/libarch/x86_64/Makefile.inc 30 Jul 2010 23:35:56 -0000
@@ -3,7 +3,7 @@
LD32DIR?= none
.if (${MACHINE_ARCH} == "x86_64" && ${LD32DIR} != "i386")
-SRCS+= x86_64_mtrr.c x86_64_iopl.c
+SRCS+= x86_64_mtrr.c x86_64_iopl.c x86_64_get_ioperm.c x86_64_set_ioperm.c
.endif
MAN+= x86_64_get_mtrr.2 x86_64_iopl.2
Index: sys/arch/amd64/amd64/genassym.cf
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/amd64/genassym.cf,v
retrieving revision 1.37.4.2
diff -p -u -r1.37.4.2 genassym.cf
--- sys/arch/amd64/amd64/genassym.cf 2 Feb 2009 03:22:55 -0000 1.37.4.2
+++ sys/arch/amd64/amd64/genassym.cf 30 Jul 2010 23:36:05 -0000
@@ -146,6 +146,8 @@ define L1_SLOT_KERNBASE pl1_pi(KERNBASE)
define PDIR_SLOT_PTE PDIR_SLOT_PTE
+define IOMAPSIZE IOMAPSIZE
+
define VM_MAXUSER_ADDRESS (unsigned long long)VM_MAXUSER_ADDRESS
define L_ADDR offsetof(struct lwp, l_addr)
@@ -189,6 +191,7 @@ define PCB_RSP0 offsetof(struct pcb, pc
define PCB_CR0 offsetof(struct pcb, pcb_cr0)
define PCB_ONFAULT offsetof(struct pcb, pcb_onfault)
define PCB_FPCPU offsetof(struct pcb, pcb_fpcpu)
+define PCB_IOMAP offsetof(struct pcb, pcb_iomap)
define TF_RDI offsetof(struct trapframe, tf_rdi)
define TF_RSI offsetof(struct trapframe, tf_rsi)
@@ -237,7 +240,11 @@ define CPU_INFO_CURLDT offsetof(struct
define CPU_INFO_IDLELWP offsetof(struct cpu_info, ci_data.cpu_idlelwp)
define CPU_INFO_PMAP offsetof(struct cpu_info, ci_pmap)
define CPU_INFO_CPUMASK offsetof(struct cpu_info, ci_cpumask)
+define CPU_INFO_TSS offsetof(struct cpu_info, ci_tss)
define CPU_INFO_RSP0 offsetof(struct cpu_info, ci_tss.tss_rsp0)
+define CPU_INFO_IOBASE offsetof(struct cpu_info, ci_tss.tss_iobase)
+define CPU_INFO_IOMAP offsetof(struct cpu_info, ci_iomap)
+define IOMAP_INVALOFF IOMAP_INVALOFF
define CPU_INFO_NSYSCALL offsetof(struct cpu_info, ci_data.cpu_nsyscall)
define CPU_INFO_NTRAP offsetof(struct cpu_info, ci_data.cpu_ntrap)
define CPU_INFO_CURPRIORITY offsetof(struct cpu_info,
ci_schedstate.spc_curpriority)
Index: sys/arch/amd64/amd64/locore.S
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/amd64/locore.S,v
retrieving revision 1.47.8.4
diff -p -u -r1.47.8.4 locore.S
--- sys/arch/amd64/amd64/locore.S 22 Apr 2010 20:02:48 -0000 1.47.8.4
+++ sys/arch/amd64/amd64/locore.S 30 Jul 2010 23:36:05 -0000
@@ -955,6 +955,13 @@ ENTRY(cpu_switchto)
testl $LW_SYSTEM,L_FLAG(%r12)
jnz 4f
+ /* Switch I/O bitmap */
+ movq PCB_IOMAP(%rbx),%rax
+/* orq %rax,%rax */
+ jnz,pn .Lcopy_iobitmap
+ movl $(IOMAP_INVALOFF << 16),CPUVAR(IOBASE)
+.Liobitmap_done:
+
/* Is this process using RAS (restartable atomic sequences)? */
movq L_PROC(%r12),%rdi
cmpq $0,P_RASLIST(%rdi)
@@ -1003,6 +1010,21 @@ ENTRY(cpu_switchto)
movq %rax,TF_RIP(%rbx)
jmp 2b
+.Lcopy_iobitmap:
+ /* Copy I/O bitmap. */
+ movq $(IOMAPSIZE/8),%rcx
+ pushq %rsi
+ pushq %rdi
+ movq %rax,%rsi /* pcb_iomap */
+ movq CPUVAR(SELF),%rdi
+ leaq CPU_INFO_IOMAP(%rdi),%rdi
+ rep
+ movsq
+ popq %rdi
+ popq %rsi
+ movq $((CPU_INFO_IOMAP - CPU_INFO_TSS) << 16),CPUVAR(IOBASE)
+ jmp .Liobitmap_done
+
/*
* void savectx(struct pcb *pcb);
*
Index: sys/arch/amd64/amd64/vm_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/amd64/Attic/vm_machdep.c,v
retrieving revision 1.34.4.1
diff -p -u -r1.34.4.1 vm_machdep.c
--- sys/arch/amd64/amd64/vm_machdep.c 16 Feb 2009 03:04:38 -0000 1.34.4.1
+++ sys/arch/amd64/amd64/vm_machdep.c 30 Jul 2010 23:36:05 -0000
@@ -179,6 +179,8 @@ cpu_lwp_fork(struct lwp *l1, struct lwp
pcb->pcb_rsp0 = (USER_TO_UAREA(l2->l_addr) + KSTACK_SIZE - 16) & ~0xf;
+ pcb->pcb_iomap = NULL;
+
/*
* Copy the trapframe.
*/
Index: sys/arch/amd64/include/pcb.h
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/include/pcb.h,v
retrieving revision 1.15
diff -p -u -r1.15 pcb.h
--- sys/arch/amd64/include/pcb.h 26 Oct 2008 00:08:15 -0000 1.15
+++ sys/arch/amd64/include/pcb.h 30 Jul 2010 23:36:06 -0000
@@ -104,6 +104,7 @@ struct pcb {
uint64_t pcb_gs;
uint64_t pcb_fs;
int pcb_iopl;
+ char *pcb_iomap; /* I/O permission bitmap */
};
/*
Index: sys/arch/x86/x86/sys_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/x86/sys_machdep.c,v
retrieving revision 1.15.8.1
diff -p -u -r1.15.8.1 sys_machdep.c
--- sys/arch/x86/x86/sys_machdep.c 4 Apr 2009 17:39:09 -0000 1.15.8.1
+++ sys/arch/x86/x86/sys_machdep.c 30 Jul 2010 23:36:15 -0000
@@ -71,7 +71,11 @@ __KERNEL_RCSID(0, "$NetBSD: sys_machdep.
#undef USER_LDT
#undef PERFCTRS
#undef VM86
-#undef IOPERM
+#if 0 /* khorben */
+# undef IOPERM
+#else
+# define IOPERM
+#endif
#else
#if defined(XEN)
#undef IOPERM
Home |
Main Index |
Thread Index |
Old Index