int copyinstr(const void *uaddr, void *kaddr, size_t len) { if (user_area(uaddr) && user_area((u_char *)uaddr + len)) { strlcpy(kaddr, uaddr, len); return 0; } return EFAULT; }
int copyout(const void *kaddr, void *uaddr, size_t len) { if (user_area(uaddr) && user_area((u_char *)uaddr + len)) { memcpy(uaddr, kaddr, len); return 0; } return EFAULT; }
/** * vm_allocate - allocate zero-filled memory for specified address * * If "anywhere" argument is true, the "addr" argument will be * ignored. In this case, the address of free space will be * found automatically. * * The allocated area has writable, user-access attribute by * default. The "addr" and "size" argument will be adjusted * to page boundary. */ int vm_allocate(task_t task, void **addr, size_t size, int anywhere) { int err; void *uaddr; sched_lock(); if (!task_valid(task)) { err = ESRCH; goto out; } if (task != cur_task() && !task_capable(CAP_MEMORY)) { err = EPERM; goto out; } if (umem_copyin(addr, &uaddr, sizeof(*addr))) { err = EFAULT; goto out; } if (anywhere == 0 && !user_area(*addr)) { err = EACCES; goto out; } err = do_allocate(task->map, &uaddr, size, anywhere); if (err == 0) { if (umem_copyout(&uaddr, addr, sizeof(uaddr))) err = EFAULT; } out: sched_unlock(); return err; }
/* * Change attribute of specified virtual address. * * The "addr" argument points to a memory region previously * allocated through a call to vm_allocate(). The attribute * type can be chosen a combination of VMA_READ, VMA_WRITE. * Note: VMA_EXEC is not supported, yet. */ int vm_attribute(task_t task, void *addr, int attr) { int err; sched_lock(); if (attr == 0 || attr & ~(VMA_READ | VMA_WRITE)) { err = EINVAL; goto out; } if (!task_valid(task)) { err = ESRCH; goto out; } if (task != cur_task() && !task_capable(CAP_MEMORY)) { err = EPERM; goto out; } if (!user_area(addr)) { err = EFAULT; goto out; } err = do_attribute(task->map, addr, attr); out: sched_unlock(); return err; }
/** * vm_map - map another task's memory to current task. * * Note: This routine does not support mapping to the specific * address. */ int vm_map(task_t target, void *addr, size_t size, void **alloc) { int err; sched_lock(); if (!task_valid(target)) { err = ESRCH; goto out; } if (target == cur_task()) { err = EINVAL; goto out; } if (!task_capable(CAP_MEMORY)) { err = EPERM; goto out; } if (!user_area(addr)) { err = EFAULT; goto out; } err = do_map(target->map, addr, size, alloc); out: sched_unlock(); return err; }
/* * Install an exception handler for the current task. * * NULL can be specified as handler to remove current handler. * If handler is removed, all pending exceptions are discarded * immediately. In this case, all threads blocked in * exception_wait() are unblocked. * * Only one exception handler can be set per task. If the * previous handler exists in task, exception_setup() just * override that handler. */ int exception_setup(void (*handler)(int)) { task_t self = cur_task(); list_t head, n; thread_t th; if (handler != NULL && !user_area(handler)) return EFAULT; sched_lock(); if (self->handler && handler == NULL) { /* * Remove existing exception handler. Do clean up * job for all threads in the target task. */ head = &self->threads; for (n = list_first(head); n != head; n = list_next(n)) { /* * Clear pending exceptions. */ th = list_entry(n, struct thread, task_link); irq_lock(); th->excbits = 0; irq_unlock(); /* * If the thread is waiting for an exception, * cancel it. */ if (th->slpevt == &exception_event) sched_unsleep(th, SLP_BREAK); } }
/* * Get system information */ int sys_info(int type, void *buf) { struct info_memory infomem; struct info_timer infotmr; struct info_thread infothr; struct info_device infodev; int err = 0; if (buf == NULL || !user_area(buf)) return EFAULT; sched_lock(); switch (type) { case INFO_KERNEL: err = umem_copyout(&infokern, buf, sizeof(infokern)); break; case INFO_MEMORY: page_info(&infomem); kpage_info(&infomem); err = umem_copyout(&infomem, buf, sizeof(infomem)); break; case INFO_THREAD: if (umem_copyin(buf, &infothr, sizeof(infothr))) { err = EFAULT; break; } if ((err = thread_info(&infothr))) break; infothr.cookie++; err = umem_copyout(&infothr, buf, sizeof(infothr)); break; case INFO_DEVICE: if (umem_copyin(buf, &infodev, sizeof(infodev))) { err = EFAULT; break; } if ((err = device_info(&infodev))) break; infodev.cookie++; err = umem_copyout(&infodev, buf, sizeof(infodev)); break; case INFO_TIMER: timer_info(&infotmr); err = umem_copyout(&infotmr, buf, sizeof(infotmr)); break; default: err = EINVAL; break; } sched_unlock(); return err; }
/* * Send a reply message. * * The target object must be the object that we are receiving. * Otherwise, this function will be failed. */ int msg_reply(object_t obj, void *msg, size_t size) { thread_t t; size_t len; if (!user_area(msg)) return EFAULT; sched_lock(); if (!object_valid(obj) || obj != curthread->recvobj) { sched_unlock(); return EINVAL; } /* * Check if sender still exists */ if (curthread->sender == NULL) { /* Clear receive state */ curthread->recvobj = NULL; sched_unlock(); return EINVAL; } /* * Copy a message to the sender's buffer. */ t = curthread->sender; len = MIN(size, t->msgsize); if (len > 0) { if (copyin(msg, t->msgaddr, len)) { sched_unlock(); return EFAULT; } } /* * Wakeup sender with no error. */ sched_unsleep(t, 0); t->receiver = NULL; /* Clear transmit state */ curthread->sender = NULL; curthread->recvobj = NULL; sched_unlock(); return 0; }
/* * Send a reply message. * * The target object must be an appropriate object that current * thread has been received from. Otherwise, this function will * be failed. * * Since the target object may already be deleted, we can not * access the data of the object within this routine. */ int msg_reply(object_t obj, void *msg, size_t size) { thread_t th; size_t len; int err = 0; if (!user_area(msg)) return EFAULT; sched_lock(); if (!object_valid(obj) || obj != cur_thread->recvobj) { sched_unlock(); return EINVAL; } /* * Check if sender still exists */ if (cur_thread->sender == NULL) { err = EINVAL; goto out; } /* * Copy message to the sender's buffer. */ th = cur_thread->sender; len = min(size, th->msgsize); if (len > 0) { if (umem_copyin(msg, th->msgaddr, len)) { sched_unlock(); return EFAULT; } } /* * Wakeup sender with no error. */ sched_unsleep(th, 0); th->receiver = NULL; out: /* Clear transmit state */ cur_thread->sender = NULL; cur_thread->recvobj = NULL; sched_unlock(); return err; }
/* * Install an exception handler for the current task. * * EXC_DFL can be specified as handler to remove the current handler. * If handler is removed, all pending exceptions are discarded * immediately. At this time, all threads blocked in exception_wait() * are automatically unblocked. * * We allow only one exception handler per task. If the handler * has already been set in task, exception_setup() just override * the previous handler. */ int exception_setup(void (*handler)(int)) { task_t self = curtask; list_t head, n; thread_t t; int s; if (handler != EXC_DFL && !user_area(handler)) return EFAULT; if (handler == NULL) return EINVAL; sched_lock(); if (self->handler != EXC_DFL && handler == EXC_DFL) { /* * Remove existing exception handler. Do clean up * job for all threads in the target task. */ head = &self->threads; for (n = list_first(head); n != head; n = list_next(n)) { /* * Clear pending exceptions. */ s = splhigh(); t = list_entry(n, struct thread, task_link); t->excbits = 0; splx(s); /* * If the thread is waiting for an exception, * cancel it. */ if (t->slpevt == &exception_event) { DPRINTF(("Exception cancelled task=%s\n", self->name)); sched_unsleep(t, SLP_BREAK); } } }
/* * Deallocate memory region for specified address. * * The "addr" argument points to a memory region previously * allocated through a call to vm_allocate() or vm_map(). The * number of bytes freed is the number of bytes of the * allocated region. If one of the region of previous and * next are free, it combines with them, and larger free * region is created. */ int vm_free(task_t task, void *addr) { int err; sched_lock(); if (!task_valid(task)) { err = ESRCH; goto out; } if (task != cur_task() && !task_capable(CAP_MEMORY)) { err = EPERM; goto out; } if (!user_area(addr)) { err = EFAULT; goto out; } err = do_free(task->map, addr); out: sched_unlock(); return err; }
/* * Send a message. * * The current thread will be blocked until any other thread * receives and reply the message. A thread can send a * message to any object if it knows the object id. */ int msg_send(object_t obj, void *msg, size_t size) { struct msg_header *hdr; thread_t t; void *kmsg; int rc; if (!user_area(msg)) return EFAULT; if (size < sizeof(struct msg_header)) return EINVAL; sched_lock(); if (!object_valid(obj)) { sched_unlock(); return EINVAL; } /* * A thread can not send a message when it is * already receiving from the target object. * It will obviously cause a deadlock. */ if (obj == curthread->recvobj) { sched_unlock(); return EDEADLK; } /* * Translate message address to the kernel linear * address. So that a receiver thread can access * the message via kernel pointer. We can catch * the page fault here. */ if ((kmsg = kmem_map(msg, size)) == NULL) { sched_unlock(); return EFAULT; } curthread->msgaddr = kmsg; curthread->msgsize = size; /* * The sender ID is filled in the message header * by the kernel. So, the receiver can trust it. */ hdr = (struct msg_header *)kmsg; hdr->task = curtask; /* * If receiver already exists, wake it up. * The highest priority thread can get the message. */ if (!queue_empty(&obj->recvq)) { t = msg_dequeue(&obj->recvq); sched_unsleep(t, 0); } /* * Sleep until we get a reply message. * Note: Do not touch any data in the object * structure after we wakeup. This is because the * target object may be deleted while we are sleeping. */ curthread->sendobj = obj; msg_enqueue(&obj->sendq, curthread); rc = sched_sleep(&ipc_event); if (rc == SLP_INTR) queue_remove(&curthread->ipc_link); curthread->sendobj = NULL; sched_unlock(); /* * Check sleep result. */ switch (rc) { case SLP_BREAK: return EAGAIN; /* Receiver has been terminated */ case SLP_INVAL: return EINVAL; /* Object has been deleted */ case SLP_INTR: return EINTR; /* Exception */ default: /* DO NOTHING */ break; } return 0; }
/* * Receive a message. * * A thread can receive a message from the object which was * created by any thread belongs to same task. If the message * has not reached yet, it blocks until any message comes in. * * The size argument specifies the "maximum" size of the message * buffer to receive. If the sent message is larger than this * size, the kernel will automatically clip the message to this * maximum buffer size. * * When a message is received, the sender thread is removed from * object's send queue. So, another thread can receive the * subsequent message from that object. This is important for * the multi-thread server which must receive multiple messages * simultaneously. */ int msg_receive(object_t obj, void *msg, size_t size) { thread_t t; size_t len; int rc, error = 0; if (!user_area(msg)) return EFAULT; sched_lock(); if (!object_valid(obj)) { sched_unlock(); return EINVAL; } if (obj->owner != curtask) { sched_unlock(); return EACCES; } /* * Check if this thread finished previous receive * operation. A thread can not receive different * messages at once. */ if (curthread->recvobj) { sched_unlock(); return EBUSY; } curthread->recvobj = obj; /* * If no message exists, wait until message arrives. */ while (queue_empty(&obj->sendq)) { /* * Block until someone sends a message. */ msg_enqueue(&obj->recvq, curthread); rc = sched_sleep(&ipc_event); if (rc != 0) { /* * Receive is failed due to some reasons. */ switch (rc) { case SLP_INVAL: error = EINVAL; /* Object has been deleted */ break; case SLP_INTR: queue_remove(&curthread->ipc_link); error = EINTR; /* Got exception */ break; default: panic("msg_receive"); break; } curthread->recvobj = NULL; sched_unlock(); return error; } /* * Check the existence of the sender thread again. * Even if this thread is woken by the sender thread, * the message may be received by another thread. * This may happen when another high priority thread * becomes runnable before we receive the message. */ } t = msg_dequeue(&obj->sendq); /* * Copy out the message to the user-space. */ len = MIN(size, t->msgsize); if (len > 0) { if (copyout(t->msgaddr, msg, len)) { msg_enqueue(&obj->sendq, t); curthread->recvobj = NULL; sched_unlock(); return EFAULT; } } /* * Detach the message from the target object. */ curthread->sender = t; t->receiver = curthread; sched_unlock(); return error; }
/* * Send a message. * * The current thread will be blocked until any other thread * receives the message and calls msg_reply() for the target * object. When new message has been reached to the object, it * will be received by highest priority thread waiting for * that message. A thread can send a message to any object if * it knows the object id. */ int msg_send(object_t obj, void *msg, size_t size, u_long timeout) { struct msg_header *hdr; thread_t th; void *kmsg; int rc; if (!user_area(msg)) return EFAULT; if (size < sizeof(struct msg_header)) return EINVAL; sched_lock(); if (!object_valid(obj)) { sched_unlock(); return EINVAL; } if (obj->owner != cur_task() && !task_capable(CAP_IPC)) { sched_unlock(); return EPERM; } /* * A thread can not send a message when the * thread is already receiving from the target * object. This will obviously cause a deadlock. */ if (obj == cur_thread->recvobj) { sched_unlock(); return EDEADLK; } /* * Translate message address to the kernel linear * address. So that a receiver thread can access * the message via kernel pointer. We can catch * the page fault here. */ if ((kmsg = kmem_map(msg, size)) == NULL) { /* Error - no physical address for the message */ sched_unlock(); return EFAULT; } /* * The sender ID in the message header is filled * by the kernel. So, the receiver can trust it. */ hdr = (struct msg_header *)kmsg; hdr->task = cur_task(); /* Save information about the message block. */ cur_thread->msgaddr = kmsg; cur_thread->msgsize = size; /* * If receiver already exists, wake it up. * Highest priority thread will get this message. */ if (!queue_empty(&obj->recvq)) { th = msg_dequeue(&obj->recvq); sched_unsleep(th, 0); } /* * Sleep until we get a reply message. * Note: Do not touch any data in the object * structure after we wakeup. This is because the * target object may be deleted during we were * sleeping. */ cur_thread->sendobj = obj; msg_enqueue(&obj->sendq, cur_thread); rc = sched_tsleep(&ipc_event, timeout); if (rc == SLP_INTR) queue_remove(&cur_thread->ipc_link); cur_thread->sendobj = NULL; sched_unlock(); /* * Check sleep result. */ switch (rc) { case SLP_BREAK: return EAGAIN; /* Receiver has been terminated */ case SLP_INVAL: return EINVAL; /* Object has been deleted */ case SLP_INTR: return EINTR; /* Exception */ case SLP_TIMEOUT: return ETIMEDOUT; /* Timeout */ default: /* DO NOTHING */ break; } return 0; }
/* * Receive a message. * * A thread can receive a message from the object which was * created by any thread belongs to same task. If the message * has not arrived yet, it blocks until any message comes in. * * The size argument specifies the "maximum" size of the message * buffer to receive. If the sent message is larger than this * size, the kernel will automatically clip the message to the * receive buffer size. * * When message is received, the sender thread is removed from * object's send queue. So, another thread can receive the * subsequent message from that object. This is important for * the multi-thread server which receives some messages * simultaneously. */ int msg_receive(object_t obj, void *msg, size_t size, u_long timeout) { thread_t th; size_t len; int rc, err = 0; if (!user_area(msg)) return EFAULT; sched_lock(); if (!object_valid(obj)) { err = EINVAL; goto out; } if (obj->owner != cur_task()) { err = EACCES; goto out; } /* * Check if this thread finished previous receive * operation. A thread can not receive different * messages at once. */ if (cur_thread->recvobj) { err = EBUSY; goto out; } cur_thread->recvobj = obj; /* * If no message exists, wait until message arrives. */ while (queue_empty(&obj->sendq)) { /* * Block until someone sends the message. */ msg_enqueue(&obj->recvq, cur_thread); rc = sched_tsleep(&ipc_event, timeout); if (rc != 0) { /* * Receive is failed due to some reasons. */ switch (rc) { case SLP_INVAL: err = EINVAL; /* Object has been deleted */ break; case SLP_INTR: queue_remove(&cur_thread->ipc_link); err = EINTR; /* Got exception */ break; case SLP_TIMEOUT: err = ETIMEDOUT; /* Timeout */ break; default: panic("msg_receive"); break; } cur_thread->recvobj = NULL; goto out; } /* * Even if this thread is woken by the sender thread, * the message may be received by another thread * before this thread runs. This can occur when * higher priority thread becomes runnable at that * time. So, it is necessary to check the existence * of the sender, again. */ } th = msg_dequeue(&obj->sendq); /* * Copy out the message to the user-space. * The smaller buffer size is used as copy length * between sender and receiver thread. */ len = min(size, th->msgsize); if (len > 0) { if (umem_copyout(th->msgaddr, msg, len)) { msg_enqueue(&obj->sendq, th); cur_thread->recvobj = NULL; err = EFAULT; goto out; } } /* * Detach the message from the target object. */ cur_thread->sender = th; th->receiver = cur_thread; out: sched_unlock(); return err; }