Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1/*
  2 * Copyright (C) 2005 MIPS Technologies, Inc.  All rights reserved.
  3 *
  4 *  This program is free software; you can distribute it and/or modify it
  5 *  under the terms of the GNU General Public License (Version 2) as
  6 *  published by the Free Software Foundation.
  7 *
  8 *  This program is distributed in the hope it will be useful, but WITHOUT
  9 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 10 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 11 *  for more details.
 12 *
 13 *  You should have received a copy of the GNU General Public License along
 14 *  with this program; if not, write to the Free Software Foundation, Inc.,
 15 *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
 16 *
 17 */
 18#include <linux/kernel.h>
 19#include <linux/module.h>
 20#include <linux/sched.h>
 21#include <linux/unistd.h>
 22#include <linux/file.h>
 23#include <linux/fdtable.h>
 24#include <linux/fs.h>
 25#include <linux/syscalls.h>
 26#include <linux/workqueue.h>
 27#include <linux/errno.h>
 28#include <linux/list.h>
 29
 30#include <asm/vpe.h>
 31#include <asm/rtlx.h>
 32#include <asm/kspd.h>
 33
 34static struct workqueue_struct *workqueue;
 35static struct work_struct work;
 36
 37extern unsigned long cpu_khz;
 38
 39struct mtsp_syscall {
 40	int cmd;
 41	unsigned char abi;
 42	unsigned char size;
 43};
 44
 45struct mtsp_syscall_ret {
 46	int retval;
 47	int errno;
 48};
 49
 50struct mtsp_syscall_generic {
 51	int arg0;
 52	int arg1;
 53	int arg2;
 54	int arg3;
 55	int arg4;
 56	int arg5;
 57	int arg6;
 58};
 59
 60static struct list_head kspd_notifylist;
 61static int sp_stopping;
 62
 63/* these should match with those in the SDE kit */
 64#define MTSP_SYSCALL_BASE	0
 65#define MTSP_SYSCALL_EXIT	(MTSP_SYSCALL_BASE + 0)
 66#define MTSP_SYSCALL_OPEN	(MTSP_SYSCALL_BASE + 1)
 67#define MTSP_SYSCALL_READ	(MTSP_SYSCALL_BASE + 2)
 68#define MTSP_SYSCALL_WRITE	(MTSP_SYSCALL_BASE + 3)
 69#define MTSP_SYSCALL_CLOSE	(MTSP_SYSCALL_BASE + 4)
 70#define MTSP_SYSCALL_LSEEK32	(MTSP_SYSCALL_BASE + 5)
 71#define MTSP_SYSCALL_ISATTY	(MTSP_SYSCALL_BASE + 6)
 72#define MTSP_SYSCALL_GETTIME	(MTSP_SYSCALL_BASE + 7)
 73#define MTSP_SYSCALL_PIPEFREQ	(MTSP_SYSCALL_BASE + 8)
 74#define MTSP_SYSCALL_GETTOD	(MTSP_SYSCALL_BASE + 9)
 75#define MTSP_SYSCALL_IOCTL     (MTSP_SYSCALL_BASE + 10)
 76
 77#define MTSP_O_RDONLY		0x0000
 78#define MTSP_O_WRONLY		0x0001
 79#define MTSP_O_RDWR		0x0002
 80#define MTSP_O_NONBLOCK		0x0004
 81#define MTSP_O_APPEND		0x0008
 82#define MTSP_O_SHLOCK		0x0010
 83#define MTSP_O_EXLOCK		0x0020
 84#define MTSP_O_ASYNC		0x0040
 85/* XXX: check which of these is actually O_SYNC vs O_DSYNC */
 86#define MTSP_O_FSYNC		O_SYNC
 87#define MTSP_O_NOFOLLOW		0x0100
 88#define MTSP_O_SYNC		0x0080
 89#define MTSP_O_CREAT		0x0200
 90#define MTSP_O_TRUNC		0x0400
 91#define MTSP_O_EXCL		0x0800
 92#define MTSP_O_BINARY		0x8000
 93
 94extern int tclimit;
 95
 96struct apsp_table  {
 97	int sp;
 98	int ap;
 99};
100
101/* we might want to do the mode flags too */
102struct apsp_table open_flags_table[] = {
103	{ MTSP_O_RDWR, O_RDWR },
104	{ MTSP_O_WRONLY, O_WRONLY },
105	{ MTSP_O_CREAT, O_CREAT },
106	{ MTSP_O_TRUNC, O_TRUNC },
107	{ MTSP_O_NONBLOCK, O_NONBLOCK },
108	{ MTSP_O_APPEND, O_APPEND },
109	{ MTSP_O_NOFOLLOW, O_NOFOLLOW }
110};
111
112struct apsp_table syscall_command_table[] = {
113	{ MTSP_SYSCALL_OPEN, __NR_open },
114	{ MTSP_SYSCALL_CLOSE, __NR_close },
115	{ MTSP_SYSCALL_READ, __NR_read },
116	{ MTSP_SYSCALL_WRITE, __NR_write },
117	{ MTSP_SYSCALL_LSEEK32, __NR_lseek },
118	{ MTSP_SYSCALL_IOCTL, __NR_ioctl }
119};
120
121static int sp_syscall(int num, int arg0, int arg1, int arg2, int arg3)
122{
123	register long int _num  __asm__("$2") = num;
124	register long int _arg0  __asm__("$4") = arg0;
125	register long int _arg1  __asm__("$5") = arg1;
126	register long int _arg2  __asm__("$6") = arg2;
127	register long int _arg3  __asm__("$7") = arg3;
128
129	mm_segment_t old_fs;
130
131	old_fs = get_fs();
132 	set_fs(KERNEL_DS);
133
134  	__asm__ __volatile__ (
135 	"	syscall					\n"
136 	: "=r" (_num), "=r" (_arg3)
137 	: "r" (_num), "r" (_arg0), "r" (_arg1), "r" (_arg2), "r" (_arg3));
138
139	set_fs(old_fs);
140
141	/* $a3 is error flag */
142	if (_arg3)
143		return -_num;
144
145	return _num;
146}
147
148static int translate_syscall_command(int cmd)
149{
150	int i;
151	int ret = -1;
152
153	for (i = 0; i < ARRAY_SIZE(syscall_command_table); i++) {
154		if ((cmd == syscall_command_table[i].sp))
155			return syscall_command_table[i].ap;
156	}
157
158	return ret;
159}
160
161static unsigned int translate_open_flags(int flags)
162{
163	int i;
164	unsigned int ret = 0;
165
166	for (i = 0; i < ARRAY_SIZE(open_flags_table); i++) {
167		if( (flags & open_flags_table[i].sp) ) {
168			ret |= open_flags_table[i].ap;
169		}
170	}
171
172	return ret;
173}
174
175
176static int sp_setfsuidgid(uid_t uid, gid_t gid)
177{
178	struct cred *new;
179
180	new = prepare_creds();
181	if (!new)
182		return -ENOMEM;
183
184	new->fsuid = uid;
185	new->fsgid = gid;
186
187	commit_creds(new);
188
189	return 0;
190}
191
192/*
193 * Expects a request to be on the sysio channel. Reads it.  Decides whether
194 * its a linux syscall and runs it, or whatever.  Puts the return code back
195 * into the request and sends the whole thing back.
196 */
197void sp_work_handle_request(void)
198{
199	struct mtsp_syscall sc;
200	struct mtsp_syscall_generic generic;
201	struct mtsp_syscall_ret ret;
202	struct kspd_notifications *n;
203	unsigned long written;
204	mm_segment_t old_fs;
205	struct timeval tv;
206	struct timezone tz;
207	int err, cmd;
208
209	char *vcwd;
210	int size;
211
212	ret.retval = -1;
213
214	old_fs = get_fs();
215	set_fs(KERNEL_DS);
216
217	if (!rtlx_read(RTLX_CHANNEL_SYSIO, &sc, sizeof(struct mtsp_syscall))) {
218		set_fs(old_fs);
219		printk(KERN_ERR "Expected request but nothing to read\n");
220		return;
221	}
222
223	size = sc.size;
224
225	if (size) {
226		if (!rtlx_read(RTLX_CHANNEL_SYSIO, &generic, size)) {
227			set_fs(old_fs);
228			printk(KERN_ERR "Expected request but nothing to read\n");
229			return;
230		}
231	}
232
233	/* Run the syscall at the privilege of the user who loaded the
234	   SP program */
235
236	if (vpe_getuid(tclimit)) {
237		err = sp_setfsuidgid(vpe_getuid(tclimit), vpe_getgid(tclimit));
238		if (!err)
239			pr_err("Change of creds failed\n");
240	}
241
242	switch (sc.cmd) {
243	/* needs the flags argument translating from SDE kit to
244	   linux */
245 	case MTSP_SYSCALL_PIPEFREQ:
246 		ret.retval = cpu_khz * 1000;
247 		ret.errno = 0;
248 		break;
249
250 	case MTSP_SYSCALL_GETTOD:
251 		memset(&tz, 0, sizeof(tz));
252 		if ((ret.retval = sp_syscall(__NR_gettimeofday, (int)&tv,
253					     (int)&tz, 0, 0)) == 0)
254			ret.retval = tv.tv_sec;
255		break;
256
257 	case MTSP_SYSCALL_EXIT:
258		list_for_each_entry(n, &kspd_notifylist, list)
259			n->kspd_sp_exit(tclimit);
260		sp_stopping = 1;
261
262		printk(KERN_DEBUG "KSPD got exit syscall from SP exitcode %d\n",
263		       generic.arg0);
264 		break;
265
266 	case MTSP_SYSCALL_OPEN:
267 		generic.arg1 = translate_open_flags(generic.arg1);
268
269		vcwd = vpe_getcwd(tclimit);
270
271		/* change to cwd of the process that loaded the SP program */
272		old_fs = get_fs();
273		set_fs(KERNEL_DS);
274		sys_chdir(vcwd);
275		set_fs(old_fs);
276
277 		sc.cmd = __NR_open;
278
279		/* fall through */
280
281  	default:
282 		if ((sc.cmd >= __NR_Linux) &&
283		    (sc.cmd <= (__NR_Linux +  __NR_Linux_syscalls)) )
284			cmd = sc.cmd;
285		else
286			cmd = translate_syscall_command(sc.cmd);
287
288		if (cmd >= 0) {
289			ret.retval = sp_syscall(cmd, generic.arg0, generic.arg1,
290			                        generic.arg2, generic.arg3);
291		} else
292 			printk(KERN_WARNING
293			       "KSPD: Unknown SP syscall number %d\n", sc.cmd);
294		break;
295 	} /* switch */
296
297	if (vpe_getuid(tclimit)) {
298		err = sp_setfsuidgid(0, 0);
299		if (!err)
300			pr_err("restoring old creds failed\n");
301	}
302
303	old_fs = get_fs();
304	set_fs(KERNEL_DS);
305	written = rtlx_write(RTLX_CHANNEL_SYSIO, &ret, sizeof(ret));
306	set_fs(old_fs);
307	if (written < sizeof(ret))
308		printk("KSPD: sp_work_handle_request failed to send to SP\n");
309}
310
311static void sp_cleanup(void)
312{
313	struct files_struct *files = current->files;
314	int i, j;
315	struct fdtable *fdt;
316
317	j = 0;
318
319	/*
320	 * It is safe to dereference the fd table without RCU or
321	 * ->file_lock
322	 */
323	fdt = files_fdtable(files);
324	for (;;) {
325		unsigned long set;
326		i = j * __NFDBITS;
327		if (i >= fdt->max_fds)
328			break;
329		set = fdt->open_fds->fds_bits[j++];
330		while (set) {
331			if (set & 1) {
332				struct file * file = xchg(&fdt->fd[i], NULL);
333				if (file)
334					filp_close(file, files);
335			}
336			i++;
337			set >>= 1;
338		}
339	}
340
341	/* Put daemon cwd back to root to avoid umount problems */
342	sys_chdir("/");
343}
344
345static int channel_open;
346
347/* the work handler */
348static void sp_work(struct work_struct *unused)
349{
350	if (!channel_open) {
351		if( rtlx_open(RTLX_CHANNEL_SYSIO, 1) != 0) {
352			printk("KSPD: unable to open sp channel\n");
353			sp_stopping = 1;
354		} else {
355			channel_open++;
356			printk(KERN_DEBUG "KSPD: SP channel opened\n");
357		}
358	} else {
359		/* wait for some data, allow it to sleep */
360		rtlx_read_poll(RTLX_CHANNEL_SYSIO, 1);
361
362		/* Check we haven't been woken because we are stopping */
363		if (!sp_stopping)
364			sp_work_handle_request();
365	}
366
367	if (!sp_stopping)
368		queue_work(workqueue, &work);
369	else
370		sp_cleanup();
371}
372
373static void startwork(int vpe)
374{
375	sp_stopping = channel_open = 0;
376
377	if (workqueue == NULL) {
378		if ((workqueue = create_singlethread_workqueue("kspd")) == NULL) {
379			printk(KERN_ERR "unable to start kspd\n");
380			return;
381		}
382
383		INIT_WORK(&work, sp_work);
384	}
385
386	queue_work(workqueue, &work);
387}
388
389static void stopwork(int vpe)
390{
391	sp_stopping = 1;
392
393	printk(KERN_DEBUG "KSPD: SP stopping\n");
394}
395
396void kspd_notify(struct kspd_notifications *notify)
397{
398	list_add(&notify->list, &kspd_notifylist);
399}
400
401static struct vpe_notifications notify;
402static int kspd_module_init(void)
403{
404	INIT_LIST_HEAD(&kspd_notifylist);
405
406	notify.start = startwork;
407	notify.stop = stopwork;
408	vpe_notify(tclimit, &notify);
409
410	return 0;
411}
412
413static void kspd_module_exit(void)
414{
415
416}
417
418module_init(kspd_module_init);
419module_exit(kspd_module_exit);
420
421MODULE_DESCRIPTION("MIPS KSPD");
422MODULE_AUTHOR("Elizabeth Oldham, MIPS Technologies, Inc.");
423MODULE_LICENSE("GPL");