aboutsummaryrefslogblamecommitdiff
path: root/src/kernel/user/syscall.c
blob: 350262cc3f27409f64d7991a4ad27a630d1d1f1e (plain) (tree)
1
2
3
4
5
6
7
8
9
                   
 

                    
                
                
 
                
                   
 




                                                                                         


                                                     
                                                     










                                   
                                                   


                                                    



                          
                                                   
 
                                  
                                                                                  
 
                                                  
                      


                 
                                   



                 
                                    



                       
                                       
                                                     
                                
 




                                                                   
 
                  


                 
                                        


                                                                                          
                                         



                 

                                  
                                  


                                                                      
                                       






                                                                                      
                                    


                                                                
                                    


                                                        

                                        
                                    

                         
                                                    

















                                                       
                                    

                         
                                                    

















                                                       
                                  

                         

                                                          
























                                                                                               
                                  

                         
                                                    


















                                                         

                                  
                                  

                    
                                                    





















                                                       
                                   



                                                 
                                  








                                                                 
                                   








                                                                 
                                     


                                                                 
                                        
                                             
                                          

 
                                       







                                                                 
                                   







                                                                 
                                  


                                                                 










                                                  

 
                                    












                                                                 
                                          















                                                                                         

                                                  

                                                                                     
















                                                                                                                 

            
                                          




































                                                       
                                      











                                                   
                                       








                                                                 
                                       














                                                   

                              
                                     











































                                                                          

 
                                        






















                                                                   

 
                                      








































                                                                          

 
                                   





                                                         



                                                        
                                      



                                                             

 
                                     
                        
 























                                                                     

 
                                     















                                                                     

 
                                       

































                                                              

 
                                         







                                                                     

 
                                       









                                                                              

 
                                       














                                                                             

 



                            
                            


                                                 
                                           

                                                     

                                       
                                                 

                                           




                                           






                                                 
                                         
                                       
                                           
 



                                                     






                                                   

                                                                              




                                                     







                                                             






                                                
                        
                                                                            





                                       
#include <string.h>

#include <process.h>
#include <vfs.h>
#include <elf.h>
#include <ipc.h>

#include <sct.h>
#include <worker.h>

typedef struct {
	uint32_t sc_id, a, b, c, d, e;		// a: ebx, b: ecx, c: edx, d: esi, e: edi
} sc_args_t;

typedef uint32_t (*syscall_handler_t)(sc_args_t);

static syscall_handler_t sc_handlers[SC_MAX] = { 0 };

char* sc_copy_string(const char* addr, size_t slen) {
	probe_for_read(addr, slen);

	char* buf = malloc(slen+1);
	if (buf == 0) return 0;

	memcpy(buf, addr, slen);
	buf[slen] = 0;

	return buf;
}

char* sc_copy_string_x(uint32_t s, uint32_t slen) {
	return sc_copy_string((const char*)s, slen);
}

// ==================== //
// THE SYSCALLS CODE !! //
// ==================== //

//  ---- Related to the current process's execution

uint32_t exit_sc(sc_args_t args) {
	dbg_printf("Proc %d exit with code %d\n", current_process()->pid, args.a);

	current_process_exit(PS_FINISHED, args.a);
	ASSERT(false);
	return 0;
}

uint32_t yield_sc(sc_args_t args) {
	yield();
	return 0;
}

uint32_t usleep_sc(sc_args_t args) {
	usleep(args.a);
	return 0;
}

uint32_t dbg_print_sc(sc_args_t args) {
	char* msg = sc_copy_string_x(args.a, args.b);
	if (msg == 0) return -1;

	if (strchr(msg, '\n')) {
		dbg_printf("[%d] %s", current_process()->pid, msg);
	} else {
		dbg_print(msg);
	}

	free(msg);
	return 0;
}

uint32_t new_thread_sc(sc_args_t args) {
	return process_new_thread(current_process(), (proc_entry_t)args.a, (void*)args.b);
}

uint32_t exit_thread_sc(sc_args_t args) {
	exit();
	return 0;
}

//  ---- Memory management related

uint32_t mmap_sc(sc_args_t args) {
	return mmap(current_process(), (void*)args.a, args.b, args.c);
}

uint32_t mmap_file_sc(sc_args_t args) {
	int fd = args.a;
	fs_handle_t *h = proc_read_fd(current_process(), fd);
	if (h == 0) return false;

	return mmap_file(current_process(), h, args.b, (void*)args.c, args.d, args.e);
}

uint32_t mchmap_sc(sc_args_t args) {
	return mchmap(current_process(), (void*)args.a, args.b);
}

uint32_t munmap_sc(sc_args_t args) {
	return munmap(current_process(), (void*)args.a);
}

//  ---- Accessing the VFS - filesystems

uint32_t create_sc(sc_args_t args) {
	bool ret = false;

	char* fn = sc_copy_string_x(args.a, args.b);
	if (fn == 0) goto end_create;

	char* sep = strchr(fn, ':');
	if (sep == 0) goto end_create;

	*sep = 0;
	char* file = sep + 1;

	fs_t *fs = proc_find_fs(current_process(), fn);
	if (fs == 0) goto end_create;

	ret = fs_create(fs, file, args.c);
	
end_create:
	if (fn) free(fn);
	return ret;
}

uint32_t delete_sc(sc_args_t args) {
	bool ret = false;

	char* fn = sc_copy_string_x(args.a, args.b);
	if (fn == 0) goto end_del;

	char* sep = strchr(fn, ':');
	if (sep == 0) goto end_del;

	*sep = 0;
	char* file = sep + 1;

	fs_t *fs = proc_find_fs(current_process(), fn);
	if (fs == 0) goto end_del;

	ret = fs_delete(fs, file);
	
end_del:
	if (fn) free(fn);
	return ret;
}

uint32_t move_sc(sc_args_t args) {
	bool ret = false;

	char *fn_a = sc_copy_string_x(args.a, args.b),
		 *fn_b = sc_copy_string_x(args.c, args.d);
	if (fn_a == 0 || fn_b == 0) goto end_move;

	char* sep_a = strchr(fn_a, ':');
	if (sep_a == 0) goto end_move;
	*sep_a = 0;

	char* sep_b = strchr(fn_b, ':');
	if (sep_b == 0) goto end_move;
	*sep_b = 0;

	if (strcmp(fn_a, fn_b) != 0) goto end_move;		// can only move within same FS

	char *file_a = sep_a + 1, *file_b = sep_b + 1;

	fs_t *fs = proc_find_fs(current_process(), fn_a);
	if (fs == 0) goto end_move;

	ret = fs_move(fs, file_a, file_b);
	
end_move:
	if (fn_a) free(fn_a);
	if (fn_b) free(fn_b);
	return ret;
}

uint32_t stat_sc(sc_args_t args) {
	bool ret = false;

	char* fn = sc_copy_string_x(args.a, args.b);
	if (fn == 0) goto end_stat;

	char* sep = strchr(fn, ':');
	if (sep == 0) goto end_stat;

	*sep = 0;
	char* file = sep + 1;

	fs_t *fs = proc_find_fs(current_process(), fn);
	if (fs == 0) goto end_stat;

	probe_for_write((stat_t*)args.c, sizeof(stat_t));
	ret = fs_stat(fs, file, (stat_t*)args.c);
	
end_stat:
	if (fn) free(fn);
	return ret;
}

//  ---- Accessing the VFS - files

uint32_t open_sc(sc_args_t args) {
	int ret = 0;

	char* fn = sc_copy_string_x(args.a, args.b);
	if (fn == 0) goto end_open;

	char* sep = strchr(fn, ':');
	if (sep == 0) goto end_open;

	*sep = 0;
	char* file = sep + 1;

	fs_t *fs = proc_find_fs(current_process(), fn);
	if (fs == 0) goto end_open;

	fs_handle_t *h = fs_open(fs, file, args.c);
	if (h == 0) goto end_open;

	ret = proc_add_fd(current_process(), h);
	if (ret == 0) unref_file(h);
	
end_open:
	if (fn) free(fn);
	return ret;
}

uint32_t close_sc(sc_args_t args) {
	proc_close_fd(current_process(), args.a);
	return 0;
}

uint32_t read_sc(sc_args_t args) {
	fs_handle_t *h = proc_read_fd(current_process(), args.a);
	if (h == 0) return 0;

	char* data = (char*)args.d;
	size_t len = args.c;
	probe_for_write(data, len);
	return file_read(h, args.b, len, data);
}

uint32_t write_sc(sc_args_t args) {
	fs_handle_t *h = proc_read_fd(current_process(), args.a);
	if (h == 0) return 0;

	char* data = (char*)args.d;
	size_t len = args.c;
	probe_for_read(data, len);
	return file_write(h, args.b, len, data);
}

uint32_t readdir_sc(sc_args_t args) {
	fs_handle_t *h = proc_read_fd(current_process(), args.a);
	if (h == 0) return false;

	dirent_t *o = (dirent_t*)args.c;
	probe_for_write(o, sizeof(dirent_t));
	return file_readdir(h, args.b, o);
}

uint32_t stat_open_sc(sc_args_t args) {
	fs_handle_t *h = proc_read_fd(current_process(), args.a);
	if (h == 0) return false;

	stat_t *o = (stat_t*)args.b;
	probe_for_write(o, sizeof(stat_t));
	return file_stat(h, o);
}

uint32_t ioctl_sc(sc_args_t args) {
	fs_handle_t *h = proc_read_fd(current_process(), args.a);
	if (h == 0) return -1;

	void* data = (void*)args.c;
	if (data >= (void*)K_HIGHHALF_ADDR) return -1;
	return file_ioctl(h, args.b, data);
}

uint32_t fctl_sc(sc_args_t args) {
	fs_handle_t *h = proc_read_fd(current_process(), args.a);
	if (h == 0) return 0;

	if (args.b == FC_GET_MODE) {
		return h->mode;
	} else if (args.b == FC_SET_BLOCKING) {
		h->mode |= FM_BLOCKING;
		return 1;
	} else if (args.b == FC_SET_NONBLOCKING) {
		h->mode &= ~FM_BLOCKING;
		return 1;
	} else {
		return 0;
	}
}

uint32_t select_sc(sc_args_t args) {
	sel_fd_t *fds = (sel_fd_t*)args.a;
	size_t n = args.b;
	int timeout = args.c;

	probe_for_write(fds, n * sizeof(sel_fd_t));

	uint64_t select_begin_time = get_kernel_time();

	void** wait_objs = (void**)malloc((n+1) * sizeof(void*));
	if (!wait_objs) return false;

	bool ret = false;

	int st = enter_critical(CL_NOINT);

	while (true) {
		//  ---- Poll FDs, if any is ok then return it
		size_t n_wait_objs = 0;
		if (timeout > 0) wait_objs[n_wait_objs++] = current_thread;
		for (size_t i = 0; i < n; i++) {
			fs_handle_t *h = proc_read_fd(current_process(), fds[i].fd);
			if (h) {
				fds[i].got_flags = file_poll(h, &wait_objs[n_wait_objs]);
				if (wait_objs[n_wait_objs]) n_wait_objs++;
				if (fds[i].got_flags & fds[i].req_flags) ret = true;
			}
		}

		uint64_t time = get_kernel_time();

		//  ---- If any is ok, return true
		if (ret) break;
		//  ---- If none of the handles given is a valid handle, return false
		if (n_wait_objs == 0) break;
		//  ---- If the timeout is over, return false
		if (timeout >= 0 && time - select_begin_time >= (uint64_t)timeout) break;

		//  ---- Do a wait, if interrupted (killed or whatever) return false
		void resume_on_v(void*x) {
			resume_on(x);
		}
		if (timeout > 0) worker_push_in(time - select_begin_time - timeout, resume_on_v, current_thread);
		if (!wait_on_many(wait_objs, n_wait_objs)) break;
	}

	exit_critical(st);

	free(wait_objs);
	return ret;
}

//  ---- IPC

uint32_t make_channel_sc(sc_args_t args) {
	// messy messy messy

	bool blocking = (args.a != 0);

	fd_pair_t *f = (fd_pair_t*)args.b;
	probe_for_write(f, sizeof(fd_pair_t));

	f->a = f->b = 0;

	fs_handle_pair_t ch = make_channel(blocking);

	if (ch.a == 0 || ch.b == 0) goto error;

	f->a = proc_add_fd(current_process(), ch.a);
	if (f->a == 0) goto error;

	f->b = proc_add_fd(current_process(), ch.b);
	if (f->b == 0) goto error;

	return true;

error:
	if (f->a) {
		proc_close_fd(current_process(), f->a);
		f->a = 0;
	} else {
		if (ch.a) unref_file(ch.a);
	}
	if (f->b) {
		proc_close_fd(current_process(), f->b);
		f->b = 0;
	} else {
		if (ch.b) unref_file(ch.b);
	}
	return false;
}

uint32_t make_shm_sc(sc_args_t args) {
	fs_handle_t *h = make_shm(args.a);
	if (h == 0) return 0;

	int fd = proc_add_fd(current_process(), h);
	if (fd == 0) {
		unref_file(h);
		return 0;
	}

	return fd;
}

uint32_t gen_token_sc(sc_args_t args) {
	fs_handle_t *h = proc_read_fd(current_process(), args.a);
	if (h == 0) return false;

	token_t *tok = (token_t*)args.b;
	probe_for_write(tok, sizeof(token_t));

	return gen_token_for(h, tok);
}

uint32_t use_token_sc(sc_args_t args) {
	token_t *tok = (token_t*)args.a;
	probe_for_read(tok, sizeof(token_t));

	fs_handle_t *h = use_token(tok);

	if (h == 0) return 0;

	int fd = proc_add_fd(current_process(), h);
	if (fd == 0) unref_file(h);

	return fd;

	return 0;	//TODO
}

//  ---- Managing file systems

uint32_t make_fs_sc(sc_args_t args) {
	sc_make_fs_args_t *a = (sc_make_fs_args_t*)args.a;
	probe_for_read(a, sizeof(sc_make_fs_args_t));

	bool ok = false;

	char* driver = 0;
	char* fs_name = 0;
	char* opts = 0;
	fs_t *the_fs = 0;

	process_t *p;
	if (a->bind_to_pid == 0) {
		p = current_process();
	} else {
		p = process_find_child(current_process(), a->bind_to_pid);
		if (p == 0) goto end_mk_fs;
	}

	fs_handle_t *source = 0;
	if (a->source_fd != 0) {
		source = proc_read_fd(current_process(), a->source_fd);
		if (!source) goto end_mk_fs;
	}

	driver = sc_copy_string(a->driver, a->driver_strlen);
	if (!driver) goto end_mk_fs;

	fs_name = sc_copy_string(a->fs_name, a->fs_name_strlen);
	if (!fs_name) goto end_mk_fs;

	opts = sc_copy_string(a->opts, a->opts_strlen);
	if (!opts) goto end_mk_fs;
	
	the_fs = make_fs(driver, source, opts);
	if (!the_fs) goto end_mk_fs;

	ok = proc_add_fs(p, the_fs, fs_name);

end_mk_fs:
	if (driver) free(driver);
	if (fs_name) free(fs_name);
	if (opts) free(opts);
	if (the_fs && !ok) unref_fs(the_fs);
	return ok;
}

uint32_t fs_add_src_sc(sc_args_t args) {
	bool ok = false;

	char* opts = 0;
	char* fs_name = 0;

	fs_name = sc_copy_string_x(args.a, args.b);
	if (fs_name == 0) goto end_add_src;

	opts = sc_copy_string_x(args.d, args.e);
	if (opts == 0) goto end_add_src;

	fs_t *fs = proc_find_fs(current_process(), fs_name);
	if (fs == 0) goto end_add_src;

	fs_handle_t *src = proc_read_fd(current_process(), args.c);
	if (src == 0) goto end_add_src;

	ok = fs_add_source(fs, src, opts);

end_add_src:
	if (fs_name) free(fs_name);
	if (opts) free(opts);
	return ok;
}

uint32_t fs_subfs_sc(sc_args_t args) {
	sc_subfs_args_t *a = (sc_subfs_args_t*)args.a;
	probe_for_read(a, sizeof(sc_subfs_args_t));

	bool ok = false;

	char* new_name = 0;
	char* from_fs = 0;
	char* root = 0;
	fs_t* new_fs = 0;

	process_t *p;
	if (a->bind_to_pid == 0) {
		p = current_process();
	} else {
		p = process_find_child(current_process(), a->bind_to_pid);
		if (p == 0) goto end_subfs;
	}

	new_name = sc_copy_string(a->new_name, a->new_name_strlen);
	if (!new_name) goto end_subfs;

	from_fs = sc_copy_string(a->from_fs, a->from_fs_strlen);
	if (!from_fs) goto end_subfs;

	root = sc_copy_string(a->root, a->root_strlen);
	if (!root) goto end_subfs;

	fs_t *orig_fs = proc_find_fs(current_process(), from_fs);
	if (!orig_fs) goto end_subfs;

	new_fs = fs_subfs(orig_fs, root, a->ok_modes);
	if (!new_fs) goto end_subfs;

	ok = proc_add_fs(p, new_fs, new_name);

end_subfs:
	if (new_name) free(new_name);
	if (from_fs) free(from_fs);
	if (root) free(root);
	if (new_fs && !ok) unref_fs(new_fs);
	return ok;
}

uint32_t rm_fs_sc(sc_args_t args) {
	char* fs_name = sc_copy_string_x(args.a, args.b);
	if (fs_name == 0) return false;

	proc_rm_fs(current_process(), fs_name);
	free(fs_name);
	return true;
}

//  ---- Spawning new processes & giving them ressources

uint32_t new_proc_sc(sc_args_t args) {
	process_t *new_proc = new_process(current_process());
	if (new_proc == 0) return 0;

	return new_proc->pid;
}

uint32_t bind_fs_sc(sc_args_t args) {
	bool ok = false;

	char* old_name = 0;
	char* new_name = 0;
	fs_t* fs = 0;

	process_t *p = process_find_child(current_process(), args.a);
	if (p == 0) goto end_bind_fs;

	new_name = sc_copy_string_x(args.b, args.c);
	if (!new_name) goto end_bind_fs;

	old_name = sc_copy_string_x(args.d, args.e);
	if (!old_name) goto end_bind_fs;

	fs = proc_find_fs(current_process(), old_name);
	if (!fs) goto end_bind_fs;

	ref_fs(fs);
	ok = proc_add_fs(p, fs, new_name);

end_bind_fs:
	if (old_name) free(old_name);
	if (new_name) free(new_name);
	if (fs && !ok) unref_fs(fs);
	return ok;
}

uint32_t bind_fd_sc(sc_args_t args) {
	bool ok = false;

	fs_handle_t *h = 0;

	process_t *p = process_find_child(current_process(), args.a);
	if (p == 0) goto end_bind_fd;

	h = proc_read_fd(current_process(), args.c);
	if (h == 0) goto end_bind_fd;

	ref_file(h);
	ok = proc_add_fd_as(p, h, args.b);

end_bind_fd:
	if (h && !ok) unref_file(h);
	return ok;
}

uint32_t proc_exec_sc(sc_args_t args) {
	bool ok = false;

	process_t *p = 0;
	char* exec_name = 0;
	fs_handle_t *h = 0;
	
	p = process_find_child(current_process(), args.a);
	if (p == 0) goto end_exec;

	exec_name = sc_copy_string_x(args.b, args.c);
	if (exec_name == 0) goto end_exec;

	char* sep = strchr(exec_name, ':');
	if (sep == 0) goto end_exec;

	*sep = 0;
	char* file = sep + 1;

	fs_t *fs = proc_find_fs(current_process(), exec_name);
	if (fs == 0) goto end_exec;

	h = fs_open(fs, file, FM_READ | FM_MMAP);
	if (h == 0) h = fs_open(fs, file, FM_READ);
	if (h == 0) goto end_exec;

	proc_entry_t *entry = elf_load(h, p);
	if (entry == 0) goto end_exec;

	ok = start_process(p, entry);

end_exec:
	if (exec_name) free(exec_name);
	if (h) unref_file(h);
	return ok;
}

uint32_t proc_status_sc(sc_args_t args) {
	proc_status_t *st = (proc_status_t*)args.b;
	probe_for_write(st, sizeof(proc_status_t));

	process_t *p = process_find_child(current_process(), args.a);
	if (p == 0) return false;

	process_get_status(p, st);
	return true;
}

uint32_t proc_kill_sc(sc_args_t args) {
	proc_status_t *st = (proc_status_t*)args.b;
	probe_for_write(st, sizeof(proc_status_t));

	process_t *p = process_find_child(current_process(), args.a);
	if (p == 0) return false;

	process_exit(p, PS_KILLED, 0);
	process_wait(p, st, true);		// (should return immediately)

	return true;
}

uint32_t proc_wait_sc(sc_args_t args) {
	proc_status_t *st = (proc_status_t*)args.c;
	probe_for_write(st, sizeof(proc_status_t));

	bool wait = (args.b != 0);

	if (args.a == 0) {
		process_wait_any_child(current_process(), st, wait);
		return true;
	} else {
		process_t *p = process_find_child(current_process(), args.a);
		if (p == 0) return false;

		process_wait(p, st, wait);
		return true;
	}
}

// ====================== //
// SYSCALLS SETUP ROUTINE //
// ====================== //

void setup_syscall_table() {
	sc_handlers[SC_EXIT] = exit_sc;
	sc_handlers[SC_YIELD] = yield_sc;
	sc_handlers[SC_DBG_PRINT] = dbg_print_sc;
	sc_handlers[SC_USLEEP] = usleep_sc;
	sc_handlers[SC_NEW_THREAD] = new_thread_sc;
	sc_handlers[SC_EXIT_THREAD] = exit_thread_sc;

	sc_handlers[SC_MMAP] = mmap_sc;
	sc_handlers[SC_MMAP_FILE] = mmap_file_sc;
	sc_handlers[SC_MCHMAP] = mchmap_sc;
	sc_handlers[SC_MUNMAP] = munmap_sc;

	sc_handlers[SC_CREATE] = create_sc;
	sc_handlers[SC_DELETE] = delete_sc;
	sc_handlers[SC_MOVE] = move_sc;
	sc_handlers[SC_STAT] = stat_sc;

	sc_handlers[SC_OPEN] = open_sc;
	sc_handlers[SC_CLOSE] = close_sc;
	sc_handlers[SC_READ] = read_sc;
	sc_handlers[SC_WRITE] = write_sc;
	sc_handlers[SC_READDIR] = readdir_sc;
	sc_handlers[SC_STAT_OPEN] = stat_open_sc;
	sc_handlers[SC_IOCTL] = ioctl_sc;
	sc_handlers[SC_FCTL] = fctl_sc;
	sc_handlers[SC_SELECT] = select_sc;

	sc_handlers[SC_MK_CHANNEL] = make_channel_sc;
	sc_handlers[SC_GEN_TOKEN] = gen_token_sc;
	sc_handlers[SC_USE_TOKEN] = use_token_sc;

	sc_handlers[SC_MAKE_FS] = make_fs_sc;
	sc_handlers[SC_FS_ADD_SRC] = fs_add_src_sc;
	sc_handlers[SC_SUBFS] = fs_subfs_sc;
	sc_handlers[SC_RM_FS] = rm_fs_sc;

	sc_handlers[SC_NEW_PROC] = new_proc_sc;
	sc_handlers[SC_BIND_FS] = bind_fs_sc;
	sc_handlers[SC_BIND_SUBFS] = fs_subfs_sc; 	// no bind_subfs_sc;
	sc_handlers[SC_BIND_MAKE_FS] = make_fs_sc;	// no bind_make_fs_sc;
	sc_handlers[SC_BIND_FD] = bind_fd_sc;
	sc_handlers[SC_PROC_EXEC] = proc_exec_sc;
	sc_handlers[SC_PROC_STATUS] = proc_status_sc;
	sc_handlers[SC_PROC_KILL] = proc_kill_sc;
	sc_handlers[SC_PROC_WAIT] = proc_wait_sc;
}

void syscall_handler(registers_t *regs) {
	ASSERT(regs->int_no == 64);

	if (regs->eax < SC_MAX) {
		syscall_handler_t h = sc_handlers[regs->eax];
		if (h != 0) {
			sc_args_t args = {
				.a = regs->ebx,
				.b = regs->ecx,
				.c = regs->edx,
				.d = regs->esi,
				.e = regs->edi};
			regs->eax = h(args);
		} else {
			dbg_printf("Unimplemented syscall %d\n", regs->eax);
			regs->eax = -1;
		}
	}
}

/* vim: set ts=4 sw=4 tw=0 noet :*/