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