Difference between revisions of "Specifiche phase 2 2016/17"
		
		
		
		
		
		Jump to navigation
		Jump to search
		
				
		
		
	
|  (Created page with "<verbatim> ==System call== === send ===   int send(struct tcb_t *dest, uintptr_t msg)   a0 = 1, a1 = dest. a2 = msg  === recv ===   struct tcb_t *recv(struct tcb_t *src, uintp...") |  (Aggiunto link al documento delle specifiche di progetto) | ||
| (9 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
| − | + | ==Documento specifiche== | |
| + | [http://www.cs.unibo.it/~renzo/so/mikaboo/phase2.pdf Link al documento] | ||
| ==System call== | ==System call== | ||
| === send === | === send === | ||
| Line 15: | Line 16: | ||
| ==servizi della SSI== | ==servizi della SSI== | ||
| − | + | solo da kernel mode | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| + | {| class="wikitable" | ||
| + | ! Nome servizio !! Codice servizio !! Parametri !! Tipo di ritorno !! Note | ||
| + | |-  | ||
| + | | GET_ERRNO || 0 || || int || 0 okay | ||
| + | |- | ||
| + | | CREATE_PROCESS || 1 || state_t* || strcut tcb_t* || NULL per errore | ||
| + | |- | ||
| + | | CREATE_THREAD || 2 || state_t* || strcut tcb_t* || NULL per errore | ||
| + | |- | ||
| + | | TERMINATE_PROCESS || 3 || state_t* ||  ||  | ||
| + | |- | ||
| + | | TERMINATE_THREAD || 4 || state_t* ||  || (se e' l'ultimo thread diventa come TERMINATE_PROCESS) | ||
| + | |- | ||
| + | | SETPGMMGR || 5 || struct tcb_t* ||  | ||
| + | | rowspan="3" |  SET*MGR restituiscono lo stesso tcb_t, NULL per errore. <br> | ||
| + | (puà essere chiamato una volta sola per processo, TERMINATE_PROCESS alla seconda chiamata). <br> | ||
| + | I manager ricevono messaggi che hanno come sender il tcb_t del thread che ha causato la trap, <br> | ||
| + | un puntatore al suo state_t come payload. | ||
| + | |- | ||
| + | | SETTLBMGR || 6 || struct tcb_t* ||  ||  | ||
| + | |- | ||
| + | | SETSYSMGR || 7 || struct tcb_t* ||  || | ||
| + | |- | ||
| + | | GETCPUTIME || 8 || || unsigned int || in microsecondi, relativo al processo | ||
| + | |- | ||
| + | | WAIT_FOR_CLOCK || 9 || || Sospende il thread fino al prossimo tick di pseudo-clock || | ||
| + | |- | ||
| + | | DO_IO || 10 || DEVICE_REG_ADDR, COMMAND, ...  || status  | ||
| + | | | ||
| + | *Per Dischi: DEVICE_REG_ADDR, COMMAND, DATA1 (DATA1 e' l'indirizzo per il DMA, settore/testina e cyl, sono in COMMAND)  | ||
| + | *Per Nastri: DEVICE_REG_ADDR, COMMAND, DATA1 (DATA1 e' l'indirizzo per il DMA)  | ||
| + | *Per Rete: DEVICE_REG_ADDR, COMMAND, DATA1, DATA2 (DATA1 e' l'indirizzo del pacchetto, DATA2 la lunghezza) | ||
| + | *Per Stampante:  DEVICE_REG_ADDR, COMMAND, DATA1 (DATA1 contiene il carattere da stampare)  | ||
| + | *Per Terminale IN: DEVICE_REG_ADDR, COMMAND  | ||
| + | *Per Terminale OUT: DEVICE_REG_ADDR, COMMAND  | ||
| + | |- | ||
| + | | GET_PROCESSID || 11 || struct tcb_t* || struct pcb_t * || | ||
| + | |- | ||
| + | | GET_MYTHREADID || 12 || || struct tcb_t * ||  | ||
| + | |- | ||
| + | | GET_PARENTPROCID || 13 || pcb_t* || pcb_t* || | ||
| + | |} | ||
| − | + | ==Sorgenti di test== | |
| − | + | ===nucleus.h=== | |
| − | </ | + | <source lang="C"> | 
| + | #ifndef NUCLEUS_H | ||
| + | #define NUCLEUS_H | ||
| + | |||
| + | #include <stdint.h> | ||
| + | #include "mikabooq.h" | ||
| + | |||
| + | /* Syscall mnemonic values */ | ||
| + | #define SYS_SEND    1 | ||
| + | #define SYS_RECV    2 | ||
| + | |||
| + | /* SSI requests */ | ||
| + | #define GET_ERRNO 0 | ||
| + | #define CREATE_PROCESS 1 | ||
| + | #define CREATE_THREAD 2 | ||
| + | #define TERMINATE_PROCESS 3 | ||
| + | #define TERMINATE_THREAD 4 | ||
| + | #define SETPGMMGR 5 | ||
| + | #define SETTLBMGR 6 | ||
| + | #define SETSYSMGR 7 | ||
| + | #define GET_CPUTIME 8 | ||
| + | #define WAIT_FOR_CLOCK 9 | ||
| + | #define DO_IO 10 | ||
| + | #define GET_PROCESSID 11 | ||
| + | #define GET_MYTHREADID 12 | ||
| + | #define GET_PARENTPROCID 13 | ||
| + | |||
| + | typedef uintptr_t memaddr; | ||
| + | typedef uintptr_t cputime; | ||
| + | typedef uintptr_t devaddr; | ||
| + | |||
| + | extern void* SSI; | ||
| + | |||
| + | #define msgsend(dest, payload) (SYSCALL(SYS_SEND,(unsigned int) (dest),(unsigned int) (payload),0)) | ||
| + | |||
| + | #define msgrecv(source, reply) (((struct tcb_t *) SYSCALL(SYS_RECV,(unsigned int) (source),(unsigned int) (reply),0))) | ||
| + | |||
| + | static inline uintptr_t geterrno(void) { | ||
| + |     uintptr_t retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |     } req = {GET_ERRNO}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline struct tcb_t* create_process(state_t* s) { | ||
| + |     struct tcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         state_t* s; | ||
| + |     } req = {CREATE_PROCESS, s}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline struct tcb_t* create_thread(state_t* s) { | ||
| + |     struct tcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         state_t* s; | ||
| + |     } req = {CREATE_THREAD, s}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline void terminate_process(void) { | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |     } req = {TERMINATE_PROCESS}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, NULL); | ||
| + | } | ||
| + | |||
| + | static inline void terminate_thread(void) { | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |     } req = {TERMINATE_THREAD}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, NULL); | ||
| + | } | ||
| + | |||
| + | static inline struct tcb_t* setpgmmgr(struct tcb_t* s) { | ||
| + |     struct tcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         struct tcb_t* s; | ||
| + |     } req = {SETPGMMGR, s}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline struct tcb_t* settlbmgr(struct tcb_t* s) { | ||
| + |     struct tcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         struct tcb_t* s; | ||
| + |     } req = {SETTLBMGR, s}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline struct tcb_t* setsysmgr(struct tcb_t* s) { | ||
| + |     struct tcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         struct tcb_t* s; | ||
| + |     } req = {SETSYSMGR, s}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline cputime getcputime(void) { | ||
| + |     uintptr_t retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |     } req = {GET_CPUTIME}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline void waitforclock(void) { | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |     } req = {WAIT_FOR_CLOCK}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, NULL); | ||
| + | } | ||
| + | |||
| + | static inline unsigned int do_io(devaddr device, uintptr_t command, uintptr_t data1, uintptr_t data2) { | ||
| + |     uintptr_t retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         devaddr device; | ||
| + |         uintptr_t command; | ||
| + |         uintptr_t data1; | ||
| + |         uintptr_t data2; | ||
| + |     } req = {DO_IO, device, command, data1, data2}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | #define do_disk_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0) | ||
| + | #define do_tape_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0) | ||
| + | #define do_net_io(dev, cmd, d1, d2) do_io((dev),(cmd),(d1),(d2)) | ||
| + | #define do_printer_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0) | ||
| + | #define do_terminal_io(dev, cmd) do_io((dev),(cmd),0,0) | ||
| + | |||
| + | static inline struct pcb_t* get_processid(struct tcb_t* s) { | ||
| + |     struct pcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         struct tcb_t* s; | ||
| + |     } req = {GET_PROCESSID, s}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline struct pcb_t* get_parentprocid(struct pcb_t* s) { | ||
| + |     struct pcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |         struct pcb_t* s; | ||
| + |     } req = {GET_PARENTPROCID, s}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | static inline struct tcb_t* get_mythreadid(void) { | ||
| + |     struct tcb_t* retval; | ||
| + |     struct { | ||
| + |         uintptr_t reqtag; | ||
| + |     } req = {GET_MYTHREADID}; | ||
| + |     msgsend(SSI, &req); | ||
| + |     msgrecv(SSI, &retval); | ||
| + |     return retval; | ||
| + | } | ||
| + | |||
| + | #endif | ||
| + | </source> | ||
| + | ===p2test.c=== | ||
| + | <source lang="C"> | ||
| + | /* | ||
| + |  * Copyright (C) 2017 Renzo Davoli | ||
| + |  * | ||
| + |  * This program is free software; you can redistribute it and/or modify | ||
| + |  * it under the terms of the GNU General Public License as published by | ||
| + |  * the Free Software Foundation; either version 2 of the License, or | ||
| + |  * (at your option) any later version. | ||
| + |  * | ||
| + |  * This program is distributed in the hope that it will be useful, | ||
| + |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| + |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
| + |  * GNU General Public License for more details. | ||
| + |  * | ||
| + |  * You should have received a copy of the GNU General Public License | ||
| + |  * along with this program; if not, write to the Free Software | ||
| + |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||
| + |  */ | ||
| + | |||
| + | #include <uARMconst.h> | ||
| + | #include <uARMtypes.h> | ||
| + | #include <libuarm.h> | ||
| + | |||
| + | #include "nucleus.h" | ||
| + | |||
| + | #define QPAGE FRAME_SIZE | ||
| + | #define TERM0ADDR               0x24C | ||
| + | |||
| + | static struct tcb_t* printid; | ||
| + | |||
| + | static void ttyprintstring(devaddr device, char* s) { | ||
| + |     uintptr_t status; | ||
| + | |||
| + |     for (; *s; s++) { | ||
| + |         status = do_terminal_io(device, DEV_TTRS_C_TRSMCHAR | (*s << 8)); | ||
| + |         switch (status & 0xff) { | ||
| + |             case DEV_S_READY: | ||
| + |             case DEV_TTRS_S_CHARTRSM: | ||
| + |                 break; | ||
| + |             default: | ||
| + |                 return; | ||
| + |         } | ||
| + |     } | ||
| + | } | ||
| + | |||
| + | void tty0out_thread(void) { | ||
| + |     uintptr_t payload; | ||
| + |     struct tcb_t* sender; | ||
| + | |||
| + |     for (;;) { | ||
| + |         sender = msgrecv(NULL, &payload); | ||
| + |         ttyprintstring(TERM0ADDR, (char*) payload); | ||
| + |         msgsend(sender, NULL); | ||
| + |     } | ||
| + | } | ||
| + | |||
| + | static inline void tty0print(char* s) { | ||
| + |     msgsend(printid, s); | ||
| + |     msgrecv(printid, NULL); | ||
| + | } | ||
| + | |||
| + | static inline void panic(char* s) { | ||
| + |     tty0print("!!! PANIC: "); | ||
| + |     tty0print(s); | ||
| + |     PANIC(); | ||
| + | } | ||
| + | |||
| + | static struct tcb_t* csid; | ||
| + | |||
| + | static inline void CSIN() { | ||
| + |     msgsend(csid, NULL); | ||
| + |     msgrecv(csid, NULL); | ||
| + | } | ||
| + | |||
| + | #define CSOUT msgsend(csid, NULL) | ||
| + | |||
| + | void cs_thread(void) { | ||
| + |     struct tcb_t* sender; | ||
| + |     for (;;) { | ||
| + |         sender = msgrecv(NULL, NULL); | ||
| + |         msgsend(sender, NULL); | ||
| + |         msgrecv(sender, NULL); | ||
| + |     } | ||
| + | } | ||
| + | |||
| + | #define SYNCCODE 0x01000010 | ||
| + | |||
| + | void p2(), p3(), p4(), p5(), p6(), p7(), p8(); | ||
| + | |||
| + | struct tcb_t* testt, * p2t, * p3t, * p4t, * p5t, * p6t, * p7t, * p8t; | ||
| + | |||
| + | static state_t tmpstate; | ||
| + | memaddr stackalloc; | ||
| + | |||
| + | uintptr_t p5sys = 0; | ||
| + | uintptr_t p5send = 0; | ||
| + | |||
| + | void test(void) { | ||
| + |     ttyprintstring(TERM0ADDR, "NUCLEUS TEST: starting...\n"); | ||
| + |     STST(&tmpstate); | ||
| + |     stackalloc = (tmpstate.sp + (QPAGE - 1)) & (~(QPAGE - 1)); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     tmpstate.pc = (memaddr) tty0out_thread; | ||
| + |     tmpstate.cpsr = STATUS_ALL_INT_ENABLE(tmpstate.cpsr); | ||
| + |     printid = create_thread(&tmpstate); | ||
| + |     tty0print("NUCLEUS: first msg printed by tty0out_thread\n"); | ||
| + |     testt = get_mythreadid(); | ||
| + | |||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     tmpstate.pc = (memaddr) cs_thread; | ||
| + |     csid = create_process(&tmpstate); | ||
| + |     tty0print("NUCLEUS: critical section thread started\n"); | ||
| + | |||
| + |     CSIN(); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     tmpstate.pc = (memaddr) p2; | ||
| + |     p2t = create_process(&tmpstate); | ||
| + |     msgsend(p2t, SYNCCODE); | ||
| + |     msgrecv(p2t, NULL); | ||
| + | |||
| + |     tty0print("p2 completed\n"); | ||
| + | |||
| + |     CSIN(); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     tmpstate.pc = (memaddr) p3; | ||
| + |     p3t = create_process(&tmpstate); | ||
| + |     msgrecv(p3t, NULL); | ||
| + | |||
| + |     tty0print("p3 completed\n"); | ||
| + | |||
| + |     CSIN(); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     tmpstate.pc = (memaddr) p4; | ||
| + |     p4t = create_process(&tmpstate); | ||
| + |     msgsend(p4t, NULL); | ||
| + |     msgrecv(p4t, NULL); | ||
| + |     msgsend(p4t, tmpstate.sp); | ||
| + |     msgrecv(p4t, NULL); | ||
| + | |||
| + |     if (geterrno() == 0) | ||
| + |         panic("p1 wrong errno: recv from p4 should abort, p4 terminated\n"); | ||
| + |     else { | ||
| + |         tty0print("p4 errno ok\n"); | ||
| + |     } | ||
| + |     tty0print("p4 completed\n"); | ||
| + | |||
| + |     CSIN(); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     tmpstate.pc = (memaddr) p5; | ||
| + |     p5t = create_process(&tmpstate); | ||
| + |     msgsend(p5t, NULL); | ||
| + |     msgrecv(p5t, NULL); | ||
| + | |||
| + |     if (p5sys == 1) tty0print("p5a usermode sys passup ok\n"); | ||
| + |     else panic("p5a usermode passup error\n"); | ||
| + | |||
| + |     if (p5send != 2) tty0print("p5a usermode msg priv kill is ok\n"); | ||
| + |     else panic("p5a usermode msg priv kill error\n"); | ||
| + | |||
| + |     CSIN(); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     tmpstate.pc = (memaddr) p6; | ||
| + |     p6t = create_process(&tmpstate); | ||
| + |     msgrecv(p6t, NULL); | ||
| + |     tty0print("p6 completed\n"); | ||
| + | |||
| + |     CSIN(); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     tmpstate.pc = (memaddr) p7; | ||
| + |     p7t = create_process(&tmpstate); | ||
| + |     msgrecv(p7t, NULL); | ||
| + |     tty0print("p7 completed\n"); | ||
| + |     //check total number of thread | ||
| + | |||
| + |     CSIN(); | ||
| + |     tmpstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     tmpstate.pc = (memaddr) p8; | ||
| + |     p8t = create_process(&tmpstate); | ||
| + |     msgrecv(p8t, NULL); | ||
| + |     tty0print("p8 completed\n"); | ||
| + | |||
| + |     tty0print("IT'S ALIVE! IT'S ALIVE! THE KERNEL IS ALIVE!\n"); | ||
| + |     HALT(); | ||
| + | } | ||
| + | |||
| + | #define MINLOOPTIME             10000 | ||
| + | #define LOOPNUM                 10000 | ||
| + | |||
| + | void p2(void) { | ||
| + |     struct tcb_t* p1t; | ||
| + |     uintptr_t value; | ||
| + |     cputime cpu_t1, cpu_t2; | ||
| + |     int i; | ||
| + | |||
| + |     tty0print("p2 started\n"); | ||
| + | |||
| + |     /* test: GET_MYTHREADID GET_PROCESSID GET_PARENTPROCID */ | ||
| + |     if (get_mythreadid() != p2t) | ||
| + |         panic("p2 get_mythreadid: wrong pid returned\n"); | ||
| + | |||
| + |     p1t = msgrecv(NULL, &value); | ||
| + |     if (value != SYNCCODE) | ||
| + |         panic("p2 recv: got the wrong value\n"); | ||
| + |     if (p1t != testt) | ||
| + |         panic("p2 recv: got the wrong sender\n"); | ||
| + |     if (get_processid(p1t) != get_parentprocid(get_processid(get_mythreadid()))) | ||
| + |         panic("p2 get_parentprocid get_processid error\n"); | ||
| + | |||
| + |     /* test: GET_CPUTIME */ | ||
| + | |||
| + |     cpu_t1 = getcputime(); | ||
| + |     /* delay for several milliseconds */ | ||
| + |     for (i = 1; i < LOOPNUM; i++); | ||
| + | |||
| + |     cpu_t2 = getcputime(); | ||
| + | |||
| + |     if ((cpu_t2 - cpu_t1) >= MINLOOPTIME) | ||
| + |         tty0print("p2 GET_CPUTIME sounds okay\n"); | ||
| + |     else | ||
| + |         panic("p2 GETCPUTIME sounds faulty\n"); | ||
| + | |||
| + |     msgsend(p1t, NULL); | ||
| + |     msgrecv(p1t, NULL); | ||
| + | |||
| + |     terminate_thread(); | ||
| + | |||
| + |     panic("p2 survived TERMINATE_THREAD\n"); | ||
| + | } | ||
| + | |||
| + | #define PSEUDOCLOCK 100000 | ||
| + | #define NWAIT 10 | ||
| + | |||
| + | void p3(void) { | ||
| + |     tty0print("p3 started\n"); | ||
| + | |||
| + |     cputime time1, time2; | ||
| + |     int i; | ||
| + |     time1 = getTODLO(); | ||
| + |     for (i = 0; i < NWAIT; i++) { | ||
| + |         waitforclock(); | ||
| + |     } | ||
| + |     time2 = getTODLO(); | ||
| + | |||
| + |     if ((time2 - time1) < (PSEUDOCLOCK * (NWAIT - 1))) { | ||
| + |         panic("WAITCLOCK too small\n"); | ||
| + |     } else if ((time2 - time1) > (PSEUDOCLOCK * (NWAIT + 1))) { | ||
| + |         panic("WAITCLOCK too big\n"); | ||
| + |     } else { | ||
| + |         tty0print("WAITCLOCK OK\n"); | ||
| + |     } | ||
| + | |||
| + |     msgsend(testt, "NULL"); | ||
| + | |||
| + |     terminate_process(); | ||
| + | |||
| + |     panic("p3 survived TERMINATE_PROCESS\n"); | ||
| + | } | ||
| + | |||
| + | void p4(void) { | ||
| + |     static int p4inc = 1; | ||
| + |     struct tcb_t* parent; | ||
| + |     struct tcb_t* child; | ||
| + |     state_t p4childstate; | ||
| + | |||
| + |     switch (p4inc) { | ||
| + |         case 1: | ||
| + |             tty0print("first incarnation of p4 starts\n"); | ||
| + |             break; | ||
| + |         case 2: | ||
| + |             tty0print("second incarnation of p4 starts\n"); | ||
| + |             break; | ||
| + |     } | ||
| + |     p4inc++; | ||
| + |     parent = msgrecv(NULL, NULL); | ||
| + |     msgsend(parent, NULL); | ||
| + | |||
| + |     msgrecv(NULL, NULL); | ||
| + |     /* only the first incarnation reaches this point */ | ||
| + | |||
| + |     STST(&p4childstate); | ||
| + |     CSIN(); | ||
| + |     p4childstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     p4childstate.pc = (memaddr) p4; | ||
| + | |||
| + |     child = create_process(&p4childstate); | ||
| + |     msgsend(child, NULL); | ||
| + |     msgrecv(child, NULL); | ||
| + | |||
| + |     terminate_process(); | ||
| + | |||
| + |     panic("p4 survived TERMINATE_PROCESS\n"); | ||
| + | } | ||
| + | |||
| + | void p5a(); | ||
| + | |||
| + | void p5p(void) { | ||
| + |     struct tcb_t* sender; | ||
| + |     state_t* state; | ||
| + |     short int should_terminate = 0; | ||
| + |     for (;;) { | ||
| + |         sender = msgrecv(NULL, &state); | ||
| + |         switch (CAUSE_EXCCODE_GET(state->CP15_Cause)) { | ||
| + |             case EXC_RESERVEDINSTR: | ||
| + |                 tty0print("p5a got RESERVEDINSTR\n"); | ||
| + |                 should_terminate = 1; | ||
| + |                 break; | ||
| + |             default: | ||
| + |                 PANIC(); | ||
| + |         } | ||
| + |         if (should_terminate) { | ||
| + |             terminate_process(); | ||
| + |         } else { | ||
| + |             msgsend(sender, NULL); | ||
| + |         } | ||
| + |     } | ||
| + | } | ||
| + | |||
| + | void p5m(void) { | ||
| + |     struct tcb_t* sender; | ||
| + |     state_t* state; | ||
| + |     for (;;) { | ||
| + |         sender = msgrecv(NULL, &state); | ||
| + |         switch (CAUSE_EXCCODE_GET(state->CP15_Cause)) { | ||
| + |             default: | ||
| + |                 tty0print("p5 mem error passup okay\n"); | ||
| + |                 sender->t_s.pc = (memaddr) p5a; | ||
| + |                 break; | ||
| + |         } | ||
| + |         msgsend(sender, NULL); | ||
| + |     } | ||
| + | } | ||
| + | |||
| + | void p5s(void) { | ||
| + |     uintptr_t retval; | ||
| + |     struct tcb_t* sender; | ||
| + |     state_t* state; | ||
| + |     for (;;) { | ||
| + |         sender = msgrecv(NULL, &state); | ||
| + |         switch (state->a1) { | ||
| + |             case 42: | ||
| + |                 retval = 42; | ||
| + |                 break; | ||
| + |             case 3: | ||
| + |                 retval = 0; | ||
| + |                 break; | ||
| + |             default: | ||
| + |                 retval = -1; | ||
| + |                 break; | ||
| + |         } | ||
| + |         state->a1 = retval; | ||
| + |         msgsend(sender, NULL); | ||
| + |     } | ||
| + | } | ||
| + | |||
| + | #define BADADDR 0xFFFFFFFF | ||
| + | |||
| + | void p5(void) { | ||
| + |     state_t mgrstate; | ||
| + |     uintptr_t retval = 200; | ||
| + | |||
| + |     tty0print("p5 started\n"); | ||
| + | |||
| + |     STST(&mgrstate); | ||
| + |     msgrecv(NULL, &mgrstate.pc); | ||
| + |     CSIN(); | ||
| + |     mgrstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     mgrstate.pc = (memaddr) p5p; | ||
| + |     setpgmmgr(create_thread(&mgrstate)); | ||
| + | |||
| + |     CSIN(); | ||
| + |     mgrstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     mgrstate.pc = (memaddr) p5m; | ||
| + |     settlbmgr(create_thread(&mgrstate)); | ||
| + | |||
| + |     CSIN(); | ||
| + |     mgrstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     mgrstate.pc = (memaddr) p5s; | ||
| + | |||
| + |     setsysmgr(create_thread(&mgrstate)); | ||
| + | |||
| + |     /* this should me handled by p5s */ | ||
| + | |||
| + |     retval = SYSCALL(42, 42, 42, 42); | ||
| + |     if (retval == 42) | ||
| + |         tty0print("p5 syscall passup okay\n"); | ||
| + |     else | ||
| + |         panic("p5 syscall passup error\n"); | ||
| + | |||
| + |     *((memaddr*) BADADDR) = 0; | ||
| + | |||
| + |     panic("p5 survived mem error"); | ||
| + | } | ||
| + | |||
| + | void p5a(void) { | ||
| + |     uintptr_t retval = 200; | ||
| + |     /* switch to user mode */ | ||
| + |     setSTATUS((getSTATUS() & STATUS_CLEAR_MODE) | STATUS_USER_MODE); | ||
| + | |||
| + |     p5sys = 0; | ||
| + | |||
| + |     retval = SYSCALL(3, 2, 1, 0); | ||
| + |     if (retval == 0) { | ||
| + |         p5sys = 1; | ||
| + |     } else { | ||
| + |         p5sys = 2; | ||
| + |     } | ||
| + | |||
| + |     /* this should fail! */ | ||
| + |     msgsend(SSI, NULL); | ||
| + | |||
| + |     p5send = 2; | ||
| + |     //infinite loop: this should not be reaached. | ||
| + |     // The loop is added in order to allow debug in case of failure (manually check p5send) | ||
| + |     for (;;); | ||
| + | } | ||
| + | |||
| + | void p6(void) { | ||
| + |     tty0print("p6 started\n"); | ||
| + |     *((memaddr*) BADADDR) = 0; | ||
| + | |||
| + |     panic("p6 survived mem error"); | ||
| + | } | ||
| + | |||
| + | void p7(void) { | ||
| + |     tty0print("p7 started\n"); | ||
| + | |||
| + |     SYSCALL(3, 2, 1, 0); | ||
| + | |||
| + |     panic("p7 survived syscall without manager"); | ||
| + | } | ||
| + | |||
| + | void p8child(), p8grandchild(); | ||
| + | |||
| + | void p8(void) { | ||
| + |     tty0print("p8 started\n"); | ||
| + |     state_t childstate; | ||
| + |     struct tcb_t* pid = get_mythreadid(); | ||
| + |     STST(&childstate); | ||
| + |     childstate.pc = (memaddr) p8child; | ||
| + |     CSIN(); | ||
| + |     childstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     msgsend(create_process(&childstate), pid); | ||
| + |     CSIN(); | ||
| + |     childstate.sp = (stackalloc -= QPAGE); | ||
| + |     CSOUT; | ||
| + |     msgsend(create_process(&childstate), pid); | ||
| + |     msgrecv(NULL, NULL); | ||
| + |     msgrecv(NULL, NULL); | ||
| + |     msgrecv(NULL, NULL); | ||
| + |     msgrecv(NULL, NULL); | ||
| + |     msgrecv(NULL, NULL); | ||
| + |     msgrecv(NULL, NULL); | ||
| + | |||
| + |     terminate_process(); | ||
| + | |||
| + |     panic("p8 survived TERMINATE_PROCESS\n"); | ||
| + | } | ||
| + | |||
| + | #define NGRANDCHILDREN 3 | ||
| + | |||
| + | void p8child() { | ||
| + |     int i; | ||
| + |     state_t childstate; | ||
| + |     struct tcb_t* ppid; | ||
| + |     msgrecv(NULL, &ppid); | ||
| + |     for (i = 0; i < NGRANDCHILDREN; i++) { | ||
| + |         STST(&childstate); | ||
| + |         childstate.pc = (memaddr) p8grandchild; | ||
| + |         CSIN(); | ||
| + |         childstate.sp = (stackalloc -= QPAGE); | ||
| + |         CSOUT; | ||
| + |         msgsend(create_process(&childstate), ppid); | ||
| + |     } | ||
| + |     msgrecv(NULL, NULL); | ||
| + | |||
| + |     panic("p8child survived parent termination\n"); | ||
| + | } | ||
| + | |||
| + | void p8grandchild() { | ||
| + |     struct tcb_t* gpid; | ||
| + |     msgrecv(NULL, &gpid); | ||
| + |     msgsend(gpid, NULL); | ||
| + |     msgrecv(NULL, NULL); | ||
| + | |||
| + |     panic("p8grandchild survived grandparent termination\n"); | ||
| + | } | ||
| + | </source> | ||
Latest revision as of 17:56, 10 June 2017
Documento specifiche
System call
send
int send(struct tcb_t *dest, uintptr_t msg) a0 = 1, a1 = dest. a2 = msg
recv
struct tcb_t *recv(struct tcb_t *src, uintptr_t *pmsg) a0 = 2, a1 = src, a2 = *pmsg
NB: system call 1 e 2 possono essere chiamate solo in kernel mode system call 0 e' errore system call non 1 o 2 vengono trasformate in messaggi al thread definito tramite SETSYSMGR se esiste altrimenti msg SETPGMMGR se esiste altrimenti TERMINATE_THREAD
servizi della SSI
solo da kernel mode
| Nome servizio | Codice servizio | Parametri | Tipo di ritorno | Note | 
|---|---|---|---|---|
| GET_ERRNO | 0 | int | 0 okay | |
| CREATE_PROCESS | 1 | state_t* | strcut tcb_t* | NULL per errore | 
| CREATE_THREAD | 2 | state_t* | strcut tcb_t* | NULL per errore | 
| TERMINATE_PROCESS | 3 | state_t* | ||
| TERMINATE_THREAD | 4 | state_t* | (se e' l'ultimo thread diventa come TERMINATE_PROCESS) | |
| SETPGMMGR | 5 | struct tcb_t* | SET*MGR restituiscono lo stesso tcb_t, NULL per errore. (puà essere chiamato una volta sola per processo, TERMINATE_PROCESS alla seconda chiamata).  | |
| SETTLBMGR | 6 | struct tcb_t* | ||
| SETSYSMGR | 7 | struct tcb_t* | ||
| GETCPUTIME | 8 | unsigned int | in microsecondi, relativo al processo | |
| WAIT_FOR_CLOCK | 9 | Sospende il thread fino al prossimo tick di pseudo-clock | ||
| DO_IO | 10 | DEVICE_REG_ADDR, COMMAND, ... | status | 
 | 
| GET_PROCESSID | 11 | struct tcb_t* | struct pcb_t * | |
| GET_MYTHREADID | 12 | struct tcb_t * | ||
| GET_PARENTPROCID | 13 | pcb_t* | pcb_t* | 
Sorgenti di test
nucleus.h
#ifndef NUCLEUS_H
#define NUCLEUS_H
#include <stdint.h>
#include "mikabooq.h"
/* Syscall mnemonic values */
#define SYS_SEND    1
#define SYS_RECV    2
/* SSI requests */
#define GET_ERRNO 0
#define CREATE_PROCESS 1
#define CREATE_THREAD 2
#define TERMINATE_PROCESS 3
#define TERMINATE_THREAD 4
#define SETPGMMGR 5
#define SETTLBMGR 6
#define SETSYSMGR 7
#define GET_CPUTIME 8
#define WAIT_FOR_CLOCK 9
#define DO_IO 10
#define GET_PROCESSID 11
#define GET_MYTHREADID 12
#define GET_PARENTPROCID 13
typedef uintptr_t memaddr;
typedef uintptr_t cputime;
typedef uintptr_t devaddr;
extern void* SSI;
#define msgsend(dest, payload) (SYSCALL(SYS_SEND,(unsigned int) (dest),(unsigned int) (payload),0))
#define msgrecv(source, reply) (((struct tcb_t *) SYSCALL(SYS_RECV,(unsigned int) (source),(unsigned int) (reply),0)))
static inline uintptr_t geterrno(void) {
    uintptr_t retval;
    struct {
        uintptr_t reqtag;
    } req = {GET_ERRNO};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline struct tcb_t* create_process(state_t* s) {
    struct tcb_t* retval;
    struct {
        uintptr_t reqtag;
        state_t* s;
    } req = {CREATE_PROCESS, s};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline struct tcb_t* create_thread(state_t* s) {
    struct tcb_t* retval;
    struct {
        uintptr_t reqtag;
        state_t* s;
    } req = {CREATE_THREAD, s};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline void terminate_process(void) {
    struct {
        uintptr_t reqtag;
    } req = {TERMINATE_PROCESS};
    msgsend(SSI, &req);
    msgrecv(SSI, NULL);
}
static inline void terminate_thread(void) {
    struct {
        uintptr_t reqtag;
    } req = {TERMINATE_THREAD};
    msgsend(SSI, &req);
    msgrecv(SSI, NULL);
}
static inline struct tcb_t* setpgmmgr(struct tcb_t* s) {
    struct tcb_t* retval;
    struct {
        uintptr_t reqtag;
        struct tcb_t* s;
    } req = {SETPGMMGR, s};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline struct tcb_t* settlbmgr(struct tcb_t* s) {
    struct tcb_t* retval;
    struct {
        uintptr_t reqtag;
        struct tcb_t* s;
    } req = {SETTLBMGR, s};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline struct tcb_t* setsysmgr(struct tcb_t* s) {
    struct tcb_t* retval;
    struct {
        uintptr_t reqtag;
        struct tcb_t* s;
    } req = {SETSYSMGR, s};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline cputime getcputime(void) {
    uintptr_t retval;
    struct {
        uintptr_t reqtag;
    } req = {GET_CPUTIME};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline void waitforclock(void) {
    struct {
        uintptr_t reqtag;
    } req = {WAIT_FOR_CLOCK};
    msgsend(SSI, &req);
    msgrecv(SSI, NULL);
}
static inline unsigned int do_io(devaddr device, uintptr_t command, uintptr_t data1, uintptr_t data2) {
    uintptr_t retval;
    struct {
        uintptr_t reqtag;
        devaddr device;
        uintptr_t command;
        uintptr_t data1;
        uintptr_t data2;
    } req = {DO_IO, device, command, data1, data2};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
#define do_disk_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0)
#define do_tape_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0)
#define do_net_io(dev, cmd, d1, d2) do_io((dev),(cmd),(d1),(d2))
#define do_printer_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0)
#define do_terminal_io(dev, cmd) do_io((dev),(cmd),0,0)
static inline struct pcb_t* get_processid(struct tcb_t* s) {
    struct pcb_t* retval;
    struct {
        uintptr_t reqtag;
        struct tcb_t* s;
    } req = {GET_PROCESSID, s};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline struct pcb_t* get_parentprocid(struct pcb_t* s) {
    struct pcb_t* retval;
    struct {
        uintptr_t reqtag;
        struct pcb_t* s;
    } req = {GET_PARENTPROCID, s};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
static inline struct tcb_t* get_mythreadid(void) {
    struct tcb_t* retval;
    struct {
        uintptr_t reqtag;
    } req = {GET_MYTHREADID};
    msgsend(SSI, &req);
    msgrecv(SSI, &retval);
    return retval;
}
#endif
p2test.c
/*
 * Copyright (C) 2017 Renzo Davoli
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <uARMconst.h>
#include <uARMtypes.h>
#include <libuarm.h>
#include "nucleus.h"
#define QPAGE FRAME_SIZE
#define TERM0ADDR               0x24C
static struct tcb_t* printid;
static void ttyprintstring(devaddr device, char* s) {
    uintptr_t status;
    for (; *s; s++) {
        status = do_terminal_io(device, DEV_TTRS_C_TRSMCHAR | (*s << 8));
        switch (status & 0xff) {
            case DEV_S_READY:
            case DEV_TTRS_S_CHARTRSM:
                break;
            default:
                return;
        }
    }
}
void tty0out_thread(void) {
    uintptr_t payload;
    struct tcb_t* sender;
    for (;;) {
        sender = msgrecv(NULL, &payload);
        ttyprintstring(TERM0ADDR, (char*) payload);
        msgsend(sender, NULL);
    }
}
static inline void tty0print(char* s) {
    msgsend(printid, s);
    msgrecv(printid, NULL);
}
static inline void panic(char* s) {
    tty0print("!!! PANIC: ");
    tty0print(s);
    PANIC();
}
static struct tcb_t* csid;
static inline void CSIN() {
    msgsend(csid, NULL);
    msgrecv(csid, NULL);
}
#define CSOUT msgsend(csid, NULL)
void cs_thread(void) {
    struct tcb_t* sender;
    for (;;) {
        sender = msgrecv(NULL, NULL);
        msgsend(sender, NULL);
        msgrecv(sender, NULL);
    }
}
#define SYNCCODE 0x01000010
void p2(), p3(), p4(), p5(), p6(), p7(), p8();
struct tcb_t* testt, * p2t, * p3t, * p4t, * p5t, * p6t, * p7t, * p8t;
static state_t tmpstate;
memaddr stackalloc;
uintptr_t p5sys = 0;
uintptr_t p5send = 0;
void test(void) {
    ttyprintstring(TERM0ADDR, "NUCLEUS TEST: starting...\n");
    STST(&tmpstate);
    stackalloc = (tmpstate.sp + (QPAGE - 1)) & (~(QPAGE - 1));
    tmpstate.sp = (stackalloc -= QPAGE);
    tmpstate.pc = (memaddr) tty0out_thread;
    tmpstate.cpsr = STATUS_ALL_INT_ENABLE(tmpstate.cpsr);
    printid = create_thread(&tmpstate);
    tty0print("NUCLEUS: first msg printed by tty0out_thread\n");
    testt = get_mythreadid();
    tmpstate.sp = (stackalloc -= QPAGE);
    tmpstate.pc = (memaddr) cs_thread;
    csid = create_process(&tmpstate);
    tty0print("NUCLEUS: critical section thread started\n");
    CSIN();
    tmpstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    tmpstate.pc = (memaddr) p2;
    p2t = create_process(&tmpstate);
    msgsend(p2t, SYNCCODE);
    msgrecv(p2t, NULL);
    tty0print("p2 completed\n");
    CSIN();
    tmpstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    tmpstate.pc = (memaddr) p3;
    p3t = create_process(&tmpstate);
    msgrecv(p3t, NULL);
    tty0print("p3 completed\n");
    CSIN();
    tmpstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    tmpstate.pc = (memaddr) p4;
    p4t = create_process(&tmpstate);
    msgsend(p4t, NULL);
    msgrecv(p4t, NULL);
    msgsend(p4t, tmpstate.sp);
    msgrecv(p4t, NULL);
    if (geterrno() == 0)
        panic("p1 wrong errno: recv from p4 should abort, p4 terminated\n");
    else {
        tty0print("p4 errno ok\n");
    }
    tty0print("p4 completed\n");
    CSIN();
    tmpstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    tmpstate.pc = (memaddr) p5;
    p5t = create_process(&tmpstate);
    msgsend(p5t, NULL);
    msgrecv(p5t, NULL);
    if (p5sys == 1) tty0print("p5a usermode sys passup ok\n");
    else panic("p5a usermode passup error\n");
    if (p5send != 2) tty0print("p5a usermode msg priv kill is ok\n");
    else panic("p5a usermode msg priv kill error\n");
    CSIN();
    tmpstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    tmpstate.pc = (memaddr) p6;
    p6t = create_process(&tmpstate);
    msgrecv(p6t, NULL);
    tty0print("p6 completed\n");
    CSIN();
    tmpstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    tmpstate.pc = (memaddr) p7;
    p7t = create_process(&tmpstate);
    msgrecv(p7t, NULL);
    tty0print("p7 completed\n");
    //check total number of thread
    CSIN();
    tmpstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    tmpstate.pc = (memaddr) p8;
    p8t = create_process(&tmpstate);
    msgrecv(p8t, NULL);
    tty0print("p8 completed\n");
    tty0print("IT'S ALIVE! IT'S ALIVE! THE KERNEL IS ALIVE!\n");
    HALT();
}
#define MINLOOPTIME             10000
#define LOOPNUM                 10000
void p2(void) {
    struct tcb_t* p1t;
    uintptr_t value;
    cputime cpu_t1, cpu_t2;
    int i;
    tty0print("p2 started\n");
    /* test: GET_MYTHREADID GET_PROCESSID GET_PARENTPROCID */
    if (get_mythreadid() != p2t)
        panic("p2 get_mythreadid: wrong pid returned\n");
    p1t = msgrecv(NULL, &value);
    if (value != SYNCCODE)
        panic("p2 recv: got the wrong value\n");
    if (p1t != testt)
        panic("p2 recv: got the wrong sender\n");
    if (get_processid(p1t) != get_parentprocid(get_processid(get_mythreadid())))
        panic("p2 get_parentprocid get_processid error\n");
    /* test: GET_CPUTIME */
    cpu_t1 = getcputime();
    /* delay for several milliseconds */
    for (i = 1; i < LOOPNUM; i++);
    cpu_t2 = getcputime();
    if ((cpu_t2 - cpu_t1) >= MINLOOPTIME)
        tty0print("p2 GET_CPUTIME sounds okay\n");
    else
        panic("p2 GETCPUTIME sounds faulty\n");
    msgsend(p1t, NULL);
    msgrecv(p1t, NULL);
    terminate_thread();
    panic("p2 survived TERMINATE_THREAD\n");
}
#define PSEUDOCLOCK 100000
#define NWAIT 10
void p3(void) {
    tty0print("p3 started\n");
    cputime time1, time2;
    int i;
    time1 = getTODLO();
    for (i = 0; i < NWAIT; i++) {
        waitforclock();
    }
    time2 = getTODLO();
    if ((time2 - time1) < (PSEUDOCLOCK * (NWAIT - 1))) {
        panic("WAITCLOCK too small\n");
    } else if ((time2 - time1) > (PSEUDOCLOCK * (NWAIT + 1))) {
        panic("WAITCLOCK too big\n");
    } else {
        tty0print("WAITCLOCK OK\n");
    }
    msgsend(testt, "NULL");
    terminate_process();
    panic("p3 survived TERMINATE_PROCESS\n");
}
void p4(void) {
    static int p4inc = 1;
    struct tcb_t* parent;
    struct tcb_t* child;
    state_t p4childstate;
    switch (p4inc) {
        case 1:
            tty0print("first incarnation of p4 starts\n");
            break;
        case 2:
            tty0print("second incarnation of p4 starts\n");
            break;
    }
    p4inc++;
    parent = msgrecv(NULL, NULL);
    msgsend(parent, NULL);
    msgrecv(NULL, NULL);
    /* only the first incarnation reaches this point */
    STST(&p4childstate);
    CSIN();
    p4childstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    p4childstate.pc = (memaddr) p4;
    child = create_process(&p4childstate);
    msgsend(child, NULL);
    msgrecv(child, NULL);
    terminate_process();
    panic("p4 survived TERMINATE_PROCESS\n");
}
void p5a();
void p5p(void) {
    struct tcb_t* sender;
    state_t* state;
    short int should_terminate = 0;
    for (;;) {
        sender = msgrecv(NULL, &state);
        switch (CAUSE_EXCCODE_GET(state->CP15_Cause)) {
            case EXC_RESERVEDINSTR:
                tty0print("p5a got RESERVEDINSTR\n");
                should_terminate = 1;
                break;
            default:
                PANIC();
        }
        if (should_terminate) {
            terminate_process();
        } else {
            msgsend(sender, NULL);
        }
    }
}
void p5m(void) {
    struct tcb_t* sender;
    state_t* state;
    for (;;) {
        sender = msgrecv(NULL, &state);
        switch (CAUSE_EXCCODE_GET(state->CP15_Cause)) {
            default:
                tty0print("p5 mem error passup okay\n");
                sender->t_s.pc = (memaddr) p5a;
                break;
        }
        msgsend(sender, NULL);
    }
}
void p5s(void) {
    uintptr_t retval;
    struct tcb_t* sender;
    state_t* state;
    for (;;) {
        sender = msgrecv(NULL, &state);
        switch (state->a1) {
            case 42:
                retval = 42;
                break;
            case 3:
                retval = 0;
                break;
            default:
                retval = -1;
                break;
        }
        state->a1 = retval;
        msgsend(sender, NULL);
    }
}
#define BADADDR 0xFFFFFFFF
void p5(void) {
    state_t mgrstate;
    uintptr_t retval = 200;
    tty0print("p5 started\n");
    STST(&mgrstate);
    msgrecv(NULL, &mgrstate.pc);
    CSIN();
    mgrstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    mgrstate.pc = (memaddr) p5p;
    setpgmmgr(create_thread(&mgrstate));
    CSIN();
    mgrstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    mgrstate.pc = (memaddr) p5m;
    settlbmgr(create_thread(&mgrstate));
    CSIN();
    mgrstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    mgrstate.pc = (memaddr) p5s;
    setsysmgr(create_thread(&mgrstate));
    /* this should me handled by p5s */
    retval = SYSCALL(42, 42, 42, 42);
    if (retval == 42)
        tty0print("p5 syscall passup okay\n");
    else
        panic("p5 syscall passup error\n");
    *((memaddr*) BADADDR) = 0;
    panic("p5 survived mem error");
}
void p5a(void) {
    uintptr_t retval = 200;
    /* switch to user mode */
    setSTATUS((getSTATUS() & STATUS_CLEAR_MODE) | STATUS_USER_MODE);
    p5sys = 0;
    retval = SYSCALL(3, 2, 1, 0);
    if (retval == 0) {
        p5sys = 1;
    } else {
        p5sys = 2;
    }
    /* this should fail! */
    msgsend(SSI, NULL);
    p5send = 2;
    //infinite loop: this should not be reaached.
    // The loop is added in order to allow debug in case of failure (manually check p5send)
    for (;;);
}
void p6(void) {
    tty0print("p6 started\n");
    *((memaddr*) BADADDR) = 0;
    panic("p6 survived mem error");
}
void p7(void) {
    tty0print("p7 started\n");
    SYSCALL(3, 2, 1, 0);
    panic("p7 survived syscall without manager");
}
void p8child(), p8grandchild();
void p8(void) {
    tty0print("p8 started\n");
    state_t childstate;
    struct tcb_t* pid = get_mythreadid();
    STST(&childstate);
    childstate.pc = (memaddr) p8child;
    CSIN();
    childstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    msgsend(create_process(&childstate), pid);
    CSIN();
    childstate.sp = (stackalloc -= QPAGE);
    CSOUT;
    msgsend(create_process(&childstate), pid);
    msgrecv(NULL, NULL);
    msgrecv(NULL, NULL);
    msgrecv(NULL, NULL);
    msgrecv(NULL, NULL);
    msgrecv(NULL, NULL);
    msgrecv(NULL, NULL);
    terminate_process();
    panic("p8 survived TERMINATE_PROCESS\n");
}
#define NGRANDCHILDREN 3
void p8child() {
    int i;
    state_t childstate;
    struct tcb_t* ppid;
    msgrecv(NULL, &ppid);
    for (i = 0; i < NGRANDCHILDREN; i++) {
        STST(&childstate);
        childstate.pc = (memaddr) p8grandchild;
        CSIN();
        childstate.sp = (stackalloc -= QPAGE);
        CSOUT;
        msgsend(create_process(&childstate), ppid);
    }
    msgrecv(NULL, NULL);
    panic("p8child survived parent termination\n");
}
void p8grandchild() {
    struct tcb_t* gpid;
    msgrecv(NULL, &gpid);
    msgsend(gpid, NULL);
    msgrecv(NULL, NULL);
    panic("p8grandchild survived grandparent termination\n");
}