Difference between revisions of "Specifiche phase 2 2016/17"

From Sistemi Operativi
Jump to navigation Jump to search
(Aggiunto link al documento delle specifiche di progetto)
 
(2 intermediate revisions by the same user 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 17:
  
 
solo da kernel mode
 
solo da kernel mode
*  GET_ERRNO 0, int
 
0 okay
 
*  CREATE_PROCESS  1, state_t * -> struct tcb_t *
 
NULL per errore
 
*  CREATE_THREAD 2,  state_t * -> struct tcb_*
 
NULL per errore
 
*  TERMINATE_PROCESS 3
 
*  TERMINATE_THREAD 4
 
(se e' l'ultimo thread diventa come TERMINATE_PROCESS)
 
*  SETPGMMGR 5, struct tcb_t *
 
*  SETTLBMGR 6, struct tcb_t *
 
*  SETSYSMGR 7, struct tcb_t *
 
SET*MSG restituiscono lo stesso tcb_t, NULL per errore.
 
(una volta sola per processo, TERMINATE_PROCESS alla seconda chiamata).
 
sender tcb_t che ha causato la trap, msg codifica della trap.
 
*  GETCPUTIME 8 -> time
 
(tempo relativo al proc)
 
*  WAIT_FOR_CLOCK 9
 
restituisce il numero di tick dall'accensione del sistema.
 
*  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
 
  
 +
{| 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* ||
 +
|}
  
* GET_PROCESSID 11 struct tcb_t * -> struct pcb_t *
+
==Sorgenti di test==
* GET_THREAD 12 -> struct tcb_t *
+
===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

Link al documento

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).
I manager ricevono messaggi che hanno come sender il tcb_t del thread che ha causato la trap,
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

#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");
}