/* * 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 #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; /* * Seach the process specific file descriptor table for a free * descriptor. The new descriptor returned by the call is the lowest * numbered descriptor currently not in use by the process. */ 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_write(int fd, const char* buf, size_t len) { task_t* curr_task = per_core(current_task); /* fd is negative or greater than the maximum allowable number */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; /* fd is not an active, valid file descriptor. */ if (curr_task->fildes_table[fd] == NULL) return -EBADF; return write_fs(curr_task->fildes_table[fd], (uint8_t*)buf, len); } static int sys_open(const char* name, int flags, int mode) { int fd, check; task_t* curr_task = per_core(current_task); /* no name is given */ if (name == NULL) return -EINVAL; /* Get a free file descriptor */ fd = get_fildes(); /* fd is negative or greater than the maximum allowable number */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return fd; /* in this case fd = errno */ /* init the whole file descriptor structure */ curr_task->fildes_table[fd]->mode = mode; curr_task->fildes_table[fd]->flags = flags; curr_task->fildes_table[fd]->count = 1; check = open_fs(curr_task->fildes_table[fd], (char*) name); /* file doesn't exist! */ if (check < 0) { /* tidy up the fildescriptor */ kfree(curr_task->fildes_table[fd]); curr_task->fildes_table[fd] = 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 */ /* convert FS_TYPES in newlib FS_TYPES */ switch (node->type) { case FS_FILE: st->st_mode = 0060000; break; case FS_DIRECTORY: st->st_mode = 0040000; break; case FS_CHARDEVICE: st->st_mode = 0020000; break; default: st->st_mode = 0; break; } 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; } static int sys_read(int fd, const char* buf, size_t len) { task_t* curr_task = per_core(current_task); /* fd is negative or greater than the maximum allowable number */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; /* fd is not an active, valid file descriptor. */ if (curr_task->fildes_table[fd] == NULL) return -EBADF; return read_fs(curr_task->fildes_table[fd], (uint8_t*)buf, len); } #if defined(CONFIG_LWIP) && LWIP_SOCKET static int sys_socket(int domain, int type, int protocol) { int fd, sock; task_t* curr_task = per_core(current_task); /* Get a free file descriptor */ fd = get_fildes(); /* validate the file descriptor */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return fd; /* in this case fd = errno */ /* Get a valid lwip descriptor */ sock = lwip_socket(domain, type, protocol); /* validate the file descriptor */ if (BUILTIN_EXPECT(sock < 0, 0)) return sock; /* in this case sock = errno */ /* * init the whole file descriptor structure. * We use the offset to save the lwip descriptor * TODO: find another solution or change the name 'offset' */ curr_task->fildes_table[fd]->offset = sock; curr_task->fildes_table[fd]->count = 1; curr_task->fildes_table[fd]->node = findnode_fs("/dev/socket"); /* file doesn't exist! */ if (curr_task->fildes_table[fd]->node == NULL) { /* tidy up the fildescriptor */ kfree(curr_task->fildes_table[fd]); curr_task->fildes_table[fd] = NULL; return -ENOENT; } return fd; } static int sys_accept(int s, struct sockaddr* addr, socklen_t* addrlen) { int fd, sock2; task_t* curr_task = per_core(current_task); /* validate the 'socket'-filedescriptor */ if (BUILTIN_EXPECT((s >= NR_OPEN) || (s < 0), 0)) return -EBADF; /* validate the 'socket'-file descriptor object */ if (curr_task->fildes_table[s] == NULL) return -EBADF; /* Get a free file descriptor */ fd = get_fildes(); /* validate the file descriptor */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return fd; /* in this case fd = errno */ /* Get a valid lwip descriptor */ sock2 = lwip_accept(s, addr, addrlen); /* validate the file descriptor */ if (BUILTIN_EXPECT(sock2 < 0, 0)) return sock2; /* in this case sock = errno */ /* * init the whole file descriptor structure. * We use the offset to save the lwip descriptor * TODO: find another solution or change the name 'offset' */ curr_task->fildes_table[fd]->offset = sock2; curr_task->fildes_table[fd]->count = 1; curr_task->fildes_table[fd]->node = findnode_fs("/dev/socket"); /* file doesn't exist! */ if (curr_task->fildes_table[fd]->node == NULL) { /* tidy up the fildescriptor */ kfree(curr_task->fildes_table[fd]); curr_task->fildes_table[fd] = NULL; return -ENOENT; } return fd; } #endif static int sys_close(int fd) { int check; task_t* curr_task = per_core(current_task); /* fd is negative or greater than the maximum allowable number */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; /* fd is not an active, valid file descriptor. */ if (curr_task->fildes_table[fd] == NULL) return -EBADF; /* * The close() call deletes a descriptor from the per-process object * reference table. If this is the last reference to the underlying * object, the object will be deactivated. */ if (curr_task->fildes_table[fd]->count == 1) { check = close_fs(curr_task->fildes_table[fd]); /* close command failed -> return check = errno */ if (BUILTIN_EXPECT(check < 0, 0)) return check; kfree(curr_task->fildes_table[fd]); 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; task_t* curr_task = per_core(current_task); /* fd is negative or greater than the maximum allowable number */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; /* fd is not an active, valid file descriptor. */ if (curr_task->fildes_table[fd] == NULL) return -EBADF; /* TODO: in case of Fildes is associated with a pipe, socket, or FIFO. return ESPIPE */ /* TODO: The seek location is too large to be stored in an object of type off_t. return EOVERFLOW*/ switch(origin) { case SEEK_SET: { /* set file offset to offset */ ret = pos; /* The seek location is negative. */ if (ret < 0) return -EINVAL; curr_task->fildes_table[fd]->offset = ret; ret = 0; break; } case SEEK_CUR: { /* set file offset to current plus offset */ ret = pos + curr_task->fildes_table[fd]->offset; /* The seek location is negative. */ if (ret < 0) return -EINVAL; break; } case SEEK_END: { /* set file offset to EOF plus offset */ ret = pos + curr_task->fildes_table[fd]->node->block_size; /* The seek location is negative. */ if (ret < 0) return -EINVAL; curr_task->fildes_table[fd]->offset = ret; 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; /* fd is negative or greater than the maximum allowable number */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; /* fd is not an active, valid file descriptor. */ if (curr_task->fildes_table[fd] == NULL) return -EBADF; /* Get a free file descriptor */ new_fd = get_fildes(); /* validate the file descriptor */ if (BUILTIN_EXPECT((new_fd >= NR_OPEN) || (fd < 0), 0)) return new_fd; /* in this case fd = errno */ /* * free the memory which was allocated in get_fildes() * cause will link it to another existing memory space */ kfree(curr_task->fildes_table[new_fd]); /* and link it to another existing memory space */ 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); /* fd and fd2 is negative or greater than the maximum allowable number */ if (BUILTIN_EXPECT((fd >= NR_OPEN) || (fd < 0), 0)) return -EBADF; if (BUILTIN_EXPECT((fd2 >= NR_OPEN) || (fd2 < 0), 0)) return -EBADF; /* fd is not an active, valid file descriptor. */ if (curr_task->fildes_table[fd] == NULL) return -EBADF; /* If fd and fd2 are equal, then dup2() just returns fd2 */ if (fd == fd2) return fd2; /* * if descriptor fd2 is already in use, it is first deallocated * as if a close(2) call had been done first */ 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); // search vma containing the heap 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; // allocation and mapping of new pages for the heap // is catched by the pagefault handler //kprintf("sys_sbrk: tid=%d, start_heap=%8x, end_heap=%8x, incr=%4x\n", task->id, task->start_heap, task->end_heap, incr); 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); ret = sys_write(fd, 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); 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); ret = sys_read(fd, 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); 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; }