Merge branch 'master' into net-dev
[akaros.git] / kern / src / syscall.c
1 /* See COPYRIGHT for copyright information. */
2 #ifdef __DEPUTY__
3 #pragma nodeputy
4 #endif
5
6 #include <arch/types.h>
7 #include <arch/arch.h>
8 #include <arch/mmu.h>
9 #include <arch/console.h>
10 #include <ros/timer.h>
11 #include <ros/error.h>
12
13 #include <rl8168.h>
14 #include <string.h>
15 #include <assert.h>
16 #include <env.h>
17 #include <pmap.h>
18 #include <trap.h>
19 #include <syscall.h>
20 #include <kmalloc.h>
21
22 //Do absolutely nothing.  Used for profiling.
23 static void sys_null(void)
24 {
25         return;
26 }
27
28 //Write a buffer over the serial port
29 static ssize_t sys_serial_write(env_t* e, const char *DANGEROUS buf, size_t len) 
30 {
31         #ifdef SERIAL_IO
32                 char *COUNT(len) _buf = user_mem_assert(e, buf, len, PTE_USER_RO);
33                 for(int i =0; i<len; i++)
34                         serial_send_byte(buf[i]);       
35                 return (ssize_t)len;
36         #else
37                 return -EINVAL;
38         #endif
39 }
40
41 //Read a buffer over the serial port
42 static ssize_t sys_serial_read(env_t* e, char *DANGEROUS buf, size_t len) 
43 {
44         #ifdef SERIAL_IO
45             char *COUNT(len) _buf = user_mem_assert(e, buf, len, PTE_USER_RO);
46                 size_t bytes_read = 0;
47                 int c;
48                 while((c = serial_read_byte()) != -1) {
49                         buf[bytes_read++] = (uint8_t)c;
50                         if(bytes_read == len) break;
51                 }
52                 return (ssize_t)bytes_read;
53         #else
54                 return -EINVAL;
55         #endif
56 }
57
58 static ssize_t sys_run_binary(env_t* e, void* binary_buf, void* arg, size_t len) {
59         uint8_t* new_binary = kmalloc(len, 0);
60         if(new_binary == NULL)
61                 return -ENOMEM;
62         memcpy(new_binary, binary_buf, len);
63
64         env_t* env = env_create((uint8_t*)new_binary, len);
65         kfree(new_binary);
66         
67         e->env_status = ENV_RUNNABLE;
68         env_run(env);
69         return 0;
70 }
71
72 // This is probably not a syscall we want. Its hacky. Here just for syscall stuff until get a stack.
73 static ssize_t sys_eth_write(env_t* e, const char *DANGEROUS buf, size_t len) 
74
75         extern int eth_up;
76         
77         if (eth_up) {
78                 
79                 char *COUNT(len) _buf = user_mem_assert(e, buf, len, PTE_U);
80                 int total_sent = 0;
81                 int just_sent = 0;
82                 int cur_packet_len = 0;
83                 while (total_sent != len) {
84                         cur_packet_len = ((len - total_sent) > MAX_PACKET_DATA) ? MAX_PACKET_DATA : (len - total_sent);
85                         char* wrap_buffer = packet_wrap(buf + total_sent, cur_packet_len);
86                         just_sent = send_frame(wrap_buffer, cur_packet_len + PACKET_HEADER_SIZE);
87                         
88                         if (just_sent < 0)
89                                 return 0; // This should be an error code of its own
90                                 
91                         if (wrap_buffer)
92                                 kfree(wrap_buffer);
93                                 
94                         total_sent += cur_packet_len;
95                 }
96                 
97                 return (ssize_t)len;
98                 
99         }
100         else
101                 return -EINVAL;
102 }
103 /*
104 static ssize_t sys_eth_write(env_t* e, const char *DANGEROUS buf, size_t len) 
105
106         extern int eth_up;
107         
108         if (eth_up) {
109                 
110                 char *COUNT(len) _buf = user_mem_assert(e, buf, len, PTE_U);
111                 
112                 return(send_frame(buf, len));
113         }
114         return -EINVAL;
115 }
116 */
117
118
119 // This is probably not a syscall we want. Its hacky. Here just for syscall stuff until get a stack.
120 static ssize_t sys_eth_read(env_t* e, char *DANGEROUS buf, size_t len) 
121 {
122         extern int eth_up;
123         
124         if (eth_up) {
125                 extern int packet_waiting;
126                 extern int packet_buffer_size;
127                 extern char* packet_buffer;
128                 extern char* packet_buffer_orig;
129                 extern int packet_buffer_pos;
130                         
131                 if (packet_waiting == 0)
132                         return 0;
133                         
134                 int read_len = ((packet_buffer_pos + len) > packet_buffer_size) ? packet_buffer_size - packet_buffer_pos : len;
135
136                 memcpy(buf, packet_buffer + packet_buffer_pos, read_len);
137         
138                 packet_buffer_pos = packet_buffer_pos + read_len;
139         
140                 if (packet_buffer_pos == packet_buffer_size) {
141                         kfree(packet_buffer_orig);
142                         packet_waiting = 0;
143                 }
144         
145                 return read_len;
146         }
147         else
148                 return -EINVAL;
149 }
150
151 static ssize_t sys_shared_page_alloc(env_t* p1, 
152                                      void** addr, envid_t p2_id, 
153                                      int p1_flags, int p2_flags
154                                     ) 
155 {
156         if (!VALID_USER_PERMS(p1_flags)) return -EPERM;
157         if (!VALID_USER_PERMS(p2_flags)) return -EPERM;
158
159         page_t* page;
160         env_t* p2 = &(envs[ENVX(p2_id)]);
161         error_t e = page_alloc(&page);
162         if(e < 0) return e;
163         
164         void* p2_addr = page_insert_in_range(p2->env_pgdir, page, 
165                                              (void*)UTEXT, (void*)UTOP, p2_flags);
166         if(p2_addr == NULL) 
167                 return -EFAIL;
168                 
169         void* p1_addr = page_insert_in_range(p1->env_pgdir, page, 
170                                             (void*)UTEXT, (void*)UTOP, p1_flags);
171         if(p1_addr == NULL) {
172                 page_remove(p2->env_pgdir, p2_addr);
173                 return -EFAIL;
174         }
175         *addr = p1_addr;
176         return ESUCCESS;
177 }
178
179 static void sys_shared_page_free(env_t* p1, void* addr, envid_t p2)
180 {
181 }
182
183 // Invalidate the cache of this core
184 static void sys_cache_invalidate(void)
185 {
186         // why is this necessary with cache coherence?
187         // is it for coherence with respect to i/o?  --asw
188
189         #ifdef __i386__
190                 wbinvd();
191         #endif
192         return;
193 }
194
195 // Writes 'val' to 'num_writes' entries of the well-known array in the kernel
196 // address space.  It's just #defined to be some random 4MB chunk (which ought
197 // to be boot_alloced or something).  Meant to grab exclusive access to cache
198 // lines, to simulate doing something useful.
199 static void sys_cache_buster(env_t* e, uint32_t num_writes, uint32_t num_pages,
200                              uint32_t flags)
201 {
202         #define BUSTER_ADDR             0xd0000000  // around 512 MB deep
203         #define MAX_WRITES              1048576*8
204         #define MAX_PAGES               32
205         #define INSERT_ADDR     (UINFO + 2*PGSIZE) // should be free for these tests
206         uint32_t* buster = (uint32_t*)BUSTER_ADDR;
207         static uint32_t buster_lock = 0;
208         uint64_t ticks = -1;
209         page_t* a_page[MAX_PAGES];
210
211         /* Strided Accesses or Not (adjust to step by cachelines) */
212         uint32_t stride = 1;
213         if (flags & BUSTER_STRIDED) {
214                 stride = 16;
215                 num_writes *= 16;
216         }
217         
218         /* Shared Accesses or Not (adjust to use per-core regions)
219          * Careful, since this gives 8MB to each core, starting around 512MB.
220          * Also, doesn't separate memory for core 0 if it's an async call.
221          */
222         if (!(flags & BUSTER_SHARED))
223                 buster = (uint32_t*)(BUSTER_ADDR + core_id() * 0x00800000);
224
225         /* Start the timer, if we're asked to print this info*/
226         if (flags & BUSTER_PRINT_TICKS)
227                 ticks = start_timing();
228
229         /* Allocate num_pages (up to MAX_PAGES), to simulate doing some more
230          * realistic work.  Note we don't write to these pages, even if we pick
231          * unshared.  Mostly due to the inconvenience of having to match up the
232          * number of pages with the number of writes.  And it's unnecessary.
233          */
234         if (num_pages) {
235                 spin_lock(&buster_lock);
236                 for (int i = 0; i < MIN(num_pages, MAX_PAGES); i++) {
237                         page_alloc(&a_page[i]);
238                         page_insert(e->env_pgdir, a_page[i], (void*)INSERT_ADDR + PGSIZE*i,
239                                     PTE_USER_RW);
240                 }
241                 spin_unlock(&buster_lock);
242         }
243
244         if (flags & BUSTER_LOCKED)
245                 spin_lock(&buster_lock);
246         for (int i = 0; i < MIN(num_writes, MAX_WRITES); i=i+stride)
247                 buster[i] = 0xdeadbeef;
248         if (flags & BUSTER_LOCKED)
249                 spin_unlock(&buster_lock);
250
251         if (num_pages) {
252                 spin_lock(&buster_lock);
253                 for (int i = 0; i < MIN(num_pages, MAX_PAGES); i++) {
254                         page_remove(e->env_pgdir, (void*)(INSERT_ADDR + PGSIZE * i));
255                         page_decref(a_page[i]);
256                 }
257                 spin_unlock(&buster_lock);
258         }
259
260         /* Print info */
261         if (flags & BUSTER_PRINT_TICKS) {
262                 ticks = stop_timing(ticks);
263                 printk("%llu,", ticks);
264         }
265         return;
266 }
267
268 // Print a string to the system console.
269 // The string is exactly 'len' characters long.
270 // Destroys the environment on memory errors.
271 static ssize_t sys_cputs(env_t* e, const char *DANGEROUS s, size_t len)
272 {
273         // Check that the user has permission to read memory [s, s+len).
274         // Destroy the environment if not.
275     char *COUNT(len) _s = user_mem_assert(e, s, len, PTE_USER_RO);
276
277         // Print the string supplied by the user.
278         printk("%.*s", len, _s);
279         return (ssize_t)len;
280 }
281
282 // Read a character from the system console.
283 // Returns the character.
284 static uint16_t sys_cgetc(env_t* e)
285 {
286         uint16_t c;
287
288         // The cons_getc() primitive doesn't wait for a character,
289         // but the sys_cgetc() system call does.
290         while ((c = cons_getc()) == 0)
291                 cpu_relax();
292
293         return c;
294 }
295
296 // Returns the current environment's envid.
297 static envid_t sys_getenvid(env_t* e)
298 {
299         return e->env_id;
300 }
301
302 // Returns the id of the cpu this syscall is executed on.
303 static envid_t sys_getcpuid(void)
304 {
305         return core_id();
306 }
307
308 // Destroy a given environment (possibly the currently running environment).
309 //
310 // Returns 0 on success, < 0 on error.  Errors are:
311 //      -EBADENV if environment envid doesn't currently exist,
312 //              or the caller doesn't have permission to change envid.
313 static error_t sys_env_destroy(env_t* e, envid_t envid)
314 {
315         int r;
316         env_t *env_to_die;
317
318         if ((r = envid2env(envid, &env_to_die, 1)) < 0)
319                 return r;
320         if (env_to_die == e)
321                 printk("[%08x] exiting gracefully\n", e->env_id);
322         else
323                 printk("[%08x] destroying %08x\n", e->env_id, env_to_die->env_id);
324         env_destroy(env_to_die);
325         return ESUCCESS;
326 }
327
328 /*
329  * Current process yields its remaining "time slice".  Currently works for
330  * single-core processes.
331  */
332 static void sys_yield(env_t *e)
333 {
334         // TODO: watch for races throughout anything related to process statuses
335         // and schedule/yielding
336         assert(e->env_status == ENV_RUNNING);
337         e->env_status = ENV_RUNNABLE;
338         schedule();
339 }
340
341 // TODO: Build a dispatch table instead of switching on the syscallno
342 // Dispatches to the correct kernel function, passing the arguments.
343 intreg_t syscall(env_t* e, uint32_t syscallno, uint32_t a1, uint32_t a2,
344                 uint32_t a3, uint32_t a4, uint32_t a5)
345 {
346         // Call the function corresponding to the 'syscallno' parameter.
347         // Return any appropriate return value.
348
349         //cprintf("Incoming syscall number: %d\n    a1: %x\n   "
350         //        " a2: %x\n    a3: %x\n    a4: %x\n    a5: %x\n", 
351         //        syscallno, a1, a2, a3, a4, a5);
352
353         assert(e); // should always have an env for every syscall
354         //printk("Running syscall: %d\n", syscallno);
355         if (INVALID_SYSCALL(syscallno))
356                 return -EINVAL;
357
358         switch (syscallno) {
359                 case SYS_null:
360                         sys_null();
361                         return ESUCCESS;
362                 case SYS_run_binary:
363                         return sys_run_binary(e, (char *DANGEROUS)a1, 
364                                               (char* DANGEROUS)a2, (size_t)a3);
365                 case SYS_eth_write:
366                         return sys_eth_write(e, (char *DANGEROUS)a1, (size_t)a2);
367                 case SYS_eth_read:
368                         return sys_eth_read(e, (char *DANGEROUS)a1, (size_t)a2);        
369                 case SYS_cache_buster:
370                         sys_cache_buster(e, a1, a2, a3);
371                         return 0;
372                 case SYS_cache_invalidate:
373                         sys_cache_invalidate();
374                         return 0;
375                 case SYS_shared_page_alloc:
376                         return sys_shared_page_alloc(e, (void** DANGEROUS) a1, 
377                                                  a2, (int) a3, (int) a4);
378                 case SYS_shared_page_free:
379                         sys_shared_page_free(e, (void* DANGEROUS) a1, a2);
380                     return ESUCCESS;
381                 case SYS_cputs:
382                         return sys_cputs(e, (char *DANGEROUS)a1, (size_t)a2);
383                 case SYS_cgetc:
384                         return sys_cgetc(e);
385                 case SYS_getcpuid:
386                         return sys_getcpuid();
387                 case SYS_serial_write:
388                         return sys_serial_write(e, (char *DANGEROUS)a1, (size_t)a2);
389                 case SYS_serial_read:
390                         return sys_serial_read(e, (char *DANGEROUS)a1, (size_t)a2);
391                 case SYS_getenvid:
392                         return sys_getenvid(e);
393                 case SYS_env_destroy:
394                         return sys_env_destroy(e, (envid_t)a1);
395                 case SYS_yield:
396                         sys_yield(e);
397                         return ESUCCESS;
398                 case SYS_proc_create:
399                 case SYS_proc_run:
400                         panic("Not implemented");
401                 default:
402                         // or just return -EINVAL
403                         panic("Invalid syscall number %d for env %x!", syscallno, *e);
404         }
405         return 0xdeadbeef;
406 }
407
408 intreg_t syscall_async(env_t* e, syscall_req_t *call)
409 {
410         return syscall(e, call->num, call->args[0], call->args[1],
411                        call->args[2], call->args[3], call->args[4]);
412 }
413
414 intreg_t process_generic_syscalls(env_t* e, size_t max)
415 {
416         size_t count = 0;
417         syscall_back_ring_t* sysbr = &e->env_syscallbackring;
418
419         // make sure the env is still alive.  
420         // incref will return ESUCCESS on success.
421         if (env_incref(e))
422                 return -EFAIL;
423
424         // max is the most we'll process.  max = 0 means do as many as possible
425         while (RING_HAS_UNCONSUMED_REQUESTS(sysbr) && ((!max)||(count < max)) ) {
426                 if (!count) {
427                         // ASSUME: one queue per process
428                         // only switch cr3 for the very first request for this queue
429                         // need to switch to the right context, so we can handle the user pointer
430                         // that points to a data payload of the syscall
431                         lcr3(e->env_cr3);
432                 }
433                 count++;
434                 //printk("DEBUG PRE: sring->req_prod: %d, sring->rsp_prod: %d\n",\
435                            sysbr->sring->req_prod, sysbr->sring->rsp_prod);
436                 // might want to think about 0-ing this out, if we aren't
437                 // going to explicitly fill in all fields
438                 syscall_rsp_t rsp;
439                 // this assumes we get our answer immediately for the syscall.
440                 syscall_req_t* req = RING_GET_REQUEST(sysbr, ++(sysbr->req_cons));
441                 rsp.retval = syscall_async(e, req);
442                 // write response into the slot it came from
443                 memcpy(req, &rsp, sizeof(syscall_rsp_t));
444                 // update our counter for what we've produced (assumes we went in order!)
445                 (sysbr->rsp_prod_pvt)++;
446                 RING_PUSH_RESPONSES(sysbr);
447                 //printk("DEBUG POST: sring->req_prod: %d, sring->rsp_prod: %d\n",\
448                            sysbr->sring->req_prod, sysbr->sring->rsp_prod);
449         }
450         env_decref(e);
451         return (intreg_t)count;
452 }