/* * Copyright 2010 Stefan Lankes, Chair for Operating Systems, * RWTH Aachen University * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file is part of MetalSVM. */ #include #include #include #include #include #include #include #include #include #if defined(CONFIG_LWIP) && LWIP_SOCKET #include #include #include #include #include #include #include #include #include #include #include #endif static int get_fildes(void) { task_t* curr_task = per_core(current_task); int fd; for (fd = 0; fd < NR_OPEN; fd++) { if (curr_task->fildes_table[fd] == NULL) { /* Init Filedescriptor */ curr_task->fildes_table[fd] = kmalloc(sizeof(fildes_t)); memset(curr_task->fildes_table[fd], 0x00, sizeof(fildes_t)); return fd; } } /* can't get any free fd */ return -EMFILE; } static int sys_open(const char* name, int flags, int mode) { int fd, check; fildes_t* file = NULL; fd = get_fildes(); /* validate the fd */ if (fd < 0) return fd; file = per_core(current_task)->fildes_table[fd]; file->mode = mode; file->flags = flags; file->count = 1; check = open_fs(file, (char*) name); if (check < 0) { /* file doesn't exist! */ kfree(file, sizeof(fildes_t)); file = NULL; return check; } return fd; } static int sys_stat(const char* name, struct stat* st) { vfs_node_t* node; node = findnode_fs(name); if (node == NULL) return -EINVAL; st->st_dev = 0; /* ID of device containing file */ st->st_ino = 0; /* inode number */ st->st_mode = node->mask; /* protection */ st->st_nlink = 0; /* number of hard links */ st->st_uid = node->uid; /* user ID of owner */ st->st_gid = node->gid; /* group ID of owner */ st->st_rdev = node->type; /* device ID (if special file) */ st->st_size = (node->block_size / MAX_DATAENTRIES + 1) * MAX_DATAENTRIES; /* total size, in bytes */ st->st_blksize = node->block_size; /* blocksize for filesystem I/O */ st->st_blocks = node->block_size / MAX_DATAENTRIES + 1; /* number of blocks allocated */ st->st_atim.tv_sec = 0; /* time of last access */ st->st_atim.tv_nsec = 0; st->st_mtim.tv_sec = 0; /* time of last modification */ st->st_mtim.tv_nsec = 0; st->st_ctim.tv_sec = 0; /* time of last status change */ st->st_ctim.tv_nsec = 0; return 0; } #if defined(CONFIG_LWIP) && LWIP_SOCKET static int sys_socket(int domain, int type, int protocol) { int fd; fildes_t* file = NULL; fd = get_fildes(); /* validate the fd */ if (fd < 0) return fd; file = per_core(current_task)->fildes_table[fd]; file->offset = lwip_socket(domain, type, protocol); file->node = findnode_fs("/dev/socket"); file->count = 1; return fd; } static int sys_accept(int s, struct sockaddr* addr, socklen_t* addrlen) { int fd; fildes_t* file = NULL; if (BUILTIN_EXPECT(s >= NR_OPEN, 0)) return -EINVAL; fd = get_fildes(); /* validate the fd */ if (fd < 0) return fd; file = per_core(current_task)->fildes_table[fd]; file->offset = lwip_accept(s, addr, addrlen); file->node = findnode_fs("/dev/socket"); file->count = 1; return fd; } #endif static int sys_close(int fd) { task_t* curr_task = per_core(current_task); if (BUILTIN_EXPECT(fd >= NR_OPEN, 0)) return -EINVAL; if (curr_task->fildes_table[fd]->count == 1) { close_fs(curr_task->fildes_table[fd]); kfree(curr_task->fildes_table[fd], sizeof(fildes_t)); curr_task->fildes_table[fd] = NULL; } else { curr_task->fildes_table[fd]->count--; curr_task->fildes_table[fd] = NULL; } return 0; } static int sys_lseek(int fd, off_t pos, int origin) { int ret = -EINVAL; fildes_t* file; if (BUILTIN_EXPECT(fd >= NR_OPEN, 0)) return -EINVAL; file = per_core(current_task)->fildes_table[fd]; if (BUILTIN_EXPECT(!((file->node->type == FS_FILE) || (file->node->type == FS_CHARDEVICE)), 0)) return -EINVAL; switch(origin) { case SEEK_SET: { /* set file offset to offset */ file->offset = pos; ret = 0; break; } case SEEK_CUR: { /* set file offset to current plus offset */ ret = pos + file->offset; break; } case SEEK_END: { /* set file offset to EOF plus offset */ file->offset = pos + file->node->block_size; ret = 0; break; } default: ret = -EINVAL; break; } return ret; } static int sys_dup(int fd) { task_t* curr_task = per_core(current_task); int new_fd; if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; if (curr_task->fildes_table[fd] == NULL) return -EBADF; new_fd = get_fildes(); curr_task->fildes_table[new_fd] = curr_task->fildes_table[fd]; curr_task->fildes_table[fd]->count++; return new_fd; } static int sys_dup2(int fd, int fd2) { task_t* curr_task = per_core(current_task); if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; if (BUILTIN_EXPECT((fd2 >= NR_OPEN) || (fd2 < 0), 0)) return -EBADF; if (curr_task->fildes_table[fd] == NULL) return -EBADF; if (fd == fd2) return fd2; if (curr_task->fildes_table[fd2] != NULL) sys_close(fd2); curr_task->fildes_table[fd2] = curr_task->fildes_table[fd]; curr_task->fildes_table[fd]->count++; return fd2; } static int sys_sbrk(int incr) { task_t* task = per_core(current_task); vma_t* tmp = NULL; int ret; spinlock_lock(&task->vma_lock); tmp = task->vma_list; while(tmp && !((task->end_heap >= tmp->start) && (task->end_heap <= tmp->end))) tmp = tmp->next; ret = (int) task->end_heap; task->end_heap += incr; if (task->end_heap < task->start_heap) task->end_heap = task->start_heap; // resize virtual memory area if (tmp && (tmp->end <= task->end_heap)) tmp->end = task->end_heap; spinlock_unlock(&task->vma_lock); return ret; } int syscall_handler(uint32_t sys_nr, ...) { int ret = -EINVAL; va_list vl; check_workqueues(); va_start(vl, sys_nr); switch(sys_nr) { case __NR_exit: sys_exit(va_arg(vl, uint32_t)); ret = 0; break; case __NR_write: { int fd = va_arg(vl, int); const char* buf = va_arg(vl, const char*); size_t len = va_arg(vl, size_t); if (fd >= 0) ret = write_fs(per_core(current_task)->fildes_table[fd], (uint8_t*)buf, len); break; } case __NR_open: { const char* name = va_arg(vl, const char*); int flags = va_arg(vl, int); int mode = va_arg(vl, int); ret = sys_open(name, flags, mode); break; } case __NR_close: { int fd = va_arg(vl, int); if (fd >= 0) ret = sys_close(fd); break; } case __NR_dup: { int fd = va_arg(vl, int); ret = sys_dup(fd); break; } case __NR_dup2: { int fd = va_arg(vl, int); int fd2 = va_arg(vl, int); ret = sys_dup2(fd, fd2); break; } case __NR_stat: { const char* name = va_arg(vl, const char*); struct stat* st = va_arg(vl, struct stat*); ret = sys_stat(name, st); break; } case __NR_read: { int fd = va_arg(vl, int); const char* buf = va_arg(vl, const char*); size_t len = va_arg(vl, size_t); if (fd >= 0) ret = read_fs(per_core(current_task)->fildes_table[fd], (uint8_t*)buf, len); break; } case __NR_lseek: { int fd = va_arg(vl, int); off_t pos = va_arg(vl, off_t); int origin = va_arg(vl, int); if (fd >= 0) ret = sys_lseek(fd, pos, origin); break; } case __NR_sbrk: { int incr = va_arg(vl, int); ret = sys_sbrk(incr); break; } case __NR_getpid: ret = per_core(current_task)->id; break; case __NR_fork: ret = sys_fork(); break; case __NR_wait: { int32_t* status = va_arg(vl, int32_t*); ret = wait(status); break; } case __NR_execve: { const char* name = va_arg(vl, const char*); char** argv = va_arg(vl, char**); char** env = va_arg(vl, char**); ret = sys_execve(name, argv, env); break; } case __NR_times: { struct tms* buffer = va_arg(vl, struct tms*); clock_t* clock = va_arg(vl, clock_t*); ret = sys_times(buffer, clock); break; } #if defined(CONFIG_LWIP) && LWIP_SOCKET case __NR_closesocket: { int fd = va_arg(vl, int); if (fd < 0) { ret = -ENOTSOCK; break; } ret = lwip_close(per_core(current_task)->fildes_table[fd]->offset); if (ret < 0) ret = -errno; break; } case __NR_socket: { int domain = va_arg(vl, int); int type = va_arg(vl, int); int protocol = va_arg(vl, int); ret = sys_socket(domain, type, protocol); break; } case __NR_connect: { int fd = va_arg(vl, int); const struct sockaddr* name = va_arg(vl, const struct sockaddr*); socklen_t namelen = va_arg(vl, socklen_t); if (per_core(current_task)->fildes_table[fd]->offset < 0) { ret = -ENOTSOCK; break; } //kprintf("lwip_connect: %p with lenght %i and Socket %i", name, namelen, per_core(current_task)->fildes_table[fd].offset); ret = lwip_connect(per_core(current_task)->fildes_table[fd]->offset, name, namelen); if (ret < 0) ret = -errno; break; } case __NR_bind: { int fd = va_arg(vl, int); const struct sockaddr* name = va_arg(vl, const struct sockaddr*); socklen_t namelen = va_arg(vl, socklen_t); if (per_core(current_task)->fildes_table[fd]->offset < 0) { ret = -ENOTSOCK; break; } ret = lwip_bind(per_core(current_task)->fildes_table[fd]->offset, name, namelen); if (ret < 0) ret = -errno; break; } case __NR_listen: { int fd = va_arg(vl, int); int backlog = va_arg(vl, int); if (per_core(current_task)->fildes_table[fd]->offset < 0) { ret = -ENOTSOCK; break; } ret = lwip_listen(per_core(current_task)->fildes_table[fd]->offset, backlog); if (ret < 0) ret = -errno; break; } case __NR_accept: { int fd = va_arg(vl, int); struct sockaddr* addr = va_arg(vl, struct sockaddr*); socklen_t* addrlen = va_arg(vl, socklen_t*); if (per_core(current_task)->fildes_table[fd]->offset < 0) { ret = -ENOTSOCK; break; } ret = sys_accept(per_core(current_task)->fildes_table[fd]->offset, addr, addrlen); if (ret < 0) ret = -errno; break; } #endif default: kputs("invalid system call\n"); ret = -ENOSYS; break; }; va_end(vl); return ret; }