added tcgetattr/tcsetattr for bash
[akaros.git] / kern / arch / sparc / syscalls.c
1 #include <string.h>
2 #include <process.h>
3 #include <kmalloc.h>
4 #include <ros/error.h>
5 #include <pmap.h>
6 #include <arch/frontend.h>
7 #include <syscall.h>
8
9 void* user_memdup(struct proc* p, const void* va, int len)
10 {
11         if(!p)
12                 return (void*)va;
13
14         void* kva = NULL;
15         if(len < 0 || (kva = kmalloc(len,0)) == NULL)
16                 return ERR_PTR(-ENOMEM);
17         if(memcpy_from_user(p,kva,va,len))
18         {
19                 kfree(kva);
20                 return ERR_PTR(-EINVAL);
21         }
22
23         return kva;
24 }
25
26 static void* user_memdup_errno(struct proc* p, const void* va, int len)
27 {
28         void* kva = user_memdup(p,va,len);
29         if(IS_ERR(kva))
30         {
31                 set_errno(current_tf,-PTR_ERR(kva));
32                 return NULL;
33         }
34         return kva;
35 }
36
37 static void user_memdup_free(struct proc* p, void* va)
38 {
39         if(p)
40                 kfree(va);
41 }
42
43 char* user_strdup(struct proc* p, const char* va0, int max)
44 {
45         if(!p)
46                 return (char*)va0;
47
48         max++;
49         char* kbuf = (char*)kmalloc(PGSIZE,0);
50         if(kbuf == NULL)
51                 return ERR_PTR(-ENOMEM);
52
53         int pos = 0, len = 0;
54         const char* va = va0;
55         while(max > 0 && len == 0)
56         {
57                 int thislen = MIN(PGSIZE-(intptr_t)va%PGSIZE,max);
58                 if(memcpy_from_user(p,kbuf,va,thislen))
59                 {
60                         kfree(kbuf);
61                         return ERR_PTR(-EINVAL);
62                 }
63
64                 const char* nullterm = memchr(kbuf,0,thislen);
65                 if(nullterm)
66                         len = pos+(nullterm-kbuf)+1;
67
68                 pos += thislen;
69                 va += thislen;
70                 max -= thislen;
71         }
72
73         kfree(kbuf);
74         return len ? user_memdup(p,va0,len) : ERR_PTR(-EINVAL);
75 }
76
77 static char* user_strdup_errno(struct proc* p, const char* va, int max)
78 {
79         void* kva = user_strdup(p,va,max);
80         if(IS_ERR(kva))
81         {
82                 set_errno(current_tf,-PTR_ERR(kva));
83                 return NULL;
84         }
85         return kva;
86 }
87
88 static int memcpy_to_user_errno(struct proc* p, void* dst, const void* src,
89                                 int len)
90 {
91         if(!p)
92                 memcpy(dst,src,len);
93         else if(memcpy_to_user(p,dst,src,len))
94         {
95                 set_errno(current_tf,EINVAL);
96                 return -1;
97         }
98         return 0;
99 }
100
101 static void* kmalloc_errno(int len)
102 {
103         void* kva = NULL;
104         if(len < 0 || (kva = kmalloc(len,0)) == NULL)
105                 set_errno(current_tf,ENOMEM);
106         return kva;
107 }
108
109 int user_frontend_syscall_errno(struct proc* p, int n, int a0, int a1, int a2, int a3)
110 {
111         int errno, ret = frontend_syscall(p?p->pid:0,n,a0,a1,a2,a3,&errno);
112         if(errno && p)
113                 set_errno(current_tf,errno);
114         return ret;
115 }
116 #define fe(which,a0,a1,a2,a3) \
117         user_frontend_syscall_errno(p,RAMP_SYSCALL_##which,\
118                            (int)(a0),(int)(a1),(int)(a2),(int)(a3))
119
120 intreg_t sys_write(struct proc* p, int fd, const void* buf, int len)
121 {
122         void* kbuf = user_memdup_errno(p,buf,len);
123         if(kbuf == NULL)
124                 return -1;
125         int ret = fe(write,fd,PADDR(kbuf),len,0);
126         user_memdup_free(p,kbuf);
127         return ret;
128 }
129
130 intreg_t sys_read(struct proc* p, int fd, void* buf, int len)
131 {
132         void* kbuf = p ? kmalloc_errno(len) : buf;
133         if(kbuf == NULL)
134                 return -1;
135         int ret = fe(read,fd,PADDR(kbuf),len,0);
136         if(ret != -1 && p && memcpy_to_user_errno(p,buf,kbuf,len))
137                 ret = -1;
138         user_memdup_free(p,kbuf);
139         return ret;
140 }
141
142 intreg_t sys_pwrite(struct proc* p, int fd, const void* buf, int len, int offset)
143 {
144         void* kbuf = user_memdup_errno(p,buf,len);
145         if(kbuf == NULL)
146                 return -1;
147         int ret = fe(pwrite,fd,PADDR(kbuf),len,offset);
148         user_memdup_free(p,kbuf);
149         return ret;
150 }
151
152 intreg_t sys_pread(struct proc* p, int fd, void* buf, int len, int offset)
153 {
154         void* kbuf = p ? kmalloc_errno(len) : buf;
155         if(kbuf == NULL)
156                 return -1;
157         int ret = fe(pread,fd,PADDR(kbuf),len,offset);
158         if(ret != -1 && p && memcpy_to_user_errno(p,buf,kbuf,len))
159                 ret = -1;
160         user_memdup_free(p,kbuf);
161         return ret;
162 }
163
164 intreg_t sys_open(struct proc* p, const char* path, int oflag, int mode)
165 {
166         char* fn = user_strdup_errno(p,path,PGSIZE);
167         if(fn == NULL)
168                 return -1;
169         int ret = fe(open,PADDR(fn),oflag,mode,0);
170         user_memdup_free(p,fn);
171         return ret;
172 }
173 intreg_t sys_close(struct proc* p, int fd)
174 {
175         return fe(close,fd,0,0,0);
176 }
177
178 #define NEWLIB_STAT_SIZE 64
179 intreg_t sys_fstat(struct proc* p, int fd, void* buf)
180 {
181         int kbuf[NEWLIB_STAT_SIZE/sizeof(int)];
182         int ret = fe(fstat,fd,PADDR(kbuf),0,0);
183         if(ret != -1 && memcpy_to_user_errno(p,buf,kbuf,NEWLIB_STAT_SIZE))
184                 ret = -1;
185         return ret;
186 }
187
188 intreg_t sys_stat(struct proc* p, const char* path, void* buf)
189 {
190         int kbuf[NEWLIB_STAT_SIZE/sizeof(int)];
191         char* fn = user_strdup_errno(p,path,PGSIZE);
192         if(fn == NULL)
193                 return -1;
194
195         int ret = fe(stat,PADDR(fn),PADDR(kbuf),0,0);
196         if(ret != -1 && memcpy_to_user_errno(p,buf,kbuf,NEWLIB_STAT_SIZE))
197                 ret = -1;
198
199         user_memdup_free(p,fn);
200         return ret;
201 }
202
203 intreg_t sys_lstat(struct proc* p, const char* path, void* buf)
204 {
205         int kbuf[NEWLIB_STAT_SIZE/sizeof(int)];
206         char* fn = user_strdup_errno(p,path,PGSIZE);
207         if(fn == NULL)
208                 return -1;
209
210         int ret = fe(lstat,PADDR(fn),PADDR(kbuf),0,0);
211         if(ret != -1 && memcpy_to_user_errno(p,buf,kbuf,NEWLIB_STAT_SIZE))
212                 ret = -1;
213
214         user_memdup_free(p,fn);
215         return ret;
216 }
217
218 intreg_t sys_fcntl(struct proc* p, int fd, int cmd, int arg)
219 {
220         return fe(fcntl,fd,cmd,arg,0);
221 }
222
223 intreg_t sys_access(struct proc* p, const char* path, int type)
224 {
225         char* fn = user_strdup_errno(p,path,PGSIZE);
226         if(fn == NULL)
227                 return -1;
228         int ret = fe(access,PADDR(fn),type,0,0);
229         user_memdup_free(p,fn);
230         return ret;
231 }
232
233 intreg_t sys_umask(struct proc* p, int mask)
234 {
235         return fe(umask,mask,0,0,0);
236 }
237
238 intreg_t sys_chmod(struct proc* p, const char* path, int mode)
239 {
240         char* fn = user_strdup_errno(p,path,PGSIZE);
241         if(fn == NULL)
242                 return -1;
243         int ret = fe(chmod,PADDR(fn),mode,0,0);
244         user_memdup_free(p,fn);
245         return ret;
246 }
247
248 intreg_t sys_lseek(struct proc* p, int fd, int offset, int whence)
249 {
250         return fe(lseek,fd,offset,whence,0);
251 }
252
253 intreg_t sys_link(struct proc* p, const char* _old, const char* _new)
254 {
255         char* oldpath = user_strdup_errno(p,_old,PGSIZE);
256         if(oldpath == NULL)
257                 return -1;
258
259         char* newpath = user_strdup_errno(p,_new,PGSIZE);
260         if(newpath == NULL)
261         {
262                 user_memdup_free(p,oldpath);
263                 return -1;
264         }
265
266         int ret = fe(link,PADDR(oldpath),PADDR(newpath),0,0);
267         user_memdup_free(p,oldpath);
268         user_memdup_free(p,newpath);
269         return ret;
270 }
271
272 intreg_t sys_unlink(struct proc* p, const char* path)
273 {
274         char* fn = user_strdup_errno(p,path,PGSIZE);
275         if(fn == NULL)
276                 return -1;
277         int ret = fe(unlink,PADDR(fn),0,0,0);
278         user_memdup_free(p,fn);
279         return ret;
280 }
281
282 intreg_t sys_chdir(struct proc* p, const char* path)
283 {
284         char* fn = user_strdup_errno(p,path,PGSIZE);
285         if(fn == NULL)
286                 return -1;
287         int ret = fe(chdir,PADDR(fn),0,0,0);
288         user_memdup_free(p,fn);
289         return ret;
290 }
291
292 intreg_t sys_getcwd(struct proc* p, char* pwd, int size)
293 {
294         void* kbuf = p ? kmalloc_errno(size) : pwd;
295         if(kbuf == NULL)
296                 return -1;
297         int ret = fe(read,PADDR(kbuf),size,0,0);
298         if(ret != -1 && p && memcpy_to_user_errno(p,pwd,kbuf,strnlen(kbuf,size)))
299                 ret = -1;
300         user_memdup_free(p,kbuf);
301         return ret;
302 }
303
304 intreg_t sys_gettimeofday(struct proc* p, int* buf)
305 {
306         static spinlock_t gtod_lock = SPINLOCK_INITIALIZER;
307         static int t0 = 0;
308
309         spin_lock(&gtod_lock);
310         if(t0 == 0)
311                 t0 = fe(time,0,0,0,0);
312         spin_unlock(&gtod_lock);
313
314         long long dt = read_tsc();
315         int kbuf[2] = {t0+dt/system_timing.tsc_freq,
316             (dt%system_timing.tsc_freq)*1000000/system_timing.tsc_freq};
317
318         return memcpy_to_user_errno(p,buf,kbuf,sizeof(kbuf));
319 }
320
321 #define SIZEOF_STRUCT_TERMIOS 60
322 intreg_t sys_tcgetattr(struct proc* p, int fd, void* termios_p)
323 {
324         int kbuf[SIZEOF_STRUCT_TERMIOS/sizeof(int)];
325         int ret = fe(tcgetattr,fd,PADDR(kbuf),0,0);
326         if(ret != -1 && memcpy_to_user_errno(p,termios_p,kbuf,SIZEOF_STRUCT_TERMIOS))
327                 ret = -1;
328         return ret;
329 }
330
331 intreg_t sys_tcsetattr(struct proc* p, int fd, int optional_actions, const void* termios_p)
332 {
333         void* kbuf = user_memdup_errno(p,termios_p,SIZEOF_STRUCT_TERMIOS);
334         if(kbuf == NULL)
335                 return -1;
336         int ret = fe(tcsetattr,fd,optional_actions,PADDR(kbuf),0);
337         user_memdup_free(p,kbuf);
338         return ret;
339 }
340