Specifiche phase 2 2016/17

From Sistemi Operativi
Revision as of 19:50, 8 May 2017 by FedericoB (talk | contribs) (Aggiunti sorgenti di test proposti da studenti e accettati dal prof)
Jump to navigation Jump to search

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