Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
xcap
xcap-capability-linux
Commits
2600990e
Commit
2600990e
authored
Apr 05, 2006
by
Ralf Baechle
Browse files
[MIPS] kpsd and other AP/SP improvements.
Signed-off-by:
Ralf Baechle
<
ralf@linux-mips.org
>
parent
bce1a286
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
arch/mips/Kconfig
View file @
2600990e
...
...
@@ -1476,6 +1476,16 @@ config MIPS_VPE_APSP_API
depends on MIPS_VPE_LOADER
help
config MIPS_APSP_KSPD
bool "Enable KSPD"
depends on MIPS_VPE_APSP_API
default y
help
KSPD is a kernel daemon that accepts syscall requests from the SP
side, actions them and returns the results. It also handles the
"exit" syscall notifying other kernel modules the SP program is
exiting. You probably want to say yes here.
config SB1_PASS_1_WORKAROUNDS
bool
depends on CPU_SB1_PASS_1
...
...
arch/mips/kernel/Makefile
View file @
2600990e
...
...
@@ -36,6 +36,7 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_MIPS_MT_SMP)
+=
smp_mt.o
obj-$(CONFIG_MIPS_APSP_KSPD)
+=
kspd.o
obj-$(CONFIG_MIPS_VPE_LOADER)
+=
vpe.o
obj-$(CONFIG_MIPS_VPE_APSP_API)
+=
rtlx.o
...
...
arch/mips/kernel/kspd.c
0 → 100644
View file @
2600990e
/*
* Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved.
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
*/
#include
<linux/kernel.h>
#include
<linux/module.h>
#include
<linux/unistd.h>
#include
<linux/file.h>
#include
<linux/fs.h>
#include
<linux/syscalls.h>
#include
<linux/workqueue.h>
#include
<linux/errno.h>
#include
<linux/list.h>
#include
<asm/vpe.h>
#include
<asm/rtlx.h>
#include
<asm/kspd.h>
static
struct
workqueue_struct
*
workqueue
=
NULL
;
static
struct
work_struct
work
;
extern
unsigned
long
cpu_khz
;
struct
mtsp_syscall
{
int
cmd
;
unsigned
char
abi
;
unsigned
char
size
;
};
struct
mtsp_syscall_ret
{
int
retval
;
int
errno
;
};
struct
mtsp_syscall_generic
{
int
arg0
;
int
arg1
;
int
arg2
;
int
arg3
;
int
arg4
;
int
arg5
;
int
arg6
;
};
static
struct
list_head
kspd_notifylist
;
static
int
sp_stopping
=
0
;
/* these should match with those in the SDE kit */
#define MTSP_SYSCALL_BASE 0
#define MTSP_SYSCALL_EXIT (MTSP_SYSCALL_BASE + 0)
#define MTSP_SYSCALL_OPEN (MTSP_SYSCALL_BASE + 1)
#define MTSP_SYSCALL_READ (MTSP_SYSCALL_BASE + 2)
#define MTSP_SYSCALL_WRITE (MTSP_SYSCALL_BASE + 3)
#define MTSP_SYSCALL_CLOSE (MTSP_SYSCALL_BASE + 4)
#define MTSP_SYSCALL_LSEEK32 (MTSP_SYSCALL_BASE + 5)
#define MTSP_SYSCALL_ISATTY (MTSP_SYSCALL_BASE + 6)
#define MTSP_SYSCALL_GETTIME (MTSP_SYSCALL_BASE + 7)
#define MTSP_SYSCALL_PIPEFREQ (MTSP_SYSCALL_BASE + 8)
#define MTSP_SYSCALL_GETTOD (MTSP_SYSCALL_BASE + 9)
#define MTSP_O_RDONLY 0x0000
#define MTSP_O_WRONLY 0x0001
#define MTSP_O_RDWR 0x0002
#define MTSP_O_NONBLOCK 0x0004
#define MTSP_O_APPEND 0x0008
#define MTSP_O_SHLOCK 0x0010
#define MTSP_O_EXLOCK 0x0020
#define MTSP_O_ASYNC 0x0040
#define MTSP_O_FSYNC O_SYNC
#define MTSP_O_NOFOLLOW 0x0100
#define MTSP_O_SYNC 0x0080
#define MTSP_O_CREAT 0x0200
#define MTSP_O_TRUNC 0x0400
#define MTSP_O_EXCL 0x0800
#define MTSP_O_BINARY 0x8000
#define SP_VPE 1
struct
apsp_table
{
int
sp
;
int
ap
;
};
/* we might want to do the mode flags too */
struct
apsp_table
open_flags_table
[]
=
{
{
MTSP_O_RDWR
,
O_RDWR
},
{
MTSP_O_WRONLY
,
O_WRONLY
},
{
MTSP_O_CREAT
,
O_CREAT
},
{
MTSP_O_TRUNC
,
O_TRUNC
},
{
MTSP_O_NONBLOCK
,
O_NONBLOCK
},
{
MTSP_O_APPEND
,
O_APPEND
},
{
MTSP_O_NOFOLLOW
,
O_NOFOLLOW
}
};
struct
apsp_table
syscall_command_table
[]
=
{
{
MTSP_SYSCALL_OPEN
,
__NR_open
},
{
MTSP_SYSCALL_CLOSE
,
__NR_close
},
{
MTSP_SYSCALL_READ
,
__NR_read
},
{
MTSP_SYSCALL_WRITE
,
__NR_write
},
{
MTSP_SYSCALL_LSEEK32
,
__NR_lseek
}
};
static
int
sp_syscall
(
int
num
,
int
arg0
,
int
arg1
,
int
arg2
,
int
arg3
)
{
register
long
int
_num
__asm__
(
"$2"
)
=
num
;
register
long
int
_arg0
__asm__
(
"$4"
)
=
arg0
;
register
long
int
_arg1
__asm__
(
"$5"
)
=
arg1
;
register
long
int
_arg2
__asm__
(
"$6"
)
=
arg2
;
register
long
int
_arg3
__asm__
(
"$7"
)
=
arg3
;
mm_segment_t
old_fs
;
old_fs
=
get_fs
();
set_fs
(
KERNEL_DS
);
__asm__
__volatile__
(
" syscall
\n
"
:
"=r"
(
_num
),
"=r"
(
_arg3
)
:
"r"
(
_num
),
"r"
(
_arg0
),
"r"
(
_arg1
),
"r"
(
_arg2
),
"r"
(
_arg3
));
set_fs
(
old_fs
);
/* $a3 is error flag */
if
(
_arg3
)
return
-
_num
;
return
_num
;
}
static
int
translate_syscall_command
(
int
cmd
)
{
int
i
;
int
ret
=
-
1
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
syscall_command_table
);
i
++
)
{
if
((
cmd
==
syscall_command_table
[
i
].
sp
))
return
syscall_command_table
[
i
].
ap
;
}
return
ret
;
}
static
unsigned
int
translate_open_flags
(
int
flags
)
{
int
i
;
unsigned
int
ret
=
0
;
for
(
i
=
0
;
i
<
(
sizeof
(
open_flags_table
)
/
sizeof
(
struct
apsp_table
));
i
++
)
{
if
(
(
flags
&
open_flags_table
[
i
].
sp
)
)
{
ret
|=
open_flags_table
[
i
].
ap
;
}
}
return
ret
;
}
static
void
sp_setfsuidgid
(
uid_t
uid
,
gid_t
gid
)
{
current
->
fsuid
=
uid
;
current
->
fsgid
=
gid
;
key_fsuid_changed
(
current
);
key_fsgid_changed
(
current
);
}
/*
* Expects a request to be on the sysio channel. Reads it. Decides whether
* its a linux syscall and runs it, or whatever. Puts the return code back
* into the request and sends the whole thing back.
*/
void
sp_work_handle_request
(
void
)
{
struct
mtsp_syscall
sc
;
struct
mtsp_syscall_generic
generic
;
struct
mtsp_syscall_ret
ret
;
struct
kspd_notifications
*
n
;
struct
timeval
tv
;
struct
timezone
tz
;
int
cmd
;
char
*
vcwd
;
mm_segment_t
old_fs
;
int
size
;
ret
.
retval
=
-
1
;
if
(
!
rtlx_read
(
RTLX_CHANNEL_SYSIO
,
&
sc
,
sizeof
(
struct
mtsp_syscall
),
0
))
{
printk
(
KERN_ERR
"Expected request but nothing to read
\n
"
);
return
;
}
size
=
sc
.
size
;
if
(
size
)
{
if
(
!
rtlx_read
(
RTLX_CHANNEL_SYSIO
,
&
generic
,
size
,
0
))
{
printk
(
KERN_ERR
"Expected request but nothing to read
\n
"
);
return
;
}
}
/* Run the syscall at the priviledge of the user who loaded the
SP program */
if
(
vpe_getuid
(
SP_VPE
))
sp_setfsuidgid
(
vpe_getuid
(
SP_VPE
),
vpe_getgid
(
SP_VPE
));
switch
(
sc
.
cmd
)
{
/* needs the flags argument translating from SDE kit to
linux */
case
MTSP_SYSCALL_PIPEFREQ
:
ret
.
retval
=
cpu_khz
*
1000
;
ret
.
errno
=
0
;
break
;
case
MTSP_SYSCALL_GETTOD
:
memset
(
&
tz
,
0
,
sizeof
(
tz
));
if
((
ret
.
retval
=
sp_syscall
(
__NR_gettimeofday
,
(
int
)
&
tv
,
(
int
)
&
tz
,
0
,
0
))
==
0
)
ret
.
retval
=
tv
.
tv_sec
;
ret
.
errno
=
errno
;
break
;
case
MTSP_SYSCALL_EXIT
:
list_for_each_entry
(
n
,
&
kspd_notifylist
,
list
)
n
->
kspd_sp_exit
(
SP_VPE
);
sp_stopping
=
1
;
printk
(
KERN_DEBUG
"KSPD got exit syscall from SP exitcode %d
\n
"
,
generic
.
arg0
);
break
;
case
MTSP_SYSCALL_OPEN
:
generic
.
arg1
=
translate_open_flags
(
generic
.
arg1
);
vcwd
=
vpe_getcwd
(
SP_VPE
);
/* change to the cwd of the process that loaded the SP program */
old_fs
=
get_fs
();
set_fs
(
KERNEL_DS
);
sys_chdir
(
vcwd
);
set_fs
(
old_fs
);
sc
.
cmd
=
__NR_open
;
/* fall through */
default:
if
((
sc
.
cmd
>=
__NR_Linux
)
&&
(
sc
.
cmd
<=
(
__NR_Linux
+
__NR_Linux_syscalls
))
)
cmd
=
sc
.
cmd
;
else
cmd
=
translate_syscall_command
(
sc
.
cmd
);
if
(
cmd
>=
0
)
{
ret
.
retval
=
sp_syscall
(
cmd
,
generic
.
arg0
,
generic
.
arg1
,
generic
.
arg2
,
generic
.
arg3
);
ret
.
errno
=
errno
;
}
else
printk
(
KERN_WARNING
"KSPD: Unknown SP syscall number %d
\n
"
,
sc
.
cmd
);
break
;
}
/* switch */
if
(
vpe_getuid
(
SP_VPE
))
sp_setfsuidgid
(
0
,
0
);
if
((
rtlx_write
(
RTLX_CHANNEL_SYSIO
,
&
ret
,
sizeof
(
struct
mtsp_syscall_ret
),
0
))
<
sizeof
(
struct
mtsp_syscall_ret
))
printk
(
"KSPD: sp_work_handle_request failed to send to SP
\n
"
);
}
static
void
sp_cleanup
(
void
)
{
struct
files_struct
*
files
=
current
->
files
;
int
i
,
j
;
struct
fdtable
*
fdt
;
j
=
0
;
/*
* It is safe to dereference the fd table without RCU or
* ->file_lock
*/
fdt
=
files_fdtable
(
files
);
for
(;;)
{
unsigned
long
set
;
i
=
j
*
__NFDBITS
;
if
(
i
>=
fdt
->
max_fdset
||
i
>=
fdt
->
max_fds
)
break
;
set
=
fdt
->
open_fds
->
fds_bits
[
j
++
];
while
(
set
)
{
if
(
set
&
1
)
{
struct
file
*
file
=
xchg
(
&
fdt
->
fd
[
i
],
NULL
);
if
(
file
)
filp_close
(
file
,
files
);
}
i
++
;
set
>>=
1
;
}
}
}
static
int
channel_open
=
0
;
/* the work handler */
static
void
sp_work
(
void
*
data
)
{
if
(
!
channel_open
)
{
if
(
rtlx_open
(
RTLX_CHANNEL_SYSIO
,
1
)
!=
0
)
{
printk
(
"KSPD: unable to open sp channel
\n
"
);
sp_stopping
=
1
;
}
else
{
channel_open
++
;
printk
(
KERN_DEBUG
"KSPD: SP channel opened
\n
"
);
}
}
else
{
/* wait for some data, allow it to sleep */
rtlx_read_poll
(
RTLX_CHANNEL_SYSIO
,
1
);
/* Check we haven't been woken because we are stopping */
if
(
!
sp_stopping
)
sp_work_handle_request
();
}
if
(
!
sp_stopping
)
queue_work
(
workqueue
,
&
work
);
else
sp_cleanup
();
}
static
void
startwork
(
int
vpe
)
{
sp_stopping
=
channel_open
=
0
;
if
(
workqueue
==
NULL
)
{
if
((
workqueue
=
create_singlethread_workqueue
(
"kspd"
))
==
NULL
)
{
printk
(
KERN_ERR
"unable to start kspd
\n
"
);
return
;
}
INIT_WORK
(
&
work
,
sp_work
,
NULL
);
queue_work
(
workqueue
,
&
work
);
}
else
queue_work
(
workqueue
,
&
work
);
}
static
void
stopwork
(
int
vpe
)
{
sp_stopping
=
1
;
printk
(
KERN_DEBUG
"KSPD: SP stopping
\n
"
);
}
void
kspd_notify
(
struct
kspd_notifications
*
notify
)
{
list_add
(
&
notify
->
list
,
&
kspd_notifylist
);
}
static
struct
vpe_notifications
notify
;
static
int
kspd_module_init
(
void
)
{
INIT_LIST_HEAD
(
&
kspd_notifylist
);
notify
.
start
=
startwork
;
notify
.
stop
=
stopwork
;
vpe_notify
(
SP_VPE
,
&
notify
);
return
0
;
}
static
void
kspd_module_exit
(
void
)
{
}
module_init
(
kspd_module_init
);
module_exit
(
kspd_module_exit
);
MODULE_DESCRIPTION
(
"MIPS KSPD"
);
MODULE_AUTHOR
(
"Elizabeth Oldham, MIPS Technologies, Inc."
);
MODULE_LICENSE
(
"GPL"
);
arch/mips/kernel/rtlx.c
View file @
2600990e
...
...
@@ -21,45 +21,44 @@
#include
<linux/module.h>
#include
<linux/fs.h>
#include
<linux/init.h>
#include
<asm/uaccess.h>
#include
<linux/slab.h>
#include
<linux/list.h>
#include
<linux/vmalloc.h>
#include
<linux/elf.h>
#include
<linux/seq_file.h>
#include
<linux/syscalls.h>
#include
<linux/moduleloader.h>
#include
<linux/interrupt.h>
#include
<linux/irq.h>
#include
<linux/poll.h>
#include
<linux/sched.h>
#include
<linux/wait.h>
#include
<asm/mipsmtregs.h>
#include
<asm/bitops.h>
#include
<asm/cacheflush.h>
#include
<asm/atomic.h>
#include
<asm/cpu.h>
#include
<asm/processor.h>
#include
<asm/system.h>
#include
<asm/vpe.h>
#include
<asm/rtlx.h>
#include
<asm/uaccess.h>
#define RTLX_TARG_VPE 1
static
struct
rtlx_info
*
rtlx
;
static
int
major
;
static
char
module_name
[]
=
"rtlx"
;
static
struct
irqaction
irq
;
static
int
irq_num
;
static
inline
int
spacefree
(
int
read
,
int
write
,
int
size
)
{
if
(
read
==
write
)
{
/*
* never fill the buffer completely, so indexes are always
* equal if empty and only empty, or !equal if data available
*/
return
size
-
1
;
}
return
((
read
+
size
-
write
)
%
size
)
-
1
;
}
static
struct
chan_waitqueues
{
wait_queue_head_t
rt_queue
;
wait_queue_head_t
lx_queue
;
int
in_open
;
}
channel_wqs
[
RTLX_CHANNELS
];
static
struct
irqaction
irq
;
static
int
irq_num
;
static
struct
vpe_notifications
notify
;
static
int
sp_stopping
=
0
;
extern
void
*
vpe_get_shared
(
int
index
);
static
void
rtlx_dispatch
(
struct
pt_regs
*
regs
)
...
...
@@ -67,174 +66,298 @@ static void rtlx_dispatch(struct pt_regs *regs)
do_IRQ
(
MIPSCPU_INT_BASE
+
MIPS_CPU_RTLX_IRQ
,
regs
);
}
/* Interrupt handler may be called before rtlx_init has otherwise had
a chance to run.
*/
static
irqreturn_t
rtlx_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
int
i
;
for
(
i
=
0
;
i
<
RTLX_CHANNELS
;
i
++
)
{
struct
rtlx_channel
*
chan
=
&
rtlx
->
channel
[
i
];
if
(
chan
->
lx_read
!=
chan
->
lx_write
)
wake_up_interruptible
(
&
channel_wqs
[
i
].
lx_queue
);
wake_up
(
&
channel_wqs
[
i
].
lx_queue
);
wake_up
(
&
channel_wqs
[
i
].
rt_queue
);
}
return
IRQ_HANDLED
;
}
/* call when we have the address of the shared structure from the SP side. */
static
int
rtlx_init
(
struct
rtlx_info
*
rtlxi
)
static
__attribute_used__
void
dump_rtlx
(
void
)
{
int
i
;
if
(
rtlxi
->
id
!=
RTLX_ID
)
{
printk
(
KERN_WARNING
"no valid RTLX id at 0x%p
\n
"
,
rtlxi
);
return
-
ENOEXEC
;
}
printk
(
"id 0x%lx state %d
\n
"
,
rtlx
->
id
,
rtlx
->
state
);
/* initialise the wait queues */
for
(
i
=
0
;
i
<
RTLX_CHANNELS
;
i
++
)
{
init_waitqueue_head
(
&
channel_wqs
[
i
].
rt_queue
);
init_waitqueue_head
(
&
channel_wqs
[
i
].
lx_queue
);
}
struct
rtlx_channel
*
chan
=
&
rtlx
->
channel
[
i
];
/* set up for interrupt handling */
memset
(
&
irq
,
0
,
sizeof
(
struct
irqaction
)
);
printk
(
" rt_state %d lx_state %d buffer_size %d
\n
"
,
chan
->
rt_state
,
chan
->
lx_state
,
chan
->
buffer_size
);
if
(
cpu_has_vint
)
set_vi_handler
(
MIPS_CPU_RTLX_IRQ
,
rtlx_dispatch
);
printk
(
" rt_read %d rt_write %d
\n
"
,
chan
->
rt_read
,
chan
->
rt_write
);
irq_num
=
MIPSCPU_INT_BASE
+
MIPS_CPU_RTLX_IRQ
;
irq
.
handler
=
rtlx_interrupt
;
irq
.
flags
=
SA_INTERRUPT
;
irq
.
name
=
"RTLX"
;
irq
.
dev_id
=
rtlx
;
setup_irq
(
irq_num
,
&
irq
);
printk
(
" lx_read %d lx_write %d
\n
"
,
chan
->
lx_read
,
chan
->
lx_write
);
printk
(
" rt_buffer <%s>
\n
"
,
chan
->
rt_buffer
);
printk
(
" lx_buffer <%s>
\n
"
,
chan
->
lx_buffer
);
}
}
/* call when we have the address of the shared structure from the SP side. */
static
int
rtlx_init
(
struct
rtlx_info
*
rtlxi
)
{
if
(
rtlxi
->
id
!=
RTLX_ID
)
{
printk
(
KERN_ERR
"no valid RTLX id at 0x%p 0x%x
\n
"
,
rtlxi
,
rtlxi
->
id
);
return
-
ENOEXEC
;
}
rtlx
=
rtlxi
;
return
0
;
}
/*
only allow one open process at a time to open each channel
*/
static
int
rtlx_open
(
struct
inode
*
inode
,
struct
file
*
filp
)
/*
notifications
*/
static
void
starting
(
int
vpe
)
{
int
minor
,
ret
;
int
i
;
sp_stopping
=
0
;
/* force a reload of rtlx */
rtlx
=
NULL
;
/* wake up any sleeping rtlx_open's */
for
(
i
=
0
;
i
<
RTLX_CHANNELS
;
i
++
)
wake_up_interruptible
(
&
channel_wqs
[
i
].
lx_queue
);
}
static
void
stopping
(
int
vpe
)
{
int
i
;
sp_stopping
=
1
;
for
(
i
=
0
;
i
<
RTLX_CHANNELS
;
i
++
)
wake_up_interruptible
(
&
channel_wqs
[
i
].
lx_queue
);
}
int
rtlx_open
(
int
index
,
int
can_sleep
)
{
int
ret
;
struct
rtlx_channel
*
chan
;
volatile
struct
rtlx_info
**
p
;
/* assume only 1 device at the mo. */
minor
=
MINOR
(
inode
->
i_rdev
);
if
(
index
>=
RTLX_CHANNELS
)
{
printk
(
KERN_DEBUG
"rtlx_open index out of range
\n
"
);
return
-
ENOSYS
;
}
if
(
channel_wqs
[
index
].
in_open
)
{
printk
(
KERN_DEBUG
"rtlx_open channel %d already opened
\n
"
,
index
);
return
-
EBUSY
;
}
channel_wqs
[
index
].
in_open
++
;
if
(
rtlx
==
NULL
)
{
struct
rtlx_info
**
p
;
if
(
(
p
=
vpe_get_shared
(
RTLX_TARG_VPE
))
==
NULL
)
{
printk
(
KERN_ERR
"vpe_get_shared is NULL. "
"Has an SP program been loaded?
\n
"
);
return
-
EFAULT
;
if
(
can_sleep
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
/* go to sleep */
add_wait_queue
(
&
channel_wqs
[
index
].
lx_queue
,
&
wait
);