clean up the code that checks user space pointers to see if they're valid. Now
[newos.git] / kernel / port.c
blobaf015c6f43894b0c31d1a2c97f9d01e17ab5891e
1 /*
2 ** Copyright 2001-2004, Mark-Jan Bastian. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
6 #include <kernel/kernel.h>
7 #include <kernel/port.h>
8 #include <kernel/sem.h>
9 #include <kernel/int.h>
10 #include <kernel/debug.h>
11 #include <kernel/heap.h>
12 #include <kernel/vm.h>
13 #include <kernel/cbuf.h>
14 #include <newos/errors.h>
16 #include <string.h>
17 #include <stdlib.h>
19 struct port_msg {
20 int msg_code;
21 cbuf* data_cbuf;
22 size_t data_len;
25 struct port_entry {
26 port_id id;
27 proc_id owner;
28 int32 capacity;
29 int lock;
30 char *name;
31 sem_id read_sem;
32 sem_id write_sem;
33 int head;
34 int tail;
35 int total_count;
36 bool closed;
37 struct port_msg* msg_queue;
40 // internal API
41 void dump_port_list(int argc, char **argv);
42 static void _dump_port_info(struct port_entry *port);
43 static void dump_port_info(int argc, char **argv);
46 // MAX_PORTS must be power of 2
47 #define MAX_PORTS 4096
48 #define MAX_QUEUE_LENGTH 4096
49 #define PORT_MAX_MESSAGE_SIZE 65536
51 static struct port_entry *ports = NULL;
52 static region_id port_region = 0;
53 static bool ports_active = false;
55 static port_id next_port = 0;
57 static int port_spinlock = 0;
58 #define GRAB_PORT_LIST_LOCK() acquire_spinlock(&port_spinlock)
59 #define RELEASE_PORT_LIST_LOCK() release_spinlock(&port_spinlock)
60 #define GRAB_PORT_LOCK(s) acquire_spinlock(&(s).lock)
61 #define RELEASE_PORT_LOCK(s) release_spinlock(&(s).lock)
63 int port_init(kernel_args *ka)
65 int i;
66 int sz;
68 sz = sizeof(struct port_entry) * MAX_PORTS;
70 // create and initialize semaphore table
71 port_region = vm_create_anonymous_region(vm_get_kernel_aspace_id(), "port_table", (void **)&ports,
72 REGION_ADDR_ANY_ADDRESS, sz, REGION_WIRING_WIRED, LOCK_RW|LOCK_KERNEL);
73 if(port_region < 0) {
74 panic("unable to allocate kernel port table!\n");
77 memset(ports, 0, sz);
78 for(i=0; i<MAX_PORTS; i++)
79 ports[i].id = -1;
81 // add debugger commands
82 dbg_add_command(&dump_port_list, "ports", "Dump a list of all active ports");
83 dbg_add_command(&dump_port_info, "port", "Dump info about a particular port");
85 ports_active = true;
87 return 0;
90 void dump_port_list(int argc, char **argv)
92 int i;
94 for(i=0; i<MAX_PORTS; i++) {
95 if(ports[i].id >= 0) {
96 dprintf("%p\tid: 0x%x\t\tname: '%s'\n", &ports[i], ports[i].id, ports[i].name);
101 static void _dump_port_info(struct port_entry *port)
103 int cnt;
104 dprintf("PORT: %p\n", port);
105 dprintf("name: '%s'\n", port->name);
106 dprintf("owner: 0x%x\n", port->owner);
107 dprintf("cap: %d\n", port->capacity);
108 dprintf("head: %d\n", port->head);
109 dprintf("tail: %d\n", port->tail);
110 sem_get_count(port->read_sem, &cnt);
111 dprintf("read_sem: %d\n", cnt);
112 sem_get_count(port->read_sem, &cnt);
113 dprintf("write_sem: %d\n", cnt);
116 static void dump_port_info(int argc, char **argv)
118 int i;
120 if(argc < 2) {
121 dprintf("port: not enough arguments\n");
122 return;
125 // if the argument looks like a hex number, treat it as such
126 if(strlen(argv[1]) > 2 && argv[1][0] == '0' && argv[1][1] == 'x') {
127 unsigned long num = atoul(argv[1]);
129 if(is_kernel_address(num)) {
130 // XXX semi-hack
131 // one can use either address or a port_id, since KERNEL_BASE > MAX_PORTS assumed
132 _dump_port_info((struct port_entry *)num);
133 return;
134 } else {
135 unsigned slot = num % MAX_PORTS;
136 if(ports[slot].id != (int)num) {
137 dprintf("port 0x%lx doesn't exist!\n", num);
138 return;
140 _dump_port_info(&ports[slot]);
141 return;
145 // walk through the ports list, trying to match name
146 for(i=0; i<MAX_PORTS; i++) {
147 if (ports[i].name != NULL)
148 if(strcmp(argv[1], ports[i].name) == 0) {
149 _dump_port_info(&ports[i]);
150 return;
155 port_id
156 port_create(int32 queue_length, const char *name)
158 int i;
159 sem_id sem_r, sem_w;
160 port_id retval;
161 char *temp_name;
162 int name_len;
163 void *q;
164 proc_id owner;
166 if(ports_active == false)
167 return ERR_PORT_NOT_ACTIVE;
169 if(name == NULL)
170 name = "unnamed port";
172 name_len = strlen(name) + 1;
173 name_len = min(name_len, SYS_MAX_OS_NAME_LEN);
175 temp_name = (char *)kmalloc(name_len);
176 if(temp_name == NULL)
177 return ERR_NO_MEMORY;
179 strlcpy(temp_name, name, name_len);
181 // check queue length
182 if (queue_length < 1 || queue_length > MAX_QUEUE_LENGTH) {
183 kfree(temp_name);
184 return ERR_INVALID_ARGS;
187 // alloc a queue
188 q = kmalloc( queue_length * sizeof(struct port_msg) );
189 if (q == NULL) {
190 kfree(temp_name); // dealloc name, too
191 return ERR_NO_MEMORY;
194 // create sem_r with owner set to -1
195 sem_r = sem_create_etc(0, temp_name, -1);
196 if (sem_r < 0) {
197 // cleanup
198 kfree(temp_name);
199 kfree(q);
200 return sem_r;
203 // create sem_w
204 sem_w = sem_create_etc(queue_length, temp_name, -1);
205 if (sem_w < 0) {
206 // cleanup
207 sem_delete(sem_r);
208 kfree(temp_name);
209 kfree(q);
210 return sem_w;
212 owner = proc_get_current_proc_id();
214 int_disable_interrupts();
215 GRAB_PORT_LIST_LOCK();
217 // find the first empty spot
218 for(i=0; i<MAX_PORTS; i++) {
219 if(ports[i].id == -1) {
220 // make the port_id be a multiple of the slot it's in
221 if(i >= next_port % MAX_PORTS) {
222 next_port += i - next_port % MAX_PORTS;
223 } else {
224 next_port += MAX_PORTS - (next_port % MAX_PORTS - i);
226 ports[i].id = next_port++;
227 ports[i].lock = 0;
228 GRAB_PORT_LOCK(ports[i]);
229 RELEASE_PORT_LIST_LOCK();
231 ports[i].capacity = queue_length;
232 ports[i].name = temp_name;
234 // assign sem
235 ports[i].read_sem = sem_r;
236 ports[i].write_sem = sem_w;
237 ports[i].msg_queue = q;
238 ports[i].head = 0;
239 ports[i].tail = 0;
240 ports[i].total_count= 0;
241 ports[i].owner = owner;
242 retval = ports[i].id;
243 RELEASE_PORT_LOCK(ports[i]);
244 goto out;
247 // not enough ports...
248 RELEASE_PORT_LIST_LOCK();
249 kfree(q);
250 kfree(temp_name);
251 retval = ERR_PORT_OUT_OF_SLOTS;
252 dprintf("port_create(): ERR_PORT_OUT_OF_SLOTS\n");
254 // cleanup
255 sem_delete(sem_w);
256 sem_delete(sem_r);
257 kfree(temp_name);
258 kfree(q);
260 out:
261 int_restore_interrupts();
263 return retval;
267 port_close(port_id id)
269 int slot;
271 if(ports_active == false)
272 return ERR_PORT_NOT_ACTIVE;
273 if(id < 0)
274 return ERR_INVALID_HANDLE;
275 slot = id % MAX_PORTS;
277 // walk through the sem list, trying to match name
278 int_disable_interrupts();
279 GRAB_PORT_LOCK(ports[slot]);
281 if (ports[slot].id != id) {
282 RELEASE_PORT_LOCK(ports[slot]);
283 int_restore_interrupts();
284 return ERR_INVALID_HANDLE;
287 // mark port to disable writing
288 ports[slot].closed = true;
290 RELEASE_PORT_LOCK(ports[slot]);
291 int_restore_interrupts();
293 return NO_ERROR;
297 port_delete(port_id id)
299 int slot;
300 sem_id r_sem, w_sem;
301 int capacity;
302 int i;
304 char *old_name;
305 struct port_msg *q;
307 if(ports_active == false)
308 return ERR_PORT_NOT_ACTIVE;
309 if(id < 0)
310 return ERR_INVALID_HANDLE;
312 slot = id % MAX_PORTS;
314 int_disable_interrupts();
315 GRAB_PORT_LOCK(ports[slot]);
317 if(ports[slot].id != id) {
318 RELEASE_PORT_LOCK(ports[slot]);
319 int_restore_interrupts();
320 dprintf("port_delete: invalid port_id %d\n", id);
321 return ERR_INVALID_HANDLE;
324 /* mark port as invalid */
325 ports[slot].id = -1;
326 old_name = ports[slot].name;
327 q = ports[slot].msg_queue;
328 r_sem = ports[slot].read_sem;
329 w_sem = ports[slot].write_sem;
330 capacity = ports[slot].capacity;
331 ports[slot].name = NULL;
333 RELEASE_PORT_LOCK(ports[slot]);
334 int_restore_interrupts();
336 // delete the cbuf's that are left in the queue (if any)
337 for (i=0; i<capacity; i++) {
338 if (q[i].data_cbuf != NULL)
339 cbuf_free_chain(q[i].data_cbuf);
342 kfree(q);
343 kfree(old_name);
345 // release the threads that were blocking on this port by deleting the sem
346 // read_port() will see the ERR_SEM_DELETED acq_sem() return value, and act accordingly
347 sem_delete(r_sem);
348 sem_delete(w_sem);
350 return NO_ERROR;
353 port_id
354 port_find(const char *port_name)
356 int i;
357 int ret_val = ERR_INVALID_HANDLE;
359 if(ports_active == false)
360 return ERR_PORT_NOT_ACTIVE;
361 if(port_name == NULL)
362 return ERR_INVALID_HANDLE;
364 // lock list of ports
365 int_disable_interrupts();
366 GRAB_PORT_LIST_LOCK();
368 // loop over list
369 for(i=0; i<MAX_PORTS; i++) {
370 // lock every individual port before comparing
371 GRAB_PORT_LOCK(ports[i]);
372 if(ports[i].id >= 0 && strcmp(port_name, ports[i].name) == 0) {
373 ret_val = ports[i].id;
374 RELEASE_PORT_LOCK(ports[i]);
375 break;
377 RELEASE_PORT_LOCK(ports[i]);
380 RELEASE_PORT_LIST_LOCK();
381 int_restore_interrupts();
383 return ret_val;
387 port_get_info(port_id id, struct port_info *info)
389 int slot;
391 if(ports_active == false)
392 return ERR_PORT_NOT_ACTIVE;
393 if (info == NULL)
394 return ERR_INVALID_ARGS;
395 if(id < 0)
396 return ERR_INVALID_HANDLE;
398 slot = id % MAX_PORTS;
400 int_disable_interrupts();
401 GRAB_PORT_LOCK(ports[slot]);
403 if(ports[slot].id != id) {
404 RELEASE_PORT_LOCK(ports[slot]);
405 int_restore_interrupts();
406 dprintf("port_get_info: invalid port_id %d\n", id);
407 return ERR_INVALID_HANDLE;
410 // fill a port_info struct with info
411 info->id = ports[slot].id;
412 info->owner = ports[slot].owner;
413 strncpy(info->name, ports[slot].name, min(strlen(ports[slot].name),SYS_MAX_OS_NAME_LEN-1));
414 info->capacity = ports[slot].capacity;
415 sem_get_count(ports[slot].read_sem, &info->queue_count);
416 info->total_count = ports[slot].total_count;
418 RELEASE_PORT_LOCK(ports[slot]);
419 int_restore_interrupts();
421 // from our port_entry
422 return NO_ERROR;
426 port_get_next_port_info(proc_id proc,
427 uint32 *cookie,
428 struct port_info *info)
430 int slot;
432 if(ports_active == false)
433 return ERR_PORT_NOT_ACTIVE;
434 if (cookie == NULL)
435 return ERR_INVALID_ARGS;
437 if (*cookie == NULL) {
438 // return first found
439 slot = 0;
440 } else {
441 // start at index cookie, but check cookie against MAX_PORTS
442 slot = *cookie;
443 if (slot >= MAX_PORTS)
444 return ERR_INVALID_HANDLE;
447 // spinlock
448 int_disable_interrupts();
449 GRAB_PORT_LIST_LOCK();
451 info->id = -1; // used as found flag
452 while (slot < MAX_PORTS) {
453 GRAB_PORT_LOCK(ports[slot]);
454 if (ports[slot].id != -1)
455 if (ports[slot].owner == proc) {
456 // found one!
457 // copy the info
458 info->id = ports[slot].id;
459 info->owner = ports[slot].owner;
460 strncpy(info->name, ports[slot].name, min(strlen(ports[slot].name),SYS_MAX_OS_NAME_LEN-1));
461 info->capacity = ports[slot].capacity;
462 sem_get_count(ports[slot].read_sem, &info->queue_count);
463 info->total_count = ports[slot].total_count;
464 RELEASE_PORT_LOCK(ports[slot]);
465 slot++;
466 break;
468 RELEASE_PORT_LOCK(ports[slot]);
469 slot++;
471 RELEASE_PORT_LIST_LOCK();
472 int_restore_interrupts();
474 if (info->id == -1)
475 return ERR_PORT_NOT_FOUND;
476 *cookie = slot;
477 return NO_ERROR;
480 ssize_t
481 port_buffer_size(port_id id)
483 return port_buffer_size_etc(id, 0, 0);
486 ssize_t
487 port_buffer_size_etc(port_id id,
488 uint32 flags,
489 bigtime_t timeout)
491 int slot;
492 int res;
493 int t;
494 int len;
496 if(ports_active == false)
497 return ERR_PORT_NOT_ACTIVE;
498 if(id < 0)
499 return ERR_INVALID_HANDLE;
501 slot = id % MAX_PORTS;
503 int_disable_interrupts();
504 GRAB_PORT_LOCK(ports[slot]);
506 if(ports[slot].id != id) {
507 RELEASE_PORT_LOCK(ports[slot]);
508 int_restore_interrupts();
509 dprintf("port_get_info: invalid port_id %d\n", id);
510 return ERR_INVALID_HANDLE;
512 RELEASE_PORT_LOCK(ports[slot]);
513 int_restore_interrupts();
515 // block if no message,
516 // if TIMEOUT flag set, block with timeout
518 // XXX - is it a race condition to acquire a sem just after we
519 // unlocked the port ?
520 // XXX: call an acquire_sem which does the release lock, restore int & block the right way
521 res = sem_acquire_etc(ports[slot].read_sem, 1, flags & (SEM_FLAG_TIMEOUT | SEM_FLAG_INTERRUPTABLE), timeout, NULL);
523 GRAB_PORT_LOCK(ports[slot]);
524 if (res == ERR_SEM_DELETED) {
525 // somebody deleted the port
526 RELEASE_PORT_LOCK(ports[slot]);
527 return ERR_PORT_DELETED;
529 if (res == ERR_SEM_TIMED_OUT) {
530 RELEASE_PORT_LOCK(ports[slot]);
531 return ERR_PORT_TIMED_OUT;
534 // once message arrived, read data's length
536 // determine tail
537 // read data's head length
538 t = ports[slot].head;
539 if (t < 0)
540 panic("port %id: tail < 0", ports[slot].id);
541 if (t > ports[slot].capacity)
542 panic("port %id: tail > cap %d", ports[slot].id, ports[slot].capacity);
543 len = ports[slot].msg_queue[t].data_len;
545 // restore readsem
546 sem_release(ports[slot].read_sem, 1);
548 RELEASE_PORT_LOCK(ports[slot]);
550 // return length of item at end of queue
551 return len;
554 int32
555 port_count(port_id id)
557 int slot;
558 int count;
560 if(ports_active == false)
561 return ERR_PORT_NOT_ACTIVE;
562 if(id < 0)
563 return ERR_INVALID_HANDLE;
565 slot = id % MAX_PORTS;
567 int_disable_interrupts();
568 GRAB_PORT_LOCK(ports[slot]);
570 if(ports[slot].id != id) {
571 RELEASE_PORT_LOCK(ports[slot]);
572 int_restore_interrupts();
573 dprintf("port_count: invalid port_id %d\n", id);
574 return ERR_INVALID_HANDLE;
577 sem_get_count(ports[slot].read_sem, &count);
578 // do not return negative numbers
579 if (count < 0)
580 count = 0;
582 RELEASE_PORT_LOCK(ports[slot]);
583 int_restore_interrupts();
585 // return count of messages (sem_count)
586 return count;
589 ssize_t
590 port_read(port_id port,
591 int32 *msg_code,
592 void *msg_buffer,
593 size_t buffer_size)
595 return port_read_etc(port, msg_code, msg_buffer, buffer_size, 0, 0);
598 ssize_t
599 port_read_etc(port_id id,
600 int32 *msg_code,
601 void *msg_buffer,
602 size_t buffer_size,
603 uint32 flags,
604 bigtime_t timeout)
606 int slot;
607 sem_id cached_semid;
608 size_t siz;
609 int res;
610 int t;
611 cbuf* msg_store;
612 int32 code;
613 int err;
615 if(ports_active == false)
616 return ERR_PORT_NOT_ACTIVE;
617 if(id < 0)
618 return ERR_INVALID_HANDLE;
619 if(msg_code == NULL)
620 return ERR_INVALID_ARGS;
621 if((msg_buffer == NULL) && (buffer_size > 0))
622 return ERR_INVALID_ARGS;
623 if (timeout < 0)
624 return ERR_INVALID_ARGS;
626 flags = flags & (PORT_FLAG_USE_USER_MEMCPY | PORT_FLAG_INTERRUPTABLE | PORT_FLAG_TIMEOUT);
628 slot = id % MAX_PORTS;
630 int_disable_interrupts();
631 GRAB_PORT_LOCK(ports[slot]);
633 if(ports[slot].id != id) {
634 RELEASE_PORT_LOCK(ports[slot]);
635 int_restore_interrupts();
636 dprintf("read_port_etc: invalid port_id %d\n", id);
637 return ERR_INVALID_HANDLE;
639 // store sem_id in local variable
640 cached_semid = ports[slot].read_sem;
642 // unlock port && enable ints/
643 RELEASE_PORT_LOCK(ports[slot]);
644 int_restore_interrupts();
646 // XXX -> possible race condition if port gets deleted (->sem deleted too), therefore
647 // sem_id is cached in local variable up here
649 // get 1 entry from the queue, block if needed
650 res = sem_acquire_etc(cached_semid, 1,
651 flags, timeout, NULL);
653 // XXX: possible race condition if port read by two threads...
654 // both threads will read in 2 different slots allocated above, simultaneously
655 // slot is a thread-local variable
657 if (res == ERR_SEM_DELETED) {
658 // somebody deleted the port
659 return ERR_PORT_DELETED;
662 if (res == ERR_SEM_INTERRUPTED) {
663 // XXX: somebody signaled the process the port belonged to, deleting the sem ?
664 return ERR_PORT_INTERRUPTED;
667 if (res == ERR_SEM_TIMED_OUT) {
668 // timed out, or, if timeout=0, 'would block'
669 return ERR_PORT_TIMED_OUT;
672 if (res != NO_ERROR) {
673 dprintf("write_port_etc: res unknown error %d\n", res);
674 return res;
677 int_disable_interrupts();
678 GRAB_PORT_LOCK(ports[slot]);
680 t = ports[slot].tail;
681 if (t < 0)
682 panic("port %id: tail < 0", ports[slot].id);
683 if (t > ports[slot].capacity)
684 panic("port %id: tail > cap %d", ports[slot].id, ports[slot].capacity);
686 ports[slot].tail = (ports[slot].tail + 1) % ports[slot].capacity;
688 msg_store = ports[slot].msg_queue[t].data_cbuf;
689 code = ports[slot].msg_queue[t].msg_code;
691 // mark queue entry unused
692 ports[slot].msg_queue[t].data_cbuf = NULL;
694 // check output buffer size
695 siz = min(buffer_size, ports[slot].msg_queue[t].data_len);
697 cached_semid = ports[slot].write_sem;
699 RELEASE_PORT_LOCK(ports[slot]);
700 int_restore_interrupts();
702 // copy message
703 *msg_code = code;
704 if (siz > 0) {
705 if (flags & PORT_FLAG_USE_USER_MEMCPY) {
706 if ((err = cbuf_user_memcpy_from_chain(msg_buffer, msg_store, 0, siz) < 0)) {
707 // leave the port intact, for other threads that might not crash
708 cbuf_free_chain(msg_store);
709 sem_release(cached_semid, 1);
710 return err;
712 } else
713 cbuf_memcpy_from_chain(msg_buffer, msg_store, 0, siz);
715 // free the cbuf
716 cbuf_free_chain(msg_store);
718 // make one spot in queue available again for write
719 sem_release(cached_semid, 1);
721 return siz;
725 port_set_owner(port_id id, proc_id proc)
727 int slot;
729 if(ports_active == false)
730 return ERR_PORT_NOT_ACTIVE;
731 if(id < 0)
732 return ERR_INVALID_HANDLE;
734 slot = id % MAX_PORTS;
736 int_disable_interrupts();
737 GRAB_PORT_LOCK(ports[slot]);
739 if(ports[slot].id != id) {
740 RELEASE_PORT_LOCK(ports[slot]);
741 int_restore_interrupts();
742 dprintf("port_set_owner: invalid port_id %d\n", id);
743 return ERR_INVALID_HANDLE;
746 // transfer ownership to other process
747 ports[slot].owner = proc;
749 // unlock port
750 RELEASE_PORT_LOCK(ports[slot]);
751 int_restore_interrupts();
753 return NO_ERROR;
757 port_write(port_id id,
758 int32 msg_code,
759 void *msg_buffer,
760 size_t buffer_size)
762 return port_write_etc(id, msg_code, msg_buffer, buffer_size, 0, 0);
766 port_write_etc(port_id id,
767 int32 msg_code,
768 void *msg_buffer,
769 size_t buffer_size,
770 uint32 flags,
771 bigtime_t timeout)
773 int slot;
774 int res;
775 sem_id cached_semid;
776 int h;
777 cbuf* msg_store;
778 int c1, c2;
779 int err;
781 if(ports_active == false)
782 return ERR_PORT_NOT_ACTIVE;
783 if(id < 0)
784 return ERR_INVALID_HANDLE;
786 // mask irrelevant flags
787 flags = flags & (PORT_FLAG_USE_USER_MEMCPY | PORT_FLAG_INTERRUPTABLE | PORT_FLAG_TIMEOUT);
789 slot = id % MAX_PORTS;
791 // check buffer_size
792 if (buffer_size > PORT_MAX_MESSAGE_SIZE)
793 return ERR_INVALID_ARGS;
795 int_disable_interrupts();
796 GRAB_PORT_LOCK(ports[slot]);
798 if(ports[slot].id != id) {
799 RELEASE_PORT_LOCK(ports[slot]);
800 int_restore_interrupts();
801 dprintf("write_port_etc: invalid port_id %d\n", id);
802 return ERR_INVALID_HANDLE;
805 if (ports[slot].closed) {
806 RELEASE_PORT_LOCK(ports[slot]);
807 int_restore_interrupts();
808 dprintf("write_port_etc: port %d closed\n", id);
809 return ERR_PORT_CLOSED;
812 // store sem_id in local variable
813 cached_semid = ports[slot].write_sem;
815 RELEASE_PORT_LOCK(ports[slot]);
816 int_restore_interrupts();
818 // XXX -> possible race condition if port gets deleted (->sem deleted too),
819 // and queue is full therefore sem_id is cached in local variable up here
821 // get 1 entry from the queue, block if needed
822 // assumes flags
823 res = sem_acquire_etc(cached_semid, 1,
824 flags & (SEM_FLAG_TIMEOUT | SEM_FLAG_INTERRUPTABLE), timeout, NULL);
826 // XXX: possible race condition if port written by two threads...
827 // both threads will write in 2 different slots allocated above, simultaneously
828 // slot is a thread-local variable
830 if (res == ERR_SEM_DELETED) {
831 // somebody deleted the port
832 return ERR_PORT_DELETED;
835 if (res == ERR_SEM_INTERRUPTED) {
836 // XXX: somebody signaled the process the port belonged to, deleting the sem ?
837 return ERR_PORT_INTERRUPTED;
840 if (res == ERR_SEM_TIMED_OUT) {
841 // timed out, or, if timeout=0, 'would block'
842 return ERR_PORT_TIMED_OUT;
845 if (res != NO_ERROR) {
846 dprintf("write_port_etc: res unknown error %d\n", res);
847 return res;
850 if (buffer_size > 0) {
851 msg_store = cbuf_get_chain(buffer_size);
852 if (msg_store == NULL)
853 return ERR_NO_MEMORY;
854 if (flags & PORT_FLAG_USE_USER_MEMCPY) {
855 // copy from user memory
856 if ((err = cbuf_user_memcpy_to_chain(msg_store, 0, msg_buffer, buffer_size)) < 0)
857 return err; // memory exception
858 } else
859 // copy from kernel memory
860 if ((err = cbuf_memcpy_to_chain(msg_store, 0, msg_buffer, buffer_size)) < 0)
861 return err; // memory exception
862 } else {
863 msg_store = NULL;
866 // attach copied message to queue
867 int_disable_interrupts();
868 GRAB_PORT_LOCK(ports[slot]);
870 h = ports[slot].head;
871 if (h < 0)
872 panic("port %id: head < 0", ports[slot].id);
873 if (h >= ports[slot].capacity)
874 panic("port %id: head > cap %d", ports[slot].id, ports[slot].capacity);
875 ports[slot].msg_queue[h].msg_code = msg_code;
876 ports[slot].msg_queue[h].data_cbuf = msg_store;
877 ports[slot].msg_queue[h].data_len = buffer_size;
878 ports[slot].head = (ports[slot].head + 1) % ports[slot].capacity;
879 ports[slot].total_count++;
881 // store sem_id in local variable
882 cached_semid = ports[slot].read_sem;
884 RELEASE_PORT_LOCK(ports[slot]);
885 int_restore_interrupts();
887 sem_get_count(ports[slot].read_sem, &c1);
888 sem_get_count(ports[slot].write_sem, &c2);
890 // release sem, allowing read (might reschedule)
891 sem_release(cached_semid, 1);
893 return NO_ERROR;
896 /* this function cycles through the ports table, deleting all the ports that are owned by
897 the passed proc_id */
898 int port_delete_owned_ports(proc_id owner)
900 int i;
901 int count = 0;
903 if(ports_active == false)
904 return ERR_PORT_NOT_ACTIVE;
906 int_disable_interrupts();
907 GRAB_PORT_LIST_LOCK();
909 for(i=0; i<MAX_PORTS; i++) {
910 if(ports[i].id != -1 && ports[i].owner == owner) {
911 port_id id = ports[i].id;
913 RELEASE_PORT_LIST_LOCK();
914 int_restore_interrupts();
916 port_delete(id);
917 count++;
919 int_disable_interrupts();
920 GRAB_PORT_LIST_LOCK();
924 RELEASE_PORT_LIST_LOCK();
925 int_restore_interrupts();
927 return count;
932 * testcode
935 port_id test_p1, test_p2, test_p3, test_p4;
937 void port_test()
939 char testdata[5];
940 thread_id t;
941 int res;
942 int32 dummy;
943 int32 dummy2;
945 strcpy(testdata, "abcd");
947 dprintf("porttest: port_create()\n");
948 test_p1 = port_create(1, "test port #1");
949 test_p2 = port_create(10, "test port #2");
950 test_p3 = port_create(1024, "test port #3");
951 test_p4 = port_create(1024, "test port #4");
953 dprintf("porttest: port_find()\n");
954 dprintf("'test port #1' has id %d (should be %d)\n", port_find("test port #1"), test_p1);
956 dprintf("porttest: port_write() on 1, 2 and 3\n");
957 port_write(test_p1, 1, &testdata, sizeof(testdata));
958 port_write(test_p2, 666, &testdata, sizeof(testdata));
959 port_write(test_p3, 999, &testdata, sizeof(testdata));
960 dprintf("porttest: port_count(test_p1) = %d\n", port_count(test_p1));
962 dprintf("porttest: port_write() on 1 with timeout of 1 sec (blocks 1 sec)\n");
963 port_write_etc(test_p1, 1, &testdata, sizeof(testdata), PORT_FLAG_TIMEOUT, 1000000);
964 dprintf("porttest: port_write() on 2 with timeout of 1 sec (wont block)\n");
965 res = port_write_etc(test_p2, 777, &testdata, sizeof(testdata), PORT_FLAG_TIMEOUT, 1000000);
966 dprintf("porttest: res=%d, %s\n", res, res == 0 ? "ok" : "BAD");
968 dprintf("porttest: port_read() on empty port 4 with timeout of 1 sec (blocks 1 sec)\n");
969 res = port_read_etc(test_p4, &dummy, &dummy2, sizeof(dummy2), PORT_FLAG_TIMEOUT, 1000000);
970 dprintf("porttest: res=%d, %s\n", res, res == ERR_PORT_TIMED_OUT ? "ok" : "BAD");
972 dprintf("porttest: spawning thread for port 1\n");
973 t = thread_create_kernel_thread("port_test", port_test_thread_func, NULL);
974 // resume thread
975 thread_resume_thread(t);
977 dprintf("porttest: write\n");
978 port_write(test_p1, 1, &testdata, sizeof(testdata));
980 // now we can write more (no blocking)
981 dprintf("porttest: write #2\n");
982 port_write(test_p1, 2, &testdata, sizeof(testdata));
983 dprintf("porttest: write #3\n");
984 port_write(test_p1, 3, &testdata, sizeof(testdata));
986 dprintf("porttest: waiting on spawned thread\n");
987 thread_wait_on_thread(t, NULL);
989 dprintf("porttest: close p1\n");
990 port_close(test_p2);
991 dprintf("porttest: attempt write p1 after close\n");
992 res = port_write(test_p2, 4, &testdata, sizeof(testdata));
993 dprintf("porttest: port_write ret %d\n", res);
995 dprintf("porttest: testing delete p2\n");
996 port_delete(test_p2);
998 dprintf("porttest: end test main thread\n");
1002 int port_test_thread_func(void* arg)
1004 int msg_code;
1005 int n;
1006 char buf[6];
1007 buf[5] = '\0';
1009 dprintf("porttest: port_test_thread_func()\n");
1011 n = port_read(test_p1, &msg_code, &buf, 3);
1012 dprintf("port_read #1 code %d len %d buf %s\n", msg_code, n, buf);
1013 n = port_read(test_p1, &msg_code, &buf, 4);
1014 dprintf("port_read #1 code %d len %d buf %s\n", msg_code, n, buf);
1015 buf[4] = 'X';
1016 n = port_read(test_p1, &msg_code, &buf, 5);
1017 dprintf("port_read #1 code %d len %d buf %s\n", msg_code, n, buf);
1019 dprintf("porttest: testing delete p1 from other thread\n");
1020 port_delete(test_p1);
1021 dprintf("porttest: end port_test_thread_func()\n");
1023 return 0;
1027 * user level ports
1030 port_id user_port_create(int32 queue_length, const char *uname)
1032 dprintf("user_port_create: queue_length %d\n", queue_length);
1033 if(uname != NULL) {
1034 char name[SYS_MAX_OS_NAME_LEN];
1035 int rc;
1037 if(is_kernel_address(uname))
1038 return ERR_VM_BAD_USER_MEMORY;
1040 rc = user_strncpy(name, uname, SYS_MAX_OS_NAME_LEN-1);
1041 if(rc < 0)
1042 return rc;
1043 name[SYS_MAX_OS_NAME_LEN-1] = 0;
1045 return port_create(queue_length, name);
1046 } else {
1047 return port_create(queue_length, NULL);
1051 int user_port_close(port_id id)
1053 return port_close(id);
1056 int user_port_delete(port_id id)
1058 return port_delete(id);
1061 port_id user_port_find(const char *port_name)
1063 if(port_name != NULL) {
1064 char name[SYS_MAX_OS_NAME_LEN];
1065 int rc;
1067 if(is_kernel_address(port_name))
1068 return ERR_VM_BAD_USER_MEMORY;
1070 rc = user_strncpy(name, port_name, SYS_MAX_OS_NAME_LEN-1);
1071 if(rc < 0)
1072 return rc;
1073 name[SYS_MAX_OS_NAME_LEN-1] = 0;
1075 return port_find(name);
1076 } else {
1077 return ERR_INVALID_ARGS;
1081 int user_port_get_info(port_id id, struct port_info *uinfo)
1083 int res;
1084 struct port_info info;
1085 int rc;
1087 if (uinfo == NULL)
1088 return ERR_INVALID_ARGS;
1089 if(is_kernel_address(uinfo))
1090 return ERR_VM_BAD_USER_MEMORY;
1092 res = port_get_info(id, &info);
1093 // copy to userspace
1094 rc = user_memcpy(uinfo, &info, sizeof(struct port_info));
1095 if(rc < 0)
1096 return rc;
1097 return res;
1100 int user_port_get_next_port_info(proc_id uproc,
1101 uint32 *ucookie,
1102 struct port_info *uinfo)
1104 int res;
1105 struct port_info info;
1106 uint32 cookie;
1107 int rc;
1109 if (ucookie == NULL)
1110 return ERR_INVALID_ARGS;
1111 if (uinfo == NULL)
1112 return ERR_INVALID_ARGS;
1113 if(is_kernel_address(ucookie))
1114 return ERR_VM_BAD_USER_MEMORY;
1115 if(is_kernel_address(uinfo))
1116 return ERR_VM_BAD_USER_MEMORY;
1118 // copy from userspace
1119 rc = user_memcpy(&cookie, ucookie, sizeof(uint32));
1120 if(rc < 0)
1121 return rc;
1123 res = port_get_next_port_info(uproc, &cookie, &info);
1124 // copy to userspace
1125 rc = user_memcpy(ucookie, &info, sizeof(uint32));
1126 if(rc < 0)
1127 return rc;
1128 rc = user_memcpy(uinfo, &info, sizeof(struct port_info));
1129 if(rc < 0)
1130 return rc;
1131 return res;
1134 ssize_t user_port_buffer_size(port_id port)
1136 return port_buffer_size_etc(port, SEM_FLAG_INTERRUPTABLE, 0);
1139 ssize_t user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
1141 return port_buffer_size_etc(port, flags | SEM_FLAG_INTERRUPTABLE, timeout);
1144 int32 user_port_count(port_id port)
1146 return port_count(port);
1149 ssize_t user_port_read(port_id uport, int32 *umsg_code, void *umsg_buffer,
1150 size_t ubuffer_size)
1152 return user_port_read_etc(uport, umsg_code, umsg_buffer, ubuffer_size, 0, 0);
1155 ssize_t user_port_read_etc(port_id uport, int32 *umsg_code, void *umsg_buffer,
1156 size_t ubuffer_size, uint32 uflags, bigtime_t utimeout)
1158 ssize_t res;
1159 int32 msg_code;
1160 int rc;
1162 if (umsg_code == NULL)
1163 return ERR_INVALID_ARGS;
1164 if (umsg_buffer == NULL)
1165 return ERR_INVALID_ARGS;
1167 if(is_kernel_address(umsg_code))
1168 return ERR_VM_BAD_USER_MEMORY;
1169 if(is_kernel_address(umsg_buffer))
1170 return ERR_VM_BAD_USER_MEMORY;
1172 res = port_read_etc(uport, &msg_code, umsg_buffer, ubuffer_size,
1173 uflags | PORT_FLAG_USE_USER_MEMCPY | SEM_FLAG_INTERRUPTABLE, utimeout);
1175 rc = user_memcpy(umsg_code, &msg_code, sizeof(int32));
1176 if(rc < 0)
1177 return rc;
1179 return res;
1182 int user_port_set_owner(port_id port, proc_id proc)
1184 return port_set_owner(port, proc);
1187 int user_port_write(port_id uport, int32 umsg_code, void *umsg_buffer,
1188 size_t ubuffer_size)
1190 return user_port_write_etc(uport, umsg_code, umsg_buffer, ubuffer_size, 0, 0);
1193 int user_port_write_etc(port_id uport, int32 umsg_code, void *umsg_buffer,
1194 size_t ubuffer_size, uint32 uflags, bigtime_t utimeout)
1196 if (umsg_buffer == NULL)
1197 return ERR_INVALID_ARGS;
1198 if(is_kernel_address(umsg_buffer))
1199 return ERR_VM_BAD_USER_MEMORY;
1200 return port_write_etc(uport, umsg_code, umsg_buffer, ubuffer_size,
1201 uflags | PORT_FLAG_USE_USER_MEMCPY | SEM_FLAG_INTERRUPTABLE, utimeout);