跳到主要內容

記憶體管理-exec

exec 係用以將現在的處理序映射替換成另一個。亦即我們可從硬碟讀取另一個可執行檔,用它替換掉剛剛被 fork 出來的子處理序。

為測試 exec,勢必先有一個簡單的可執行檔(echo)。因此,我們將製作一個類似於 C 的 Run-time:把過去可供給應用程式使用的函數單獨連結成一個檔,以後直接連結即可。 目前包括:
  • 兩個真正的系統使用 sendrec 和 printx
    • lib/syscall.asm
  • 字串操作 memcopy、memset、strcpy、strlen
    • lib/string.asm
  • FS 的介面
    • lib/open.c
    • lib/read.c
    • lib/write.c
    • lib/close.c
    • lib/unlink.c
  • MM 的介面
    • lib/fork.c
    • lib/exit.c
    • lib/wait.c
  • SYS 的介面
    • lib/getpid.c
  • 其他
    • lib/misc.c
    • lib/vsprintf.c
    • lib/printf.c
將以上檔案單獨連結成一個檔:oragescrt.a
ar rcs lib/orangescrt.a lib/syscall.asm lib/string.asm \
   lib/open.c lib/read.c lib/write.c lib/close.c lib/unlink.c \
   lib/fork.c lib/exit.c lib/wait.c \
   lib/getpid.c \
   lib/misc.c lib/vsprintf.c lib/printf.c
並寫一個最簡單的 echo(與 pwd)。

最後,想「安裝」一些應用程式到我們的檔案系統中,有如下工作:

  • 編寫應用程式,並編譯連結
  • 將連結好的應用程式打成一個 tar 包:inst.tar
  • 將 inst.tar 用工具 dd 寫入磁片(映射)的某段特定磁區(假設這一段的第一個扇區的磁區號為 X)
  • 啟動系統,這時 mkfs() 會在檔案系統中建立一個新檔 cmd.tar,它的 inode 中的 i_start_sect 成員會被設為 X
  • 在某個處理序中-比如 Init-將 cmd.tar 解包,將其中包含的檔存入文件系統



程式碼

command/echo.c

#include "stdio.h"

int main(int argc, char * argv[])
{
 int i;
 for (i = 1; i < argc; i++)
  printf("%s%s", i == 1 ? "" : " ", argv[i]);
 printf("\n");

 return 0;
}

command/Makefile

# commands/Makefile

#ENTRYPOINT = 0x1000
HD  = ../80m.img

ASM  = nasm
DASM  = objdump
CC  = gcc
LD  = ld
ASMFLAGS = -I ../include/ -f elf
CFLAGS  = -I ../include/ -c -fno-builtin -fno-stack-protector -Wall
LDFLAGS  = -Ttext 0x1000
DASMFLAGS = -D
LIB  = ../lib/orangescrt.a
BIN  = echo pwd

# All Phony Targets
.PHONY : everything final clean realclean disasm all install

# Default starting position
everything : $(BIN)

install : all clean
 cp ../kernel.bin ./ -v
 tar vcf inst.tar kernel.bin $(BIN)
 dd if=inst.tar of=$(HD) seek=`echo "obase=10;ibase=16;(\`egrep -e '^ROOT_BASE' ../boot/include/load.inc | sed -e 's/.*0x//g'\`+\`egrep -e '#define[[:space:]]*INSTALL_START_SECT' ../include/sys/config.h | sed -e 's/.*0x//g'\`)*200" | bc` bs=1 count=`ls -l inst.tar | awk -F " " '{print $$5}'` conv=notrunc

all : realclean everything

final : all clean

clean :
 rm -f *.o

realclean :
 rm -f $(BIN) *.o

kernel.bin :
 cp ../kernel.bin ./

start.o : start.asm
 $(ASM) $(ASMFLAGS) -o $@ $<

echo.o: echo.c ../include/type.h ../include/stdio.h
 $(CC) $(CFLAGS) -o $@ $<

echo : echo.o start.o $(LIB)
 $(LD) $(LDFLAGS) -o $@ $?

pwd.o: pwd.c ../include/type.h ../include/stdio.h
 $(CC) $(CFLAGS) -o $@ $<

pwd : pwd.o start.o $(LIB)
 $(LD) $(LDFLAGS) -o $@ $?

command/pwd.c

#include "type.h"
#include "stdio.h"

int main(int argc, char * argv[])
{
 printf("/\n");
 return 0;
}

command/start.asm

;; start.asm

extern main
extern exit

bits 32

[section .text]

global _start

_start:
 push eax
 push ecx
 call main
 ;; need not clean up the stack here

 push eax
 call exit

 hlt ; should never arrive here

kernel/main.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            main.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"


/*****************************************************************************
 *                               kernel_main
 *****************************************************************************/
/**
 * jmp from kernel.asm::_start.
 *
 *****************************************************************************/
PUBLIC int kernel_main()
{
 disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
   "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");

 int i, j, eflags, prio;
        u8  rpl;
        u8  priv; /* privilege */

 struct task * t;
 struct proc * p = proc_table;

 char * stk = task_stack + STACK_SIZE_TOTAL;

 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p++,t++) {
  if (i >= NR_TASKS + NR_NATIVE_PROCS) {
   p->p_flags = FREE_SLOT;
   continue;
  }

         if (i < NR_TASKS) {     /* TASK */
                        t = task_table + i;
                        priv = PRIVILEGE_TASK;
                        rpl     = RPL_TASK;
                        eflags  = 0x1202;/* IF=1, IOPL=1, bit 2 is always 1 */
   prio    = 15;
                }
                else {                  /* USER PROC */
                        t = user_proc_table + (i - NR_TASKS);
                        priv = PRIVILEGE_USER;
                        rpl     = RPL_USER;
                        eflags  = 0x202; /* IF=1, bit 2 is always 1 */
   prio    = 5;
                }

  strcpy(p->name, t->name); /* name of the process */
  p->p_parent = NO_TASK;

  if (strcmp(t->name, "INIT") != 0) {
   p->ldts[INDEX_LDT_C]  = gdt[SELECTOR_KERNEL_CS >> 3];
   p->ldts[INDEX_LDT_RW] = gdt[SELECTOR_KERNEL_DS >> 3];

   /* change the DPLs */
   p->ldts[INDEX_LDT_C].attr1  = DA_C   | priv << 5;
   p->ldts[INDEX_LDT_RW].attr1 = DA_DRW | priv << 5;
  }
  else {  /* INIT process */
   unsigned int k_base;
   unsigned int k_limit;
   int ret = get_kernel_map(&k_base, &k_limit);
   assert(ret == 0);
   init_desc(&p->ldts[INDEX_LDT_C],
      0, /* bytes before the entry point
          * are useless (wasted) for the
          * INIT process, doesn't matter
          */
      (k_base + k_limit) >> LIMIT_4K_SHIFT,
      DA_32 | DA_LIMIT_4K | DA_C | priv << 5);

   init_desc(&p->ldts[INDEX_LDT_RW],
      0, /* bytes before the entry point
          * are useless (wasted) for the
          * INIT process, doesn't matter
          */
      (k_base + k_limit) >> LIMIT_4K_SHIFT,
      DA_32 | DA_LIMIT_4K | DA_DRW | priv << 5);
  }

  p->regs.cs = INDEX_LDT_C << 3 | SA_TIL | rpl;
  p->regs.ds =
   p->regs.es =
   p->regs.fs =
   p->regs.ss = INDEX_LDT_RW << 3 | SA_TIL | rpl;
  p->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl;
  p->regs.eip = (u32)t->initial_eip;
  p->regs.esp = (u32)stk;
  p->regs.eflags = eflags;

  p->ticks = p->priority = prio;

  p->p_flags = 0;
  p->p_msg = 0;
  p->p_recvfrom = NO_TASK;
  p->p_sendto = NO_TASK;
  p->has_int_msg = 0;
  p->q_sending = 0;
  p->next_sending = 0;

  for (j = 0; j < NR_FILES; j++)
   p->filp[j] = 0;

  stk -= t->stacksize;
 }

 k_reenter = 0;
 ticks = 0;

 p_proc_ready = proc_table;

 init_clock();
        init_keyboard();

 restart();

 while(1){}
}


/*****************************************************************************
 *                                get_ticks
 *****************************************************************************/
PUBLIC int get_ticks()
{
 MESSAGE msg;
 reset_msg(&msg);
 msg.type = GET_TICKS;
 send_recv(BOTH, TASK_SYS, &msg);
 return msg.RETVAL;
}


/**
 * @struct posix_tar_header
 * Borrowed from GNU `tar'
 */
struct posix_tar_header
{    /* byte offset */
 char name[100];  /*   0 */
 char mode[8];  /* 100 */
 char uid[8];  /* 108 */
 char gid[8];  /* 116 */
 char size[12];  /* 124 */
 char mtime[12];  /* 136 */
 char chksum[8];  /* 148 */
 char typeflag;  /* 156 */
 char linkname[100]; /* 157 */
 char magic[6];  /* 257 */
 char version[2]; /* 263 */
 char uname[32];  /* 265 */
 char gname[32];  /* 297 */
 char devmajor[8]; /* 329 */
 char devminor[8]; /* 337 */
 char prefix[155]; /* 345 */
 /* 500 */
};

/*****************************************************************************
 *                                untar
 *****************************************************************************/
/**
 * Extract the tar file and store them.
 *
 * @param filename The tar file.
 *****************************************************************************/
void untar(const char * filename)
{
 printf("[extract `%s'\n", filename);
 int fd = open(filename, O_RDWR);
 assert(fd != -1);

 char buf[SECTOR_SIZE * 16];
 int chunk = sizeof(buf);

 while (1) {
  read(fd, buf, SECTOR_SIZE);
  if (buf[0] == 0)
   break;

  struct posix_tar_header * phdr = (struct posix_tar_header *)buf;

  /* calculate the file size */
  char * p = phdr->size;
  int f_len = 0;
  while (*p)
   f_len = (f_len * 8) + (*p++ - '0'); /* octal */

  int bytes_left = f_len;
  int fdout = open(phdr->name, O_CREAT | O_RDWR);
  if (fdout == -1) {
   printf("    failed to extract file: %s\n", phdr->name);
   printf(" aborted]");
   return;
  }
  printf("    %s (%d bytes)\n", phdr->name, f_len);
  while (bytes_left) {
   int iobytes = min(chunk, bytes_left);
   read(fd, buf,
        ((iobytes - 1) / SECTOR_SIZE + 1) * SECTOR_SIZE);
   write(fdout, buf, iobytes);
   bytes_left -= iobytes;
  }
  close(fdout);
 }

 close(fd);

 printf(" done]\n");
}

/*****************************************************************************
 *                                Init
 *****************************************************************************/
/**
 * The hen.
 *
 *****************************************************************************/
void Init()
{
 int fd_stdin  = open("/dev_tty0", O_RDWR);
 assert(fd_stdin  == 0);
 int fd_stdout = open("/dev_tty0", O_RDWR);
 assert(fd_stdout == 1);

 printf("Init() is running ...\n");

 /* extract `cmd.tar' */
 untar("/cmd.tar");

 int pid = fork();
 if (pid != 0) { /* parent process */
  int s;
  int child = wait(&s);
  printf("child (%d) exited with status: %d.\n", child, s);
 }
 else { /* child process */
  execl("/echo", "echo", "hello", "world", 0);
 }

 while (1) {
  int s;
  int child = wait(&s);
  printf("child (%d) exited with status: %d.\n", child, s);
 }

 assert(0);

}


/*======================================================================*
                               TestA
 *======================================================================*/
void TestA()
{
 for(;;);
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestB()
{
 for(;;);
}

/*======================================================================*
                               TestB
 *======================================================================*/
void TestC()
{
 for(;;);
}

/*****************************************************************************
 *                                panic
 *****************************************************************************/
PUBLIC void panic(const char *fmt, ...)
{
 int i;
 char buf[256];

 /* 4 is the size of fmt in the stack */
 va_list arg = (va_list)((char*)&fmt + 4);

 i = vsprintf(buf, fmt, arg);

 printl("%c !!panic!! %s", MAG_CH_PANIC, buf);

 /* should never arrive here */
 __asm__ __volatile__("ud2");
}

kernel/proc.c

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                               proc.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "tty.h"
#include "console.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "global.h"
#include "proto.h"

PRIVATE void block(struct proc* p);
PRIVATE void unblock(struct proc* p);
PRIVATE int  msg_send(struct proc* current, int dest, MESSAGE* m);
PRIVATE int  msg_receive(struct proc* current, int src, MESSAGE* m);
PRIVATE int  deadlock(int src, int dest);

/*****************************************************************************
 *                                schedule
 *****************************************************************************/
/**
 * <Ring 0> Choose one proc to run.
 *
 *****************************************************************************/
PUBLIC void schedule()
{
 struct proc* p;
 int  greatest_ticks = 0;

 while (!greatest_ticks) {
  for (p = &FIRST_PROC; p <= &LAST_PROC; p++) {
   if (p->p_flags == 0) {
    if (p->ticks > greatest_ticks) {
     greatest_ticks = p->ticks;
     p_proc_ready = p;
    }
   }
  }

  if (!greatest_ticks)
   for (p = &FIRST_PROC; p <= &LAST_PROC; p++)
    if (p->p_flags == 0)
     p->ticks = p->priority;
 }
}

/*****************************************************************************
 *                                sys_sendrec
 *****************************************************************************/
/**
 * <Ring 0> The core routine of system call `sendrec()'.
 *
 * @param function SEND or RECEIVE
 * @param src_dest To/From whom the message is transferred.
 * @param m        Ptr to the MESSAGE body.
 * @param p        The caller proc.
 *
 * @return Zero if success.
 *****************************************************************************/
PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p)
{
 assert(k_reenter == 0); /* make sure we are not in ring0 */
 assert((src_dest >= 0 && src_dest < NR_TASKS + NR_PROCS) ||
        src_dest == ANY ||
        src_dest == INTERRUPT);

 int ret = 0;
 int caller = proc2pid(p);
 MESSAGE* mla = (MESSAGE*)va2la(caller, m);
 mla->source = caller;

 assert(mla->source != src_dest);

 /**
  * Actually we have the third message type: BOTH. However, it is not
  * allowed to be passed to the kernel directly. Kernel doesn't know
  * it at all. It is transformed into a SEND followed by a RECEIVE
  * by `send_recv()'.
  */
 if (function == SEND) {
  ret = msg_send(p, src_dest, m);
  if (ret != 0)
   return ret;
 }
 else if (function == RECEIVE) {
  ret = msg_receive(p, src_dest, m);
  if (ret != 0)
   return ret;
 }
 else {
  panic("{sys_sendrec} invalid function: "
        "%d (SEND:%d, RECEIVE:%d).", function, SEND, RECEIVE);
 }

 return 0;
}

/*****************************************************************************
 *      ldt_seg_linear
 *****************************************************************************/
/**
 * <Ring 0~1> Calculate the linear address of a certain segment of a given
 * proc.
 *
 * @param p   Whose (the proc ptr).
 * @param idx Which (one proc has more than one segments).
 *
 * @return  The required linear address.
 *****************************************************************************/
PUBLIC int ldt_seg_linear(struct proc* p, int idx)
{
 struct descriptor * d = &p->ldts[idx];

 return d->base_high << 24 | d->base_mid << 16 | d->base_low;
}

/*****************************************************************************
 *      va2la
 *****************************************************************************/
/**
 * <Ring 0~1> Virtual addr --> Linear addr.
 *
 * @param pid  PID of the proc whose address is to be calculated.
 * @param va   Virtual address.
 *
 * @return The linear address for the given virtual address.
 *****************************************************************************/
PUBLIC void* va2la(int pid, void* va)
{
 struct proc* p = &proc_table[pid];

 u32 seg_base = ldt_seg_linear(p, INDEX_LDT_RW);
 u32 la = seg_base + (u32)va;

 if (pid < NR_TASKS + NR_NATIVE_PROCS) {
  assert(la == (u32)va);
 }

 return (void*)la;
}

/*****************************************************************************
 *                                reset_msg
 *****************************************************************************/
/**
 * <Ring 0~3> Clear up a MESSAGE by setting each byte to 0.
 *
 * @param p  The message to be cleared.
 *****************************************************************************/
PUBLIC void reset_msg(MESSAGE* p)
{
 memset(p, 0, sizeof(MESSAGE));
}

/*****************************************************************************
 *                                block
 *****************************************************************************/
/**
 * <Ring 0> This routine is called after `p_flags' has been set (!= 0), it
 * calls `schedule()' to choose another proc as the `proc_ready'.
 *
 * @attention This routine does not change `p_flags'. Make sure the `p_flags'
 * of the proc to be blocked has been set properly.
 *
 * @param p The proc to be blocked.
 *****************************************************************************/
PRIVATE void block(struct proc* p)
{
 assert(p->p_flags);
 schedule();
}

/*****************************************************************************
 *                                unblock
 *****************************************************************************/
/**
 * <Ring 0> This is a dummy routine. It does nothing actually. When it is
 * called, the `p_flags' should have been cleared (== 0).
 *
 * @param p The unblocked proc.
 *****************************************************************************/
PRIVATE void unblock(struct proc* p)
{
 assert(p->p_flags == 0);
}

/*****************************************************************************
 *                                deadlock
 *****************************************************************************/
/**
 * <Ring 0> Check whether it is safe to send a message from src to dest.
 * The routine will detect if the messaging graph contains a cycle. For
 * instance, if we have procs trying to send messages like this:
 * A -> B -> C -> A, then a deadlock occurs, because all of them will
 * wait forever. If no cycles detected, it is considered as safe.
 *
 * @param src   Who wants to send message.
 * @param dest  To whom the message is sent.
 *
 * @return Zero if success.
 *****************************************************************************/
PRIVATE int deadlock(int src, int dest)
{
 struct proc* p = proc_table + dest;
 while (1) {
  if (p->p_flags & SENDING) {
   if (p->p_sendto == src) {
    /* print the chain */
    p = proc_table + dest;
    printl("=_=%s", p->name);
    do {
     assert(p->p_msg);
     p = proc_table + p->p_sendto;
     printl("->%s", p->name);
    } while (p != proc_table + src);
    printl("=_=");

    return 1;
   }
   p = proc_table + p->p_sendto;
  }
  else {
   break;
  }
 }
 return 0;
}

/*****************************************************************************
 *                                msg_send
 *****************************************************************************/
/**
 * <Ring 0> Send a message to the dest proc. If dest is blocked waiting for
 * the message, copy the message to it and unblock dest. Otherwise the caller
 * will be blocked and appended to the dest's sending queue.
 *
 * @param current  The caller, the sender.
 * @param dest     To whom the message is sent.
 * @param m        The message.
 *
 * @return Zero if success.
 *****************************************************************************/
PRIVATE int msg_send(struct proc* current, int dest, MESSAGE* m)
{
 struct proc* sender = current;
 struct proc* p_dest = proc_table + dest; /* proc dest */

 assert(proc2pid(sender) != dest);

 /* check for deadlock here */
 if (deadlock(proc2pid(sender), dest)) {
  panic(">>DEADLOCK<< %s->%s", sender->name, p_dest->name);
 }

 if ((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg */
     (p_dest->p_recvfrom == proc2pid(sender) ||
      p_dest->p_recvfrom == ANY)) {
  assert(p_dest->p_msg);
  assert(m);

  phys_copy(va2la(dest, p_dest->p_msg),
     va2la(proc2pid(sender), m),
     sizeof(MESSAGE));
  p_dest->p_msg = 0;
  p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */
  p_dest->p_recvfrom = NO_TASK;
  unblock(p_dest);

  assert(p_dest->p_flags == 0);
  assert(p_dest->p_msg == 0);
  assert(p_dest->p_recvfrom == NO_TASK);
  assert(p_dest->p_sendto == NO_TASK);
  assert(sender->p_flags == 0);
  assert(sender->p_msg == 0);
  assert(sender->p_recvfrom == NO_TASK);
  assert(sender->p_sendto == NO_TASK);
 }
 else { /* dest is not waiting for the msg */
  sender->p_flags |= SENDING;
  assert(sender->p_flags == SENDING);
  sender->p_sendto = dest;
  sender->p_msg = m;

  /* append to the sending queue */
  struct proc * p;
  if (p_dest->q_sending) {
   p = p_dest->q_sending;
   while (p->next_sending)
    p = p->next_sending;
   p->next_sending = sender;
  }
  else {
   p_dest->q_sending = sender;
  }
  sender->next_sending = 0;

  block(sender);

  assert(sender->p_flags == SENDING);
  assert(sender->p_msg != 0);
  assert(sender->p_recvfrom == NO_TASK);
  assert(sender->p_sendto == dest);
 }

 return 0;
}


/*****************************************************************************
 *                                msg_receive
 *****************************************************************************/
/**
 * <Ring 0> Try to get a message from the src proc. If src is blocked sending
 * the message, copy the message from it and unblock src. Otherwise the caller
 * will be blocked.
 *
 * @param current The caller, the proc who wanna receive.
 * @param src     From whom the message will be received.
 * @param m       The message ptr to accept the message.
 *
 * @return  Zero if success.
 *****************************************************************************/
PRIVATE int msg_receive(struct proc* current, int src, MESSAGE* m)
{
 struct proc* p_who_wanna_recv = current; /**
        * This name is a little bit
        * wierd, but it makes me
        * think clearly, so I keep
        * it.
        */
 struct proc* p_from = 0; /* from which the message will be fetched */
 struct proc* prev = 0;
 int copyok = 0;

 assert(proc2pid(p_who_wanna_recv) != src);

 if ((p_who_wanna_recv->has_int_msg) &&
     ((src == ANY) || (src == INTERRUPT))) {
  /* There is an interrupt needs p_who_wanna_recv's handling and
   * p_who_wanna_recv is ready to handle it.
   */

  MESSAGE msg;
  reset_msg(&msg);
  msg.source = INTERRUPT;
  msg.type = HARD_INT;
  assert(m);
  phys_copy(va2la(proc2pid(p_who_wanna_recv), m), &msg,
     sizeof(MESSAGE));

  p_who_wanna_recv->has_int_msg = 0;

  assert(p_who_wanna_recv->p_flags == 0);
  assert(p_who_wanna_recv->p_msg == 0);
  assert(p_who_wanna_recv->p_sendto == NO_TASK);
  assert(p_who_wanna_recv->has_int_msg == 0);

  return 0;
 }


 /* Arrives here if no interrupt for p_who_wanna_recv. */
 if (src == ANY) {
  /* p_who_wanna_recv is ready to receive messages from
   * ANY proc, we'll check the sending queue and pick the
   * first proc in it.
   */
  if (p_who_wanna_recv->q_sending) {
   p_from = p_who_wanna_recv->q_sending;
   copyok = 1;

   assert(p_who_wanna_recv->p_flags == 0);
   assert(p_who_wanna_recv->p_msg == 0);
   assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
   assert(p_who_wanna_recv->p_sendto == NO_TASK);
   assert(p_who_wanna_recv->q_sending != 0);
   assert(p_from->p_flags == SENDING);
   assert(p_from->p_msg != 0);
   assert(p_from->p_recvfrom == NO_TASK);
   assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
  }
 }
 else {
  /* p_who_wanna_recv wants to receive a message from
   * a certain proc: src.
   */
  p_from = &proc_table[src];

  if ((p_from->p_flags & SENDING) &&
      (p_from->p_sendto == proc2pid(p_who_wanna_recv))) {
   /* Perfect, src is sending a message to
    * p_who_wanna_recv.
    */
   copyok = 1;

   struct proc* p = p_who_wanna_recv->q_sending;
   assert(p); /* p_from must have been appended to the
        * queue, so the queue must not be NULL
        */
   while (p) {
    assert(p_from->p_flags & SENDING);
    if (proc2pid(p) == src) { /* if p is the one */
     p_from = p;
     break;
    }
    prev = p;
    p = p->next_sending;
   }

   assert(p_who_wanna_recv->p_flags == 0);
   assert(p_who_wanna_recv->p_msg == 0);
   assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
   assert(p_who_wanna_recv->p_sendto == NO_TASK);
   assert(p_who_wanna_recv->q_sending != 0);
   assert(p_from->p_flags == SENDING);
   assert(p_from->p_msg != 0);
   assert(p_from->p_recvfrom == NO_TASK);
   assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
  }
 }

 if (copyok) {
  /* It's determined from which proc the message will
   * be copied. Note that this proc must have been
   * waiting for this moment in the queue, so we should
   * remove it from the queue.
   */
  if (p_from == p_who_wanna_recv->q_sending) { /* the 1st one */
   assert(prev == 0);
   p_who_wanna_recv->q_sending = p_from->next_sending;
   p_from->next_sending = 0;
  }
  else {
   assert(prev);
   prev->next_sending = p_from->next_sending;
   p_from->next_sending = 0;
  }

  assert(m);
  assert(p_from->p_msg);
  /* copy the message */
  phys_copy(va2la(proc2pid(p_who_wanna_recv), m),
     va2la(proc2pid(p_from), p_from->p_msg),
     sizeof(MESSAGE));

  p_from->p_msg = 0;
  p_from->p_sendto = NO_TASK;
  p_from->p_flags &= ~SENDING;
  unblock(p_from);
 }
 else {  /* nobody's sending any msg */
  /* Set p_flags so that p_who_wanna_recv will not
   * be scheduled until it is unblocked.
   */
  p_who_wanna_recv->p_flags |= RECEIVING;

  p_who_wanna_recv->p_msg = m;

  if (src == ANY)
   p_who_wanna_recv->p_recvfrom = ANY;
  else
   p_who_wanna_recv->p_recvfrom = proc2pid(p_from);

  block(p_who_wanna_recv);

  assert(p_who_wanna_recv->p_flags == RECEIVING);
  assert(p_who_wanna_recv->p_msg != 0);
  assert(p_who_wanna_recv->p_recvfrom != NO_TASK);
  assert(p_who_wanna_recv->p_sendto == NO_TASK);
  assert(p_who_wanna_recv->has_int_msg == 0);
 }

 return 0;
}

/*****************************************************************************
 *                                inform_int
 *****************************************************************************/
/**
 * <Ring 0> Inform a proc that an interrupt has occured.
 *
 * @param task_nr  The task which will be informed.
 *****************************************************************************/
PUBLIC void inform_int(int task_nr)
{
 struct proc* p = proc_table + task_nr;

 if ((p->p_flags & RECEIVING) && /* dest is waiting for the msg */
     ((p->p_recvfrom == INTERRUPT) || (p->p_recvfrom == ANY))) {
  p->p_msg->source = INTERRUPT;
  p->p_msg->type = HARD_INT;
  p->p_msg = 0;
  p->has_int_msg = 0;
  p->p_flags &= ~RECEIVING; /* dest has received the msg */
  p->p_recvfrom = NO_TASK;
  assert(p->p_flags == 0);
  unblock(p);

  assert(p->p_flags == 0);
  assert(p->p_msg == 0);
  assert(p->p_recvfrom == NO_TASK);
  assert(p->p_sendto == NO_TASK);
 }
 else {
  p->has_int_msg = 1;
 }
}

/*****************************************************************************
 *                                dump_proc
 *****************************************************************************/
PUBLIC void dump_proc(struct proc* p)
{
 char info[STR_DEFAULT_LEN];
 int i;
 int text_color = MAKE_COLOR(GREEN, RED);

 int dump_len = sizeof(struct proc);

 out_byte(CRTC_ADDR_REG, START_ADDR_H);
 out_byte(CRTC_DATA_REG, 0);
 out_byte(CRTC_ADDR_REG, START_ADDR_L);
 out_byte(CRTC_DATA_REG, 0);

 sprintf(info, "byte dump of proc_table[%d]:\n", p - proc_table); disp_color_str(info, text_color);
 for (i = 0; i < dump_len; i++) {
  sprintf(info, "%x.", ((unsigned char *)p)[i]);
  disp_color_str(info, text_color);
 }

 /* printl("^^"); */

 disp_color_str("\n\n", text_color);
 sprintf(info, "ANY: 0x%x.\n", ANY); disp_color_str(info, text_color);
 sprintf(info, "NO_TASK: 0x%x.\n", NO_TASK); disp_color_str(info, text_color);
 disp_color_str("\n", text_color);

 sprintf(info, "ldt_sel: 0x%x.  ", p->ldt_sel); disp_color_str(info, text_color);
 sprintf(info, "ticks: 0x%x.  ", p->ticks); disp_color_str(info, text_color);
 sprintf(info, "priority: 0x%x.  ", p->priority); disp_color_str(info, text_color);
 /* sprintf(info, "pid: 0x%x.  ", p->pid); disp_color_str(info, text_color); */
 sprintf(info, "name: %s.  ", p->name); disp_color_str(info, text_color);
 disp_color_str("\n", text_color);
 sprintf(info, "p_flags: 0x%x.  ", p->p_flags); disp_color_str(info, text_color);
 sprintf(info, "p_recvfrom: 0x%x.  ", p->p_recvfrom); disp_color_str(info, text_color);
 sprintf(info, "p_sendto: 0x%x.  ", p->p_sendto); disp_color_str(info, text_color);
 /* sprintf(info, "nr_tty: 0x%x.  ", p->nr_tty); disp_color_str(info, text_color); */
 disp_color_str("\n", text_color);
 sprintf(info, "has_int_msg: 0x%x.  ", p->has_int_msg); disp_color_str(info, text_color);
}


/*****************************************************************************
 *                                dump_msg
 *****************************************************************************/
PUBLIC void dump_msg(const char * title, MESSAGE* m)
{
 int packed = 0;
 printl("{%s}<0x%x>{%ssrc:%s(%d),%stype:%d,%s(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)%s}%s",  //, (0x%x, 0x%x, 0x%x)}",
        title,
        (int)m,
        packed ? "" : "\n        ",
        proc_table[m->source].name,
        m->source,
        packed ? " " : "\n        ",
        m->type,
        packed ? " " : "\n        ",
        m->u.m3.m3i1,
        m->u.m3.m3i2,
        m->u.m3.m3i3,
        m->u.m3.m3i4,
        (int)m->u.m3.m3p1,
        (int)m->u.m3.m3p2,
        packed ? "" : "\n",
        packed ? "" : "\n"/* , */
  );
}

include/stdio.h

/*************************************************************************//**
 *****************************************************************************
 * @file   stdio.h
 * @brief
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#ifndef _ORANGES_STDIO_H_
#define _ORANGES_STDIO_H_

#include "type.h"

/* the assert macro */
#define ASSERT
#ifdef ASSERT
void assertion_failure(char *exp, char *file, char *base_file, int line);
#define assert(exp)  if (exp) ; \
        else assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__)
#else
#define assert(exp)
#endif

/* EXTERN */
#define EXTERN extern /* EXTERN is defined as extern except in global.c */

/* string */
#define STR_DEFAULT_LEN 1024

#define O_CREAT  1
#define O_RDWR  2

#define SEEK_SET 1
#define SEEK_CUR 2
#define SEEK_END 3

#define MAX_PATH 128

/**
 * @struct stat
 * @brief  File status, returned by syscall stat();
 */
struct stat {
 int st_dev;  /* major/minor device number */
 int st_ino;  /* i-node number */
 int st_mode;  /* file mode, protection bits, etc. */
 int st_rdev;  /* device ID (if special file) */
 int st_size;  /* file size */
};

/**
 * @struct time
 * @brief  RTC time from CMOS.
 */
struct time {
 u32 year;
 u32 month;
 u32 day;
 u32 hour;
 u32 minute;
 u32 second;
};

#define  BCD_TO_DEC(x)      ( (x >> 4) * 10 + (x & 0x0f) )

/*========================*
 * printf, printl, printx *
 *========================*
 *
 *   printf:
 *
 *           [send msg]                WRITE           DEV_WRITE
 *                      USER_PROC ------------→ FS -------------→ TTY
 *                              ↖______________↙↖_______________/
 *           [recv msg]             SYSCALL_RET       SYSCALL_RET
 *
 *----------------------------------------------------------------------
 *
 *   printl: variant-parameter-version printx
 *
 *          calls vsprintf, then printx (trap into kernel directly)
 *
 *----------------------------------------------------------------------
 *
 *   printx: low level print without using IPC
 *
 *                       trap directly
 *           USER_PROC -- -- -- -- -- --> KERNEL
 *
 *
 *----------------------------------------------------------------------
 */

/* printf.c */
PUBLIC  int     printf(const char *fmt, ...);
PUBLIC  int     printl(const char *fmt, ...);

/* vsprintf.c */
PUBLIC  int     vsprintf(char *buf, const char *fmt, va_list args);
PUBLIC int sprintf(char *buf, const char *fmt, ...);

/*--------*/
/* 庫函數 */
/*--------*/

#ifdef ENABLE_DISK_LOG
#define SYSLOG syslog
#endif

/* lib/open.c */
PUBLIC int open  (const char *pathname, int flags);

/* lib/close.c */
PUBLIC int close  (int fd);

/* lib/read.c */
PUBLIC int read  (int fd, void *buf, int count);

/* lib/write.c */
PUBLIC int write  (int fd, const void *buf, int count);

/* lib/unlink.c */
PUBLIC int unlink  (const char *pathname);

/* lib/getpid.c */
PUBLIC int getpid  ();

/* lib/fork.c */
PUBLIC int fork  ();

/* lib/exit.c */
PUBLIC void exit  (int status);

/* lib/wait.c */
PUBLIC int wait  (int * status);

/* lib/exec.c */
PUBLIC int exec  (const char * path);
PUBLIC int execl  (const char * path, const char *arg, ...);
PUBLIC int execv  (const char * path, char * argv[]);

/* lib/stat.c */
PUBLIC int stat  (const char *path, struct stat *buf);

/* lib/syslog.c */
PUBLIC int syslog  (const char *fmt, ...);


#endif /* _ORANGES_STDIO_H_ */

include/sys/config.h

/*************************************************************************//**
 *****************************************************************************
 * @file   config.h
 * @brief  
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

/**
 * Some sector are reserved for us (the gods of the os) to copy a tar file
 * there, which will be extracted and used by the OS.
 *
 * @attention INSTALL_NR_SECTS should be a multiple of NR_DEFAULT_FILE_SECTS:
 *                INSTALL_NR_SECTS = n * NR_DEFAULT_FILE_SECTS (n=1,2,3,...)
 */
#define INSTALL_START_SECT  0x8000
#define INSTALL_NR_SECTS  0x800

/**
 * boot parameters are stored by the loader, they should be
 * there when kernel is running and should not be overwritten
 * since kernel might use them at any time.
 */
#define BOOT_PARAM_ADDR   0x900  /* physical address */
#define BOOT_PARAM_MAGIC  0xB007 /* magic number */
#define BI_MAG    0
#define BI_MEM_SIZE   1
#define BI_KERNEL_FILE   2

/**
 * corresponding with boot/include/load.inc::ROOT_BASE, which should
 * be changed if this macro is changed.
 */
#define MINOR_BOOT   MINOR_hd2a

/*
 * disk log
 */
#define ENABLE_DISK_LOG
#define SET_LOG_SECT_SMAP_AT_STARTUP
#define MEMSET_LOG_SECTS
#define NR_SECTS_FOR_LOG  NR_DEFAULT_FILE_SECTS

include/sys/const.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            const.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#ifndef _ORANGES_CONST_H_
#define _ORANGES_CONST_H_

/* max() & min() */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))

/* Color */
/*
 * e.g. MAKE_COLOR(BLUE, RED)
 *      MAKE_COLOR(BLACK, RED) | BRIGHT
 *      MAKE_COLOR(BLACK, RED) | BRIGHT | FLASH
 */
#define BLACK   0x0     /* 0000 */
#define WHITE   0x7     /* 0111 */
#define RED     0x4     /* 0100 */
#define GREEN   0x2     /* 0010 */
#define BLUE    0x1     /* 0001 */
#define FLASH   0x80    /* 1000 0000 */
#define BRIGHT  0x08    /* 0000 1000 */
#define MAKE_COLOR(x,y) ((x<<4) | y) /* MAKE_COLOR(Background,Foreground) */

/* GDT 和 IDT 中描述符的個數 */
#define GDT_SIZE 128
#define IDT_SIZE 256

/* 權限 */
#define PRIVILEGE_KRNL 0
#define PRIVILEGE_TASK 1
#define PRIVILEGE_USER 3
/* RPL */
#define RPL_KRNL SA_RPL0
#define RPL_TASK SA_RPL1
#define RPL_USER SA_RPL3

/* Process */
#define SENDING   0x02 /* set when proc trying to send */
#define RECEIVING 0x04 /* set when proc trying to recv */
#define WAITING   0x08 /* set when proc waiting for the child to terminate */
#define HANGING   0x10 /* set when proc exits without being waited by parent */
#define FREE_SLOT 0x20 /* set when proc table entry is not used
    * (ok to allocated to a new process)
    */

/* TTY */
#define NR_CONSOLES 3 /* consoles */

/* 8259A interrupt controller ports. */
#define INT_M_CTL 0x20 /* I/O port for interrupt controller         <Master> */
#define INT_M_CTLMASK 0x21 /* setting bits in this port disables ints   <Master> */
#define INT_S_CTL 0xA0 /* I/O port for second interrupt controller  <Slave>  */
#define INT_S_CTLMASK 0xA1 /* setting bits in this port disables ints   <Slave>  */

/* 8253/8254 PIT (Programmable Interval Timer) */
#define TIMER0         0x40 /* I/O port for timer channel 0 */
#define TIMER_MODE     0x43 /* I/O port for timer mode control */
#define RATE_GENERATOR 0x34 /* 00-11-010-0 :
        * Counter0 - LSB then MSB - rate generator - binary
        */
#define TIMER_FREQ     1193182L/* clock frequency for timer in PC and AT */
#define HZ             100  /* clock freq (software settable on IBM-PC) */

/* AT keyboard */
/* 8042 ports */
#define KB_DATA  0x60 /* I/O port for keyboard data
     Read : Read Output Buffer
     Write: Write Input Buffer(8042 Data&8048 Command) */
#define KB_CMD  0x64 /* I/O port for keyboard command
     Read : Read Status Register
     Write: Write Input Buffer(8042 Command) */
#define LED_CODE 0xED
#define KB_ACK  0xFA

/* VGA */
#define CRTC_ADDR_REG 0x3D4 /* CRT Controller Registers - Addr Register */
#define CRTC_DATA_REG 0x3D5 /* CRT Controller Registers - Data Register */
#define START_ADDR_H 0xC /* reg index of video mem start addr (MSB) */
#define START_ADDR_L 0xD /* reg index of video mem start addr (LSB) */
#define CURSOR_H 0xE /* reg index of cursor position (MSB) */
#define CURSOR_L 0xF /* reg index of cursor position (LSB) */
#define V_MEM_BASE 0xB8000 /* base of color video memory */
#define V_MEM_SIZE 0x8000 /* 32K: B8000H -> BFFFFH */

/* CMOS */
#define CLK_ELE  0x70 /* CMOS RAM address register port (write only)
     * Bit 7 = 1  NMI disable
     *    0  NMI enable
     * Bits 6-0 = RAM address
     */

#define CLK_IO  0x71 /* CMOS RAM data register port (read/write) */

#define  YEAR             9 /* Clock register addresses in CMOS RAM */
#define  MONTH            8
#define  DAY              7
#define  HOUR             4
#define  MINUTE           2
#define  SECOND           0
#define  CLK_STATUS    0x0B /* Status register B: RTC configuration */
#define  CLK_HEALTH    0x0E /* Diagnostic status: (should be set by Power
     * On Self-Test [POST])
     * Bit  7 = RTC lost power
     * 6 = Checksum (for addr 0x10-0x2d) bad
     * 5 = Config. Info. bad at POST
     * 4 = Mem. size error at POST
     * 3 = I/O board failed initialization
     * 2 = CMOS time invalid
     *    1-0 =    reserved
     */

/* Hardware interrupts */
#define NR_IRQ  16 /* Number of IRQs */
#define CLOCK_IRQ 0
#define KEYBOARD_IRQ 1
#define CASCADE_IRQ 2 /* cascade enable for 2nd AT controller */
#define ETHER_IRQ 3 /* default ethernet interrupt vector */
#define SECONDARY_IRQ 3 /* RS232 interrupt vector for port 2 */
#define RS232_IRQ 4 /* RS232 interrupt vector for port 1 */
#define XT_WINI_IRQ 5 /* xt winchester */
#define FLOPPY_IRQ 6 /* floppy disk */
#define PRINTER_IRQ 7
#define AT_WINI_IRQ 14 /* at winchester */

/* tasks */
/* 注意 TASK_XXX 的定義要與 global.c 中對應 */
#define INVALID_DRIVER -20
#define INTERRUPT -10
#define TASK_TTY 0
#define TASK_SYS 1
#define TASK_HD  2
#define TASK_FS  3
#define TASK_MM  4
#define INIT  5
#define ANY  (NR_TASKS + NR_PROCS + 10)
#define NO_TASK  (NR_TASKS + NR_PROCS + 20)

#define MAX_TICKS 0x7FFFABCD

/* system call */
#define NR_SYS_CALL 3

/* ipc */
#define SEND  1
#define RECEIVE  2
#define BOTH  3 /* BOTH = (SEND | RECEIVE) */

/* magic chars used by `printx' */
#define MAG_CH_PANIC '\002'
#define MAG_CH_ASSERT '\003'

/**
 * @enum msgtype
 * @brief MESSAGE types
 */
enum msgtype {
 /*
  * when hard interrupt occurs, a msg (with type==HARD_INT) will
  * be sent to some tasks
  */
 HARD_INT = 1,

 /* SYS task */
 GET_TICKS, GET_PID, GET_RTC_TIME,

 /* FS */
 OPEN, CLOSE, READ, WRITE, LSEEK, STAT, UNLINK,

 /* FS & TTY */
 SUSPEND_PROC, RESUME_PROC,

 /* MM */
 EXEC, WAIT,

 /* FS & MM */
 FORK, EXIT,

 /* TTY, SYS, FS, MM, etc */
 SYSCALL_RET,

 /* message type for drivers */
 DEV_OPEN = 1001,
 DEV_CLOSE,
 DEV_READ,
 DEV_WRITE,
 DEV_IOCTL
};

/* macros for messages */
#define FD  u.m3.m3i1
#define PATHNAME u.m3.m3p1
#define FLAGS  u.m3.m3i1
#define NAME_LEN u.m3.m3i2
#define BUF_LEN  u.m3.m3i3
#define CNT  u.m3.m3i2
#define REQUEST  u.m3.m3i2
#define PROC_NR  u.m3.m3i3
#define DEVICE  u.m3.m3i4
#define POSITION u.m3.m3l1
#define BUF  u.m3.m3p2
#define OFFSET  u.m3.m3i2
#define WHENCE  u.m3.m3i3

#define PID  u.m3.m3i2
#define RETVAL  u.m3.m3i1
#define STATUS  u.m3.m3i1









#define DIOCTL_GET_GEO 1

/* Hard Drive */
#define SECTOR_SIZE  512
#define SECTOR_BITS  (SECTOR_SIZE * 8)
#define SECTOR_SIZE_SHIFT 9

/* major device numbers (corresponding to kernel/global.c::dd_map[]) */
#define NO_DEV   0
#define DEV_FLOPPY  1
#define DEV_CDROM  2
#define DEV_HD   3
#define DEV_CHAR_TTY  4
#define DEV_SCSI  5
/* make device number from major and minor numbers */
#define MAJOR_SHIFT  8
#define MAKE_DEV(a,b)  ((a << MAJOR_SHIFT) | b)
/* separate major and minor numbers from device number */
#define MAJOR(x)  ((x >> MAJOR_SHIFT) & 0xFF)
#define MINOR(x)  (x & 0xFF)

#define INVALID_INODE  0
#define ROOT_INODE  1

#define MAX_DRIVES  2
#define NR_PART_PER_DRIVE 4
#define NR_SUB_PER_PART  16
#define NR_SUB_PER_DRIVE (NR_SUB_PER_PART * NR_PART_PER_DRIVE)
#define NR_PRIM_PER_DRIVE (NR_PART_PER_DRIVE + 1)

/**
 * @def MAX_PRIM
 * Defines the max minor number of the primary partitions.
 * If there are 2 disks, prim_dev ranges in hd[0-9], this macro will
 * equals 9.
 */
#define MAX_PRIM  (MAX_DRIVES * NR_PRIM_PER_DRIVE - 1)

#define MAX_SUBPARTITIONS (NR_SUB_PER_DRIVE * MAX_DRIVES)

/* device numbers of hard disk */
#define MINOR_hd1a  0x10
#define MINOR_hd2a  (MINOR_hd1a+NR_SUB_PER_PART)

#define ROOT_DEV  MAKE_DEV(DEV_HD, MINOR_BOOT)

#define P_PRIMARY 0
#define P_EXTENDED 1

#define ORANGES_PART 0x99 /* Orange'S partition */
#define NO_PART  0x00 /* unused entry */
#define EXT_PART 0x05 /* extended partition */

#define NR_FILES 64
#define NR_FILE_DESC 64 /* FIXME */
#define NR_INODE 64 /* FIXME */
#define NR_SUPER_BLOCK 8


/* INODE::i_mode (octal, lower 12 bits reserved) */
#define I_TYPE_MASK     0170000
#define I_REGULAR       0100000
#define I_BLOCK_SPECIAL 0060000
#define I_DIRECTORY     0040000
#define I_CHAR_SPECIAL  0020000
#define I_NAMED_PIPE 0010000

#define is_special(m) ((((m) & I_TYPE_MASK) == I_BLOCK_SPECIAL) || \
    (((m) & I_TYPE_MASK) == I_CHAR_SPECIAL))

#define NR_DEFAULT_FILE_SECTS 2048 /* 2048 * 512 = 1MB */



#endif /* _ORANGES_CONST_H_ */

include/sys/proto.h

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                            proto.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                                                    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/* kliba.asm */
PUBLIC void out_byte(u16 port, u8 value);
PUBLIC u8 in_byte(u16 port);
PUBLIC void disp_str(char * info);
PUBLIC void disp_color_str(char * info, int color);
PUBLIC void disable_irq(int irq);
PUBLIC void enable_irq(int irq);
PUBLIC void disable_int();
PUBLIC void enable_int();
PUBLIC void port_read(u16 port, void* buf, int n);
PUBLIC void port_write(u16 port, void* buf, int n);
PUBLIC void glitter(int row, int col);

/* string.asm */
PUBLIC char* strcpy(char* dst, const char* src);

/* protect.c */
PUBLIC void init_prot();
PUBLIC u32 seg2linear(u16 seg);
PUBLIC void init_desc(struct descriptor * p_desc,
     u32 base, u32 limit, u16 attribute);

/* klib.c */
PUBLIC void get_boot_params(struct boot_params * pbp);
PUBLIC int get_kernel_map(unsigned int * b, unsigned int * l);
PUBLIC void delay(int time);
PUBLIC void disp_int(int input);
PUBLIC char * itoa(char * str, int num);

/* kernel.asm */
PUBLIC void restart();

/* main.c */
PUBLIC void Init();
PUBLIC int  get_ticks();
PUBLIC void TestA();
PUBLIC void TestB();
PUBLIC void TestC();
PUBLIC void panic(const char *fmt, ...);

/* i8259.c */
PUBLIC void init_8259A();
PUBLIC void put_irq_handler(int irq, irq_handler handler);
PUBLIC void spurious_irq(int irq);

/* clock.c */
PUBLIC void clock_handler(int irq);
PUBLIC void init_clock();
PUBLIC void milli_delay(int milli_sec);

/* kernel/hd.c */
PUBLIC void task_hd();
PUBLIC void hd_handler(int irq);

/* keyboard.c */
PUBLIC void init_keyboard();
PUBLIC void keyboard_read(TTY* p_tty);

/* tty.c */
PUBLIC void task_tty();
PUBLIC void in_process(TTY* p_tty, u32 key);
PUBLIC void dump_tty_buf(); /* for debug only */

/* systask.c */
PUBLIC void task_sys();

/* fs/main.c */
PUBLIC void   task_fs();
PUBLIC int   rw_sector(int io_type, int dev, u64 pos,
       int bytes, int proc_nr, void * buf);
PUBLIC struct inode *  get_inode(int dev, int num);
PUBLIC void   put_inode(struct inode * pinode);
PUBLIC void   sync_inode(struct inode * p);
PUBLIC struct super_block * get_super_block(int dev);

/* fs/open.c */
PUBLIC int  do_open();
PUBLIC int  do_close();

/* fs/read_write.c */
PUBLIC int  do_rdwt();

/* fs/link.c */
PUBLIC int  do_unlink();

/* fs/misc.c */
PUBLIC int  do_stat();
PUBLIC int  strip_path(char * filename, const char * pathname,
       struct inode** ppinode);
PUBLIC int  search_file(char * path);

/* fs/disklog.c */
PUBLIC int  do_disklog();
PUBLIC int  disklog(char * logstr); /* for debug */
PUBLIC void  dump_fd_graph(const char * fmt, ...);

/* mm/main.c */
PUBLIC void  task_mm();
PUBLIC int  alloc_mem(int pid, int memsize);
PUBLIC int  free_mem(int pid);

/* mm/forkexit.c */
PUBLIC int  do_fork();
PUBLIC void  do_exit(int status);
PUBLIC void  do_wait();

/* mm/exec.c */
PUBLIC int  do_exec();

/* console.c */
PUBLIC void out_char(CONSOLE* p_con, char ch);
PUBLIC void scroll_screen(CONSOLE* p_con, int direction);
PUBLIC void select_console(int nr_console);
PUBLIC void init_screen(TTY* p_tty);
PUBLIC int  is_current_console(CONSOLE* p_con);

/* proc.c */
PUBLIC void schedule();
PUBLIC void* va2la(int pid, void* va);
PUBLIC int ldt_seg_linear(struct proc* p, int idx);
PUBLIC void reset_msg(MESSAGE* p);
PUBLIC void dump_msg(const char * title, MESSAGE* m);
PUBLIC void dump_proc(struct proc * p);
PUBLIC int send_recv(int function, int src_dest, MESSAGE* msg);
PUBLIC void inform_int(int task_nr);

/* lib/misc.c */
PUBLIC void spin(char * func_name);

/* 以下是系統調用相關 */

/* 系統調用 - 系統級 */
/* proc.c */
PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p);
PUBLIC int sys_printx(int _unused1, int _unused2, char* s, struct proc * p_proc);

/* syscall.asm */
PUBLIC  void    sys_call();             /* int_handler */

/* 系統調用 - 用戶級 */
PUBLIC int sendrec(int function, int src_dest, MESSAGE* p_msg);
PUBLIC int printx(char* str);

lib/misc.c

/*************************************************************************//**
 *****************************************************************************
 * @file   misc.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

/*****************************************************************************
 *                                send_recv
 *****************************************************************************/
/**
 * <Ring 1~3> IPC syscall.
 *
 * It is an encapsulation of `sendrec',
 * invoking `sendrec' directly should be avoided
 *
 * @param function  SEND, RECEIVE or BOTH
 * @param src_dest  The caller's proc_nr
 * @param msg       Pointer to the MESSAGE struct
 *
 * @return always 0.
 *****************************************************************************/
PUBLIC int send_recv(int function, int src_dest, MESSAGE* msg)
{
 int ret = 0;

 if (function == RECEIVE)
  memset(msg, 0, sizeof(MESSAGE));

 switch (function) {
 case BOTH:
  ret = sendrec(SEND, src_dest, msg);
  if (ret == 0)
   ret = sendrec(RECEIVE, src_dest, msg);
  break;
 case SEND:
 case RECEIVE:
  ret = sendrec(function, src_dest, msg);
  break;
 default:
  assert((function == BOTH) ||
         (function == SEND) || (function == RECEIVE));
  break;
 }

 return ret;
}

/*****************************************************************************
 *                                memcmp
 *****************************************************************************/
/**
 * Compare memory areas.
 *
 * @param s1  The 1st area.
 * @param s2  The 2nd area.
 * @param n   The first n bytes will be compared.
 *
 * @return  an integer less than, equal to, or greater than zero if the first
 *          n bytes of s1 is found, respectively, to be less than, to match,
 *          or  be greater than the first n bytes of s2.
 *****************************************************************************/
PUBLIC int memcmp(const void * s1, const void *s2, int n)
{
 if ((s1 == 0) || (s2 == 0)) { /* for robustness */
  return (s1 - s2);
 }

 const char * p1 = (const char *)s1;
 const char * p2 = (const char *)s2;
 int i;
 for (i = 0; i < n; i++,p1++,p2++) {
  if (*p1 != *p2) {
   return (*p1 - *p2);
  }
 }
 return 0;
}

/*****************************************************************************
 *                                strcmp
 *****************************************************************************/
/**
 * Compare two strings.
 *
 * @param s1  The 1st string.
 * @param s2  The 2nd string.
 *
 * @return  an integer less than, equal to, or greater than zero if s1 (or the
 *          first n bytes thereof) is  found,  respectively,  to  be less than,
 *          to match, or be greater than s2.
 *****************************************************************************/
PUBLIC int strcmp(const char * s1, const char *s2)
{
 if ((s1 == 0) || (s2 == 0)) { /* for robustness */
  return (s1 - s2);
 }

 const char * p1 = s1;
 const char * p2 = s2;

 for (; *p1 && *p2; p1++,p2++) {
  if (*p1 != *p2) {
   break;
  }
 }

 return (*p1 - *p2);
}

/*****************************************************************************
 *                                strcat
 *****************************************************************************/
/**
 * Concatenate two strings.
 *
 * @param s1  The 1st string.
 * @param s2  The 2nd string.
 *
 * @return  Ptr to the 1st string.
 *****************************************************************************/
PUBLIC char * strcat(char * s1, const char *s2)
{
 if ((s1 == 0) || (s2 == 0)) { /* for robustness */
  return 0;
 }

 char * p1 = s1;
 for (; *p1; p1++) {}

 const char * p2 = s2;
 for (; *p2; p1++,p2++) {
  *p1 = *p2;
 }
 *p1 = 0;

 return s1;
}

/*****************************************************************************
 *                                spin
 *****************************************************************************/
PUBLIC void spin(char * func_name)
{
 printl("\nspinning in %s ...\n", func_name);
 while (1) {}
}


/*****************************************************************************
 *                           assertion_failure
 *************************************************************************//**
 * Invoked by assert().
 *
 * @param exp       The failure expression itself.
 * @param file      __FILE__
 * @param base_file __BASE_FILE__
 * @param line      __LINE__
 *****************************************************************************/
PUBLIC void assertion_failure(char *exp, char *file, char *base_file, int line)
{
 printl("%c  assert(%s) failed: file: %s, base_file: %s, ln%d",
        MAG_CH_ASSERT,
        exp, file, base_file, line);

 /**
  * If assertion fails in a TASK, the system will halt before
  * printl() returns. If it happens in a USER PROC, printl() will
  * return like a common routine and arrive here.
  * @see sys_printx()
  *
  * We use a forever loop to prevent the proc from going on:
  */
 spin("assertion_failure()");

 /* should never arrive here */
        __asm__ __volatile__("ud2");
}

mm/forkexit.c

/*************************************************************************//**
 *****************************************************************************
 * @file   forkexit.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Tue May  6 00:37:15 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"


PRIVATE void cleanup(struct proc * proc);

/*****************************************************************************
 *                                do_fork
 *****************************************************************************/
/**
 * Perform the fork() syscall.
 *
 * @return  Zero if success, otherwise -1.
 *****************************************************************************/
PUBLIC int do_fork()
{
 /* find a free slot in proc_table */
 struct proc* p = proc_table;
 int i;
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p++)
  if (p->p_flags == FREE_SLOT)
   break;

 int child_pid = i;
 assert(p == &proc_table[child_pid]);
 assert(child_pid >= NR_TASKS + NR_NATIVE_PROCS);
 if (i == NR_TASKS + NR_PROCS) /* no free slot */
  return -1;
 assert(i < NR_TASKS + NR_PROCS);

 /* duplicate the process table */
 int pid = mm_msg.source;
 u16 child_ldt_sel = p->ldt_sel;
 *p = proc_table[pid];
 p->ldt_sel = child_ldt_sel;
 p->p_parent = pid;
 sprintf(p->name, "%s_%d", proc_table[pid].name, child_pid);

 /* duplicate the process: T, D & S */
 struct descriptor * ppd;

 /* Text segment */
 ppd = &proc_table[pid].ldts[INDEX_LDT_C];
 /* base of T-seg, in bytes */
 int caller_T_base  = reassembly(ppd->base_high, 24,
     ppd->base_mid,  16,
     ppd->base_low);
 /* limit of T-seg, in 1 or 4096 bytes,
    depending on the G bit of descriptor */
 int caller_T_limit = reassembly(0, 0,
     (ppd->limit_high_attr2 & 0xF), 16,
     ppd->limit_low);
 /* size of T-seg, in bytes */
 int caller_T_size  = ((caller_T_limit + 1) *
         ((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
          4096 : 1));

 /* Data & Stack segments */
 ppd = &proc_table[pid].ldts[INDEX_LDT_RW];
 /* base of D&S-seg, in bytes */
 int caller_D_S_base  = reassembly(ppd->base_high, 24,
       ppd->base_mid,  16,
       ppd->base_low);
 /* limit of D&S-seg, in 1 or 4096 bytes,
    depending on the G bit of descriptor */
 int caller_D_S_limit = reassembly((ppd->limit_high_attr2 & 0xF), 16,
       0, 0,
       ppd->limit_low);
 /* size of D&S-seg, in bytes */
 int caller_D_S_size  = ((caller_T_limit + 1) *
    ((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
     4096 : 1));

 /* we don't separate T, D & S segments, so we have: */
 assert((caller_T_base  == caller_D_S_base ) &&
        (caller_T_limit == caller_D_S_limit) &&
        (caller_T_size  == caller_D_S_size ));

 /* base of child proc, T, D & S segments share the same space,
    so we allocate memory just once */
 int child_base = alloc_mem(child_pid, caller_T_size);

 /* child is a copy of the parent */
 phys_copy((void*)child_base, (void*)caller_T_base, caller_T_size);

 /* child's LDT */
 init_desc(&p->ldts[INDEX_LDT_C],
    child_base,
    (PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
    DA_LIMIT_4K | DA_32 | DA_C | PRIVILEGE_USER << 5);
 init_desc(&p->ldts[INDEX_LDT_RW],
    child_base,
    (PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
    DA_LIMIT_4K | DA_32 | DA_DRW | PRIVILEGE_USER << 5);

 /* tell FS, see fs_fork() */
 MESSAGE msg2fs;
 msg2fs.type = FORK;
 msg2fs.PID = child_pid;
 send_recv(BOTH, TASK_FS, &msg2fs);

 /* child PID will be returned to the parent proc */
 mm_msg.PID = child_pid;

 /* birth of the child */
 MESSAGE m;
 m.type = SYSCALL_RET;
 m.RETVAL = 0;
 m.PID = 0;
 send_recv(SEND, child_pid, &m);

 return 0;
}

/*****************************************************************************
 *                                do_exit
 *****************************************************************************/
/**
 * Perform the exit() syscall.
 *
 * If proc A calls exit(), then MM will do the following in this routine:
 *     <1> inform FS so that the fd-related things will be cleaned up
 *     <2> free A's memory
 *     <3> set A.exit_status, which is for the parent
 *     <4> depends on parent's status. if parent (say P) is:
 *           (1) WAITING
 *                 - clean P's WAITING bit, and
 *                 - send P a message to unblock it
 *                 - release A's proc_table[] slot
 *           (2) not WAITING
 *                 - set A's HANGING bit
 *     <5> iterate proc_table[], if proc B is found as A's child, then:
 *           (1) make INIT the new parent of B, and
 *           (2) if INIT is WAITING and B is HANGING, then:
 *                 - clean INIT's WAITING bit, and
 *                 - send INIT a message to unblock it
 *                 - release B's proc_table[] slot
 *               else
 *                 if INIT is WAITING but B is not HANGING, then
 *                     - B will call exit()
 *                 if B is HANGING but INIT is not WAITING, then
 *                     - INIT will call wait()
 *
 * TERMs:
 *     - HANGING: everything except the proc_table entry has been cleaned up.
 *     - WAITING: a proc has at least one child, and it is waiting for the
 *                child(ren) to exit()
 *     - zombie: say P has a child A, A will become a zombie if
 *         - A exit(), and
 *         - P does not wait(), neither does it exit(). that is to say, P just
 *           keeps running without terminating itself or its child
 *
 * @param status  Exiting status for parent.
 *
 *****************************************************************************/
PUBLIC void do_exit(int status)
{
 int i;
 int pid = mm_msg.source; /* PID of caller */
 int parent_pid = proc_table[pid].p_parent;
 struct proc * p = &proc_table[pid];

 /* tell FS, see fs_exit() */
 MESSAGE msg2fs;
 msg2fs.type = EXIT;
 msg2fs.PID = pid;
 send_recv(BOTH, TASK_FS, &msg2fs);

 free_mem(pid);

 p->exit_status = status;

 if (proc_table[parent_pid].p_flags & WAITING) { /* parent is waiting */
  proc_table[parent_pid].p_flags &= ~WAITING;
  cleanup(&proc_table[pid]);
 }
 else { /* parent is not waiting */
  proc_table[pid].p_flags |= HANGING;
 }

 /* if the proc has any child, make INIT the new parent */
 for (i = 0; i < NR_TASKS + NR_PROCS; i++) {
  if (proc_table[i].p_parent == pid) { /* is a child */
   proc_table[i].p_parent = INIT;
   if ((proc_table[INIT].p_flags & WAITING) &&
       (proc_table[i].p_flags & HANGING)) {
    proc_table[INIT].p_flags &= ~WAITING;
    cleanup(&proc_table[i]);
   }
  }
 }
}

/*****************************************************************************
 *                                cleanup
 *****************************************************************************/
/**
 * Do the last jobs to clean up a proc thoroughly:
 *     - Send proc's parent a message to unblock it, and
 *     - release proc's proc_table[] entry
 *
 * @param proc  Process to clean up.
 *****************************************************************************/
PRIVATE void cleanup(struct proc * proc)
{
 MESSAGE msg2parent;
 msg2parent.type = SYSCALL_RET;
 msg2parent.PID = proc2pid(proc);
 msg2parent.STATUS = proc->exit_status;
 send_recv(SEND, proc->p_parent, &msg2parent);

 proc->p_flags = FREE_SLOT;
}

/*****************************************************************************
 *                                do_wait
 *****************************************************************************/
/**
 * Perform the wait() syscall.
 *
 * If proc P calls wait(), then MM will do the following in this routine:
 *     <1> iterate proc_table[],
 *         if proc A is found as P's child and it is HANGING
 *           - reply to P (cleanup() will send P a messageto unblock it)
 *           - release A's proc_table[] entry
 *           - return (MM will go on with the next message loop)
 *     <2> if no child of P is HANGING
 *           - set P's WAITING bit
 *     <3> if P has no child at all
 *           - reply to P with error
 *     <4> return (MM will go on with the next message loop)
 *
 *****************************************************************************/
PUBLIC void do_wait()
{
 int pid = mm_msg.source;

 int i;
 int children = 0;
 struct proc* p_proc = proc_table;
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p_proc++) {
  if (p_proc->p_parent == pid) {
   children++;
   if (p_proc->p_flags & HANGING) {
    cleanup(p_proc);
    return;
   }
  }
 }

 if (children) {
  /* has children, but no child is HANGING */
  proc_table[pid].p_flags |= WAITING;
 }
 else {
  /* no child at all */
  MESSAGE msg;
  msg.type = SYSCALL_RET;
  msg.PID = NO_TASK;
  send_recv(SEND, pid, &msg);
 }
}

mm/main.c

/*************************************************************************//**
 *****************************************************************************
 * @file   mm/main.c
 * @brief  Orange'S Memory Management.
 * @author Forrest Y. Yu
 * @date   Tue May  6 00:33:39 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "config.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

PUBLIC void do_fork_test();

PRIVATE void init_mm();

/*****************************************************************************
 *                                task_mm
 *****************************************************************************/
/**
 * <Ring 1> The main loop of TASK MM.
 *
 *****************************************************************************/
PUBLIC void task_mm()
{
 init_mm();

 while (1) {
  send_recv(RECEIVE, ANY, &mm_msg);
  int src = mm_msg.source;
  int reply = 1;

  int msgtype = mm_msg.type;

  switch (msgtype) {
  case FORK:
   mm_msg.RETVAL = do_fork();
   break;
  case EXIT:
   do_exit(mm_msg.STATUS);
   reply = 0;
   break;
  case EXEC:
   mm_msg.RETVAL = do_exec();
   break;
  case WAIT:
   do_wait();
   reply = 0;
   break;
  default:
   dump_msg("MM::unknown msg", &mm_msg);
   assert(0);
   break;
  }

  if (reply) {
   mm_msg.type = SYSCALL_RET;
   send_recv(SEND, src, &mm_msg);
  }
 }
}

/*****************************************************************************
 *                                init_mm
 *****************************************************************************/
/**
 * Do some initialization work.
 *
 *****************************************************************************/
PRIVATE void init_mm()
{
 struct boot_params bp;
 get_boot_params(&bp);

 memory_size = bp.mem_size;

 /* print memory size */
 printl("{MM} memsize:%dMB\n", memory_size / (1024 * 1024));
}

/*****************************************************************************
 *                                alloc_mem
 *****************************************************************************/
/**
 * Allocate a memory block for a proc.
 *
 * @param pid  Which proc the memory is for.
 * @param memsize  How many bytes is needed.
 *
 * @return  The base of the memory just allocated.
 *****************************************************************************/
PUBLIC int alloc_mem(int pid, int memsize)
{
 assert(pid >= (NR_TASKS + NR_NATIVE_PROCS));
 if (memsize > PROC_IMAGE_SIZE_DEFAULT) {
  panic("unsupported memory request: %d. "
        "(should be less than %d)",
        memsize,
        PROC_IMAGE_SIZE_DEFAULT);
 }

 int base = PROCS_BASE +
  (pid - (NR_TASKS + NR_NATIVE_PROCS)) * PROC_IMAGE_SIZE_DEFAULT;

 if (base + memsize >= memory_size)
  panic("memory allocation failed. pid:%d", pid);

 return base;
}

/*****************************************************************************
 *                                free_mem
 *****************************************************************************/
/**
 * Free a memory block. Because a memory block is corresponding with a PID, so
 * we don't need to really `free' anything. In another word, a memory block is
 * dedicated to one and only one PID, no matter what proc actually uses this
 * PID.
 *
 * @param pid  Whose memory is to be freed.
 *
 * @return  Zero if success.
 *****************************************************************************/
PUBLIC int free_mem(int pid)
{
 return 0;
}

lib/exec.c

/*************************************************************************//**
 *****************************************************************************
 * @file   lib/exec.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Tue May  6 14:26:09 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

/*****************************************************************************
 *                                exec
 *****************************************************************************/
/**
 * Executes the program pointed by path.
 *
 * @param path  The full path of the file to be executed.
 *
 * @return  Zero if successful, otherwise -1.
 *****************************************************************************/
PUBLIC int exec(const char * path)
{
 MESSAGE msg;
 msg.type = EXEC;
 msg.PATHNAME = (void*)path;
 msg.NAME_LEN = strlen(path);
 msg.BUF  = 0;
 msg.BUF_LEN = 0;

 send_recv(BOTH, TASK_MM, &msg);
 assert(msg.type == SYSCALL_RET);

 return msg.RETVAL;
}

/*****************************************************************************
 *                                execl
 *****************************************************************************/
PUBLIC int execl(const char *path, const char *arg, ...)
{
 va_list parg = (va_list)(&arg);
 char **p = (char**)parg;
 return execv(path, p);
}

/*****************************************************************************
 *                                execv
 *****************************************************************************/
PUBLIC int execv(const char *path, char * argv[])
{
 char **p = argv;
 char arg_stack[PROC_ORIGIN_STACK];
 int stack_len = 0;

 while(*p++) {
  assert(stack_len + 2 * sizeof(char*) < PROC_ORIGIN_STACK);
  stack_len += sizeof(char*);
 }

 *((int*)(&arg_stack[stack_len])) = 0;
 stack_len += sizeof(char*);

 char ** q = (char**)arg_stack;
 for (p = argv; *p != 0; p++) {
  *q++ = &arg_stack[stack_len];

  assert(stack_len + strlen(*p) + 1 < PROC_ORIGIN_STACK);
  strcpy(&arg_stack[stack_len], *p);
  stack_len += strlen(*p);
  arg_stack[stack_len] = 0;
  stack_len++;
 }

 MESSAGE msg;
 msg.type = EXEC;
 msg.PATHNAME = (void*)path;
 msg.NAME_LEN = strlen(path);
 msg.BUF  = (void*)arg_stack;
 msg.BUF_LEN = stack_len;

 send_recv(BOTH, TASK_MM, &msg);
 assert(msg.type == SYSCALL_RET);

 return msg.RETVAL;
}

mm/exec.c

/*************************************************************************//**
 *****************************************************************************
 * @file   mm/exec.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Tue May  6 14:14:02 2008
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#include "elf.h"


/*****************************************************************************
 *                                do_exec
 *****************************************************************************/
/**
 * Perform the exec() system call.
 *
 * @return  Zero if successful, otherwise -1.
 *****************************************************************************/
PUBLIC int do_exec()
{
 /* get parameters from the message */
 int name_len = mm_msg.NAME_LEN; /* length of filename */
 int src = mm_msg.source; /* caller proc nr. */
 assert(name_len < MAX_PATH);

 char pathname[MAX_PATH];
 phys_copy((void*)va2la(TASK_MM, pathname),
    (void*)va2la(src, mm_msg.PATHNAME),
    name_len);
 pathname[name_len] = 0; /* terminate the string */

 /* get the file size */
 struct stat s;
 int ret = stat(pathname, &s);
 if (ret != 0) {
  printl("{MM} MM::do_exec()::stat() returns error. %s", pathname);
  return -1;
 }

 /* read the file */
 int fd = open(pathname, O_RDWR);
 if (fd == -1)
  return -1;
 assert(s.st_size < MMBUF_SIZE);
 read(fd, mmbuf, s.st_size);
 close(fd);

 /* overwrite the current proc image with the new one */
 Elf32_Ehdr* elf_hdr = (Elf32_Ehdr*)(mmbuf);
 int i;
 for (i = 0; i < elf_hdr->e_phnum; i++) {
  Elf32_Phdr* prog_hdr = (Elf32_Phdr*)(mmbuf + elf_hdr->e_phoff +
       (i * elf_hdr->e_phentsize));
  if (prog_hdr->p_type == PT_LOAD) {
   assert(prog_hdr->p_vaddr + prog_hdr->p_memsz <
    PROC_IMAGE_SIZE_DEFAULT);
   phys_copy((void*)va2la(src, (void*)prog_hdr->p_vaddr),
      (void*)va2la(TASK_MM,
       mmbuf + prog_hdr->p_offset),
      prog_hdr->p_filesz);
  }
 }

 /* setup the arg stack */
 int orig_stack_len = mm_msg.BUF_LEN;
 char stackcopy[PROC_ORIGIN_STACK];
 phys_copy((void*)va2la(TASK_MM, stackcopy),
    (void*)va2la(src, mm_msg.BUF),
    orig_stack_len);

 u8 * orig_stack = (u8*)(PROC_IMAGE_SIZE_DEFAULT - PROC_ORIGIN_STACK);

 int delta = (int)orig_stack - (int)mm_msg.BUF;

 int argc = 0;
 if (orig_stack_len) { /* has args */
  char **q = (char**)stackcopy;
  for (; *q != 0; q++,argc++)
   *q += delta;
 }

 phys_copy((void*)va2la(src, orig_stack),
    (void*)va2la(TASK_MM, stackcopy),
    orig_stack_len);

 proc_table[src].regs.ecx = argc; /* argc */
 proc_table[src].regs.eax = (u32)orig_stack; /* argv */

 /* setup eip & esp */
 proc_table[src].regs.eip = elf_hdr->e_entry; /* @see _start.asm */
 proc_table[src].regs.esp = PROC_IMAGE_SIZE_DEFAULT - PROC_ORIGIN_STACK;

 strcpy(proc_table[src].name, pathname);

 return 0;
}

fs/main.c

/*************************************************************************//**
 *****************************************************************************
 * @file   main.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2007
 *****************************************************************************
 *****************************************************************************/

#include "type.h"
#include "config.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"

#include "hd.h"

PRIVATE void init_fs();
PRIVATE void mkfs();
PRIVATE void read_super_block(int dev);
PRIVATE int fs_fork();
PRIVATE int fs_exit();

/*****************************************************************************
 *                                task_fs
 *****************************************************************************/
/**
 * <Ring 1> The main loop of TASK FS.
 *
 *****************************************************************************/
PUBLIC void task_fs()
{
 printl("{FS} Task FS begins.\n");

 init_fs();

 while (1) {
  send_recv(RECEIVE, ANY, &fs_msg);

  int msgtype = fs_msg.type;
  int src = fs_msg.source;
  pcaller = &proc_table[src];

  switch (msgtype) {
  case OPEN:
   fs_msg.FD = do_open();
   break;
  case CLOSE:
   fs_msg.RETVAL = do_close();
   break;
  case READ:
  case WRITE:
   fs_msg.CNT = do_rdwt();
   break;
  case UNLINK:
   fs_msg.RETVAL = do_unlink();
   break;
  case RESUME_PROC:
   src = fs_msg.PROC_NR;
   break;
  case FORK:
   fs_msg.RETVAL = fs_fork();
   break;
  case EXIT:
   fs_msg.RETVAL = fs_exit();
   break;
  /* case LSEEK: */
  /*  fs_msg.OFFSET = do_lseek(); */
  /*  break; */
  case STAT:
   fs_msg.RETVAL = do_stat();
   break;
  default:
   dump_msg("FS::unknown message:", &fs_msg);
   assert(0);
   break;
  }

#ifdef ENABLE_DISK_LOG
  char * msg_name[128];
  msg_name[OPEN]   = "OPEN";
  msg_name[CLOSE]  = "CLOSE";
  msg_name[READ]   = "READ";
  msg_name[WRITE]  = "WRITE";
  msg_name[LSEEK]  = "LSEEK";
  msg_name[UNLINK] = "UNLINK";
  /* msg_name[FORK]   = "FORK"; */
  /* msg_name[EXIT]   = "EXIT"; */
  /* msg_name[STAT]   = "STAT"; */

  switch (msgtype) {
  case UNLINK:
   dump_fd_graph("%s just finished. (pid:%d)",
          msg_name[msgtype], src);
   //panic("");
  case OPEN:
  case CLOSE:
  case READ:
  case WRITE:
  case FORK:
  case EXIT:
  /* case LSEEK: */
  case STAT:
   break;
  case RESUME_PROC:
   break;
  default:
   assert(0);
  }
#endif

  /* reply */
  if (fs_msg.type != SUSPEND_PROC) {
   fs_msg.type = SYSCALL_RET;
   send_recv(SEND, src, &fs_msg);
  }
 }
}

/*****************************************************************************
 *                                init_fs
 *****************************************************************************/
/**
 * <Ring 1> Do some preparation.
 *
 *****************************************************************************/
PRIVATE void init_fs()
{
 int i;

 /* f_desc_table[] */
 for (i = 0; i < NR_FILE_DESC; i++)
  memset(&f_desc_table[i], 0, sizeof(struct file_desc));

 /* inode_table[] */
 for (i = 0; i < NR_INODE; i++)
  memset(&inode_table[i], 0, sizeof(struct inode));

 /* super_block[] */
 struct super_block * sb = super_block;
 for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
  sb->sb_dev = NO_DEV;

 /* open the device: hard disk */
 MESSAGE driver_msg;
 driver_msg.type = DEV_OPEN;
 driver_msg.DEVICE = MINOR(ROOT_DEV);
 assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);

 /* make FS */
 mkfs();

 /* load super block of ROOT */
 read_super_block(ROOT_DEV);

 sb = get_super_block(ROOT_DEV);
 assert(sb->magic == MAGIC_V1);

 root_inode = get_inode(ROOT_DEV, ROOT_INODE);
}

/*****************************************************************************
 *                                mkfs
 *****************************************************************************/
/**
 * <Ring 1> Make a available Orange'S FS in the disk. It will
 *          - Write a super block to sector 1.
 *          - Create three special files: dev_tty0, dev_tty1, dev_tty2
 *          - Create a file cmd.tar
 *          - Create the inode map
 *          - Create the sector map
 *          - Create the inodes of the files
 *          - Create `/', the root directory
 *****************************************************************************/
PRIVATE void mkfs()
{
 MESSAGE driver_msg;
 int i, j;

 /************************/
 /*      super block     */
 /************************/
 /* get the geometry of ROOTDEV */
 struct part_info geo;
 driver_msg.type  = DEV_IOCTL;
 driver_msg.DEVICE = MINOR(ROOT_DEV);
 driver_msg.REQUEST = DIOCTL_GET_GEO;
 driver_msg.BUF  = &geo;
 driver_msg.PROC_NR = TASK_FS;
 assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);

 printl("{FS} dev size: 0x%x sectors\n", geo.size);

 int bits_per_sect = SECTOR_SIZE * 8; /* 8 bits per byte */
 /* generate a super block */
 struct super_block sb;
 sb.magic   = MAGIC_V1; /* 0x111 */
 sb.nr_inodes   = bits_per_sect;
 sb.nr_inode_sects = sb.nr_inodes * INODE_SIZE / SECTOR_SIZE;
 sb.nr_sects   = geo.size; /* partition size in sector */
 sb.nr_imap_sects  = 1;
 sb.nr_smap_sects  = sb.nr_sects / bits_per_sect + 1;
 sb.n_1st_sect   = 1 + 1 +   /* boot sector & super block */
  sb.nr_imap_sects + sb.nr_smap_sects + sb.nr_inode_sects;
 sb.root_inode   = ROOT_INODE;
 sb.inode_size   = INODE_SIZE;
 struct inode x;
 sb.inode_isize_off= (int)&x.i_size - (int)&x;
 sb.inode_start_off= (int)&x.i_start_sect - (int)&x;
 sb.dir_ent_size   = DIR_ENTRY_SIZE;
 struct dir_entry de;
 sb.dir_ent_inode_off = (int)&de.inode_nr - (int)&de;
 sb.dir_ent_fname_off = (int)&de.name - (int)&de;

 memset(fsbuf, 0x90, SECTOR_SIZE);
 memcpy(fsbuf, &sb, SUPER_BLOCK_SIZE);

 /* write the super block */
 WR_SECT(ROOT_DEV, 1);

 printl("{FS} devbase:0x%x00, sb:0x%x00, imap:0x%x00, smap:0x%x00\n"
        "        inodes:0x%x00, 1st_sector:0x%x00\n",
        geo.base * 2,
        (geo.base + 1) * 2,
        (geo.base + 1 + 1) * 2,
        (geo.base + 1 + 1 + sb.nr_imap_sects) * 2,
        (geo.base + 1 + 1 + sb.nr_imap_sects + sb.nr_smap_sects) * 2,
        (geo.base + sb.n_1st_sect) * 2);

 /************************/
 /*       inode map      */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 for (i = 0; i < (NR_CONSOLES + 3); i++)
  fsbuf[0] |= 1 << i;

 assert(fsbuf[0] == 0x3F);/* 0011 1111 :
      *   || ||||
      *   || |||`--- bit 0 : reserved
      *   || ||`---- bit 1 : the first inode,
      *   || ||              which indicates `/'
      *   || |`----- bit 2 : /dev_tty0
      *   || `------ bit 3 : /dev_tty1
      *   |`-------- bit 4 : /dev_tty2
      *   `--------- bit 5 : /cmd.tar
      */
 WR_SECT(ROOT_DEV, 2);

 /************************/
 /*      secter map      */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 int nr_sects = NR_DEFAULT_FILE_SECTS + 1;
 /*             ~~~~~~~~~~~~~~~~~~~|~   |
  *                                |    `--- bit 0 is reserved
  *                                `-------- for `/'
  */
 for (i = 0; i < nr_sects / 8; i++)
  fsbuf[i] = 0xFF;

 for (j = 0; j < nr_sects % 8; j++)
  fsbuf[i] |= (1 << j);

 WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects);

 /* zeromemory the rest sector-map */
 memset(fsbuf, 0, SECTOR_SIZE);
 for (i = 1; i < sb.nr_smap_sects; i++)
  WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + i);

 /* cmd.tar */
 /* make sure it'll not be overwritten by the disk log */
 assert(INSTALL_START_SECT + INSTALL_NR_SECTS <
        sb.nr_sects - NR_SECTS_FOR_LOG);
 int bit_offset = INSTALL_START_SECT -
  sb.n_1st_sect + 1; /* sect M <-> bit (M - sb.n_1stsect + 1) */
 int bit_off_in_sect = bit_offset % (SECTOR_SIZE * 8);
 int bit_left = INSTALL_NR_SECTS;
 int cur_sect = bit_offset / (SECTOR_SIZE * 8);
 RD_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
 while (bit_left) {
  int byte_off = bit_off_in_sect / 8;
  /* this line is ineffecient in a loop, but I don't care */
  fsbuf[byte_off] |= 1 << (bit_off_in_sect % 8);
  bit_left--;
  bit_off_in_sect++;
  if (bit_off_in_sect == (SECTOR_SIZE * 8)) {
   WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
   cur_sect++;
   RD_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
   bit_off_in_sect = 0;
  }
 }
 WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);

 /************************/
 /*       inodes         */
 /************************/
 /* inode of `/' */
 memset(fsbuf, 0, SECTOR_SIZE);
 struct inode * pi = (struct inode*)fsbuf;
 pi->i_mode = I_DIRECTORY;
 pi->i_size = DIR_ENTRY_SIZE * 5; /* 5 files:
       * `.',
       * `dev_tty0', `dev_tty1', `dev_tty2',
       * `cmd.tar'
       */
 pi->i_start_sect = sb.n_1st_sect;
 pi->i_nr_sects = NR_DEFAULT_FILE_SECTS;
 /* inode of `/dev_tty0~2' */
 for (i = 0; i < NR_CONSOLES; i++) {
  pi = (struct inode*)(fsbuf + (INODE_SIZE * (i + 1)));
  pi->i_mode = I_CHAR_SPECIAL;
  pi->i_size = 0;
  pi->i_start_sect = MAKE_DEV(DEV_CHAR_TTY, i);
  pi->i_nr_sects = 0;
 }
 /* inode of `/cmd.tar' */
 pi = (struct inode*)(fsbuf + (INODE_SIZE * (NR_CONSOLES + 1)));
 pi->i_mode = I_REGULAR;
 pi->i_size = INSTALL_NR_SECTS * SECTOR_SIZE;
 pi->i_start_sect = INSTALL_START_SECT;
 pi->i_nr_sects = INSTALL_NR_SECTS;
 WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + sb.nr_smap_sects);

 /************************/
 /*          `/'         */
 /************************/
 memset(fsbuf, 0, SECTOR_SIZE);
 struct dir_entry * pde = (struct dir_entry *)fsbuf;

 pde->inode_nr = 1;
 strcpy(pde->name, ".");

 /* dir entries of `/dev_tty0~2' */
 for (i = 0; i < NR_CONSOLES; i++) {
  pde++;
  pde->inode_nr = i + 2; /* dev_tty0's inode_nr is 2 */
  sprintf(pde->name, "dev_tty%d", i);
 }
 (++pde)->inode_nr = NR_CONSOLES + 2;
 sprintf(pde->name, "cmd.tar", i);
 WR_SECT(ROOT_DEV, sb.n_1st_sect);
}

/*****************************************************************************
 *                                rw_sector
 *****************************************************************************/
/**
 * <Ring 1> R/W a sector via messaging with the corresponding driver.
 *
 * @param io_type  DEV_READ or DEV_WRITE
 * @param dev      device nr
 * @param pos      Byte offset from/to where to r/w.
 * @param bytes    r/w count in bytes.
 * @param proc_nr  To whom the buffer belongs.
 * @param buf      r/w buffer.
 *
 * @return Zero if success.
 *****************************************************************************/
PUBLIC int rw_sector(int io_type, int dev, u64 pos, int bytes, int proc_nr,
       void* buf)
{
 MESSAGE driver_msg;

 driver_msg.type  = io_type;
 driver_msg.DEVICE = MINOR(dev);
 driver_msg.POSITION = pos;
 driver_msg.BUF  = buf;
 driver_msg.CNT  = bytes;
 driver_msg.PROC_NR = proc_nr;
 assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);

 return 0;
}


/*****************************************************************************
 *                                read_super_block
 *****************************************************************************/
/**
 * <Ring 1> Read super block from the given device then write it into a free
 *          super_block[] slot.
 *
 * @param dev  From which device the super block comes.
 *****************************************************************************/
PRIVATE void read_super_block(int dev)
{
 int i;
 MESSAGE driver_msg;

 driver_msg.type  = DEV_READ;
 driver_msg.DEVICE = MINOR(dev);
 driver_msg.POSITION = SECTOR_SIZE * 1;
 driver_msg.BUF  = fsbuf;
 driver_msg.CNT  = SECTOR_SIZE;
 driver_msg.PROC_NR = TASK_FS;
 assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
 send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);

 /* find a free slot in super_block[] */
 for (i = 0; i < NR_SUPER_BLOCK; i++)
  if (super_block[i].sb_dev == NO_DEV)
   break;
 if (i == NR_SUPER_BLOCK)
  panic("super_block slots used up");

 assert(i == 0); /* currently we use only the 1st slot */

 struct super_block * psb = (struct super_block *)fsbuf;

 super_block[i] = *psb;
 super_block[i].sb_dev = dev;
}


/*****************************************************************************
 *                                get_super_block
 *****************************************************************************/
/**
 * <Ring 1> Get the super block from super_block[].
 *
 * @param dev Device nr.
 *
 * @return Super block ptr.
 *****************************************************************************/
PUBLIC struct super_block * get_super_block(int dev)
{
 struct super_block * sb = super_block;
 for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
  if (sb->sb_dev == dev)
   return sb;

 panic("super block of devie %d not found.\n", dev);

 return 0;
}


/*****************************************************************************
 *                                get_inode
 *****************************************************************************/
/**
 * <Ring 1> Get the inode ptr of given inode nr. A cache -- inode_table[] -- is
 * maintained to make things faster. If the inode requested is already there,
 * just return it. Otherwise the inode will be read from the disk.
 *
 * @param dev Device nr.
 * @param num I-node nr.
 *
 * @return The inode ptr requested.
 *****************************************************************************/
PUBLIC struct inode * get_inode(int dev, int num)
{
 if (num == 0)
  return 0;

 struct inode * p;
 struct inode * q = 0;
 for (p = &inode_table[0]; p < &inode_table[NR_INODE]; p++) {
  if (p->i_cnt) { /* not a free slot */
   if ((p->i_dev == dev) && (p->i_num == num)) {
    /* this is the inode we want */
    p->i_cnt++;
    return p;
   }
  }
  else {  /* a free slot */
   if (!q) /* q hasn't been assigned yet */
    q = p; /* q <- the 1st free slot */
  }
 }

 if (!q)
  panic("the inode table is full");

 q->i_dev = dev;
 q->i_num = num;
 q->i_cnt = 1;

 struct super_block * sb = get_super_block(dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
  ((num - 1) / (SECTOR_SIZE / INODE_SIZE));
 RD_SECT(dev, blk_nr);
 struct inode * pinode =
  (struct inode*)((u8*)fsbuf +
    ((num - 1 ) % (SECTOR_SIZE / INODE_SIZE))
     * INODE_SIZE);
 q->i_mode = pinode->i_mode;
 q->i_size = pinode->i_size;
 q->i_start_sect = pinode->i_start_sect;
 q->i_nr_sects = pinode->i_nr_sects;
 return q;
}

/*****************************************************************************
 *                                put_inode
 *****************************************************************************/
/**
 * Decrease the reference nr of a slot in inode_table[]. When the nr reaches
 * zero, it means the inode is not used any more and can be overwritten by
 * a new inode.
 *
 * @param pinode I-node ptr.
 *****************************************************************************/
PUBLIC void put_inode(struct inode * pinode)
{
 assert(pinode->i_cnt > 0);
 pinode->i_cnt--;
}

/*****************************************************************************
 *                                sync_inode
 *****************************************************************************/
/**
 * <Ring 1> Write the inode back to the disk. Commonly invoked as soon as the
 *          inode is changed.
 *
 * @param p I-node ptr.
 *****************************************************************************/
PUBLIC void sync_inode(struct inode * p)
{
 struct inode * pinode;
 struct super_block * sb = get_super_block(p->i_dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
  ((p->i_num - 1) / (SECTOR_SIZE / INODE_SIZE));
 RD_SECT(p->i_dev, blk_nr);
 pinode = (struct inode*)((u8*)fsbuf +
     (((p->i_num - 1) % (SECTOR_SIZE / INODE_SIZE))
      * INODE_SIZE));
 pinode->i_mode = p->i_mode;
 pinode->i_size = p->i_size;
 pinode->i_start_sect = p->i_start_sect;
 pinode->i_nr_sects = p->i_nr_sects;
 WR_SECT(p->i_dev, blk_nr);
}

/*****************************************************************************
 *                                fs_fork
 *****************************************************************************/
/**
 * Perform the aspects of fork() that relate to files.
 *
 * @return Zero if success, otherwise a negative integer.
 *****************************************************************************/
PRIVATE int fs_fork()
{
 int i;
 struct proc* child = &proc_table[fs_msg.PID];
 for (i = 0; i < NR_FILES; i++) {
  if (child->filp[i]) {
   child->filp[i]->fd_cnt++;
   child->filp[i]->fd_inode->i_cnt++;
  }
 }

 return 0;
}


/*****************************************************************************
 *                                fs_exit
 *****************************************************************************/
/**
 * Perform the aspects of exit() that relate to files.
 *
 * @return Zero if success.
 *****************************************************************************/
PRIVATE int fs_exit()
{
 int i;
 struct proc* p = &proc_table[fs_msg.PID];
 for (i = 0; i < NR_FILES; i++) {
  if (p->filp[i]) {
   /* release the inode */
   p->filp[i]->fd_inode->i_cnt--;
   /* release the file desc slot */
   if (--p->filp[i]->fd_cnt == 0)
    p->filp[i]->fd_inode = 0;
   p->filp[i] = 0;
  }
 }
 return 0;
}

Makefile

#########################
# Makefile for Orange'S #
#########################

# Entry point of Orange'S
# It must have the same value with 'KernelEntryPointPhyAddr' in load.inc!
ENTRYPOINT = 0x1000

# Offset of entry point in kernel file
# It depends on ENTRYPOINT
ENTRYOFFSET =   0x400

# Programs, flags, etc.
ASM  = nasm
DASM  = objdump
CC  = gcc
LD  = ld
ASMBFLAGS = -I boot/include/
ASMKFLAGS = -I include/ -I include/sys/ -f elf
CFLAGS  = -I include/ -I include/sys/ -m32 -c -fno-builtin -fno-stack-protector
LDFLAGS  = -s -melf_i386 -Ttext $(ENTRYPOINT) -Map krnl.map
DASMFLAGS = -D
ARFLAGS  = rcs

# This Program
ORANGESBOOT = boot/boot.bin boot/loader.bin
ORANGESKERNEL = kernel.bin
LIB  = lib/orangescrt.a

OBJS  = kernel/kernel.o kernel/start.o kernel/main.o\
   kernel/clock.o kernel/keyboard.o kernel/tty.o kernel/console.o\
   kernel/i8259.o kernel/global.o kernel/protect.o kernel/proc.o\
   kernel/systask.o kernel/hd.o\
   kernel/kliba.o kernel/klib.o\
   lib/syslog.o\
   mm/main.o mm/forkexit.o mm/exec.o\
   fs/main.o fs/open.o fs/misc.o fs/read_write.o\
   fs/link.o\
   fs/disklog.o
LOBJS  =  lib/syscall.o\
   lib/printf.o lib/vsprintf.o\
   lib/string.o lib/misc.o\
   lib/open.o lib/read.o lib/write.o lib/close.o lib/unlink.o\
   lib/getpid.o lib/stat.o\
   lib/fork.o lib/exit.o lib/wait.o lib/exec.o
DASMOUTPUT = kernel.bin.asm

# All Phony Targets
.PHONY : everything final image clean realclean disasm all buildimg

# Default starting position
nop :
 @echo "why not \`make image' huh? :)"

everything : $(ORANGESBOOT) $(ORANGESKERNEL)

all : realclean everything

image : realclean everything clean buildimg app vdi

clean :
 rm -f $(OBJS) $(LOBJS)

realclean :
 rm -f $(OBJS) $(LOBJS) $(LIB) $(ORANGESBOOT) $(ORANGESKERNEL)

disasm :
 $(DASM) $(DASMFLAGS) $(ORANGESKERNEL) > $(DASMOUTPUT)

# We assume that "a.img" exists in current folder
buildimg :
 mkdir tmp
 sleep 1
 sudo mount -o loop a.img tmp
 sleep 1
 sudo cp -fv boot/loader.bin tmp
 sudo cp -fv kernel.bin tmp
 sleep 1
 sudo umount tmp
 sleep 1
 rmdir tmp

app:
 cd ./command/; make install

vdi:
 rm 80m.vdi
 VBoxManage convertfromraw -format VDI 80m.img 80m.vdi
 VBoxManage internalcommands sethduuid 80m.vdi ccea0f68-01ea-4b9e-8160-e43ef528ba6e

debug:
 rm t.img
 VBoxManage clonehd -format RAW ccea0f68-01ea-4b9e-8160-e43ef528ba6e  t.img
 xxd -u -a -g 1 -c 16 -s 0xAD9C00 -l 20000 t.img
 

boot/boot.bin : boot/boot.asm boot/include/load.inc boot/include/fat12hdr.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

boot/loader.bin : boot/loader.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

$(ORANGESKERNEL) : $(OBJS) $(LIB)
 $(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $^

$(LIB) : $(LOBJS)
 $(AR) $(ARFLAGS) $@ $^

kernel/kernel.o : kernel/kernel.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/syscall.o : lib/syscall.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

kernel/start.o: kernel/start.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/main.o: kernel/main.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/clock.o: kernel/clock.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/keyboard.o: kernel/keyboard.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/tty.o: kernel/tty.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/console.o: kernel/console.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/i8259.o: kernel/i8259.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/global.o: kernel/global.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/protect.o: kernel/protect.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/proc.o: kernel/proc.c
 $(CC) $(CFLAGS) -o $@ $<

lib/printf.o: lib/printf.c
 $(CC) $(CFLAGS) -o $@ $<

lib/vsprintf.o: lib/vsprintf.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/systask.o: kernel/systask.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/hd.o: kernel/hd.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/klib.o: kernel/klib.c
 $(CC) $(CFLAGS) -o $@ $<

lib/misc.o: lib/misc.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/kliba.o : kernel/kliba.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/string.o : lib/string.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/open.o: lib/open.c
 $(CC) $(CFLAGS) -o $@ $<

lib/read.o: lib/read.c
 $(CC) $(CFLAGS) -o $@ $<

lib/write.o: lib/write.c
 $(CC) $(CFLAGS) -o $@ $<

lib/close.o: lib/close.c
 $(CC) $(CFLAGS) -o $@ $<

lib/unlink.o: lib/unlink.c
 $(CC) $(CFLAGS) -o $@ $<

lib/getpid.o: lib/getpid.c
 $(CC) $(CFLAGS) -o $@ $<

lib/syslog.o: lib/syslog.c
 $(CC) $(CFLAGS) -o $@ $<

lib/fork.o: lib/fork.c
 $(CC) $(CFLAGS) -o $@ $<

lib/exit.o: lib/exit.c
 $(CC) $(CFLAGS) -o $@ $<

lib/wait.o: lib/wait.c
 $(CC) $(CFLAGS) -o $@ $<

lib/exec.o: lib/exec.c
 $(CC) $(CFLAGS) -o $@ $<

lib/stat.o: lib/stat.c
 $(CC) $(CFLAGS) -o $@ $<

mm/main.o: mm/main.c
 $(CC) $(CFLAGS) -o $@ $<

mm/forkexit.o: mm/forkexit.c
 $(CC) $(CFLAGS) -o $@ $<

mm/exec.o: mm/exec.c
 $(CC) $(CFLAGS) -o $@ $<

fs/main.o: fs/main.c
 $(CC) $(CFLAGS) -o $@ $<

fs/open.o: fs/open.c
 $(CC) $(CFLAGS) -o $@ $<

fs/read_write.o: fs/read_write.c
 $(CC) $(CFLAGS) -o $@ $<

fs/link.o: fs/link.c
 $(CC) $(CFLAGS) -o $@ $<

fs/disklog.o: fs/disklog.c
 $(CC) $(CFLAGS) -o $@ $<

程式碼說明

  1. 從訊息體中獲取各種參數。由於使用者和 MM 處在不同的位址空間,所以對於檔案名這樣的一段記憶體,需要透過獲取其物理位置並進行物理位址複製。
  2. 透過一個新的系統使用 stat() 獲取被執行檔的大小
  3. 將被執行檔全部讀入 MM 自己的緩衝區(MM 的緩衝區有 1MB,我們姑且假設這個空間足夠了。等有一天真的不夠了,會觸發一個 assert,到時我們再做打算)。
  4. 根據 ELF 檔的程式頭(ProgramHeader)資訊,將被執行檔的各個段放置到合適的位置。
  5. 建立參數堆疊-這個堆疊在 execv() 已經準備好了,但由於記憶體空間發生了變化,所以裡面所有的指標都需要重新定位,這個過程並不難,透過一個 delta 變數即可完成
  6. 為被執行程式的 eax 和 ecx 賦值-還記得 _start 中我們將 eax 和 ecx 壓入堆疊嗎?壓的就是 argv 和 argc。它們是在這裡被賦值的。
  7. 為程式的 eip 賦值,這是程式的入口位址,即 _start 處。
  8. 為程式的 esp 賦值。這熠燿閃出剛才我們準備好的堆疊的位置。
  9. 最後是將處理序的名字改成被執行程式的名字。
執行結果

注意事項


  • 請留意 ROOT_BASE(boot/include/load.inc)的位址:必須對應開始磁區。如結果的圖,我預計將作業系統放在 HD 16 (hd2a),其開始磁區為 0x56C0
  • 請留意 INSTALL_START_SECT(include/sys/config.h)的程式放置位置:請盡量放磁區最後。如圖,HD 16 的大小為 0xA000,我選擇放在 0x8900。
  • 請留意 fs/main.c, line 282-283:INSTALL_START_SECT 雖然放後面一點,但還是有其極限:INSTALL_START_SECT - INSTALL_NR_SECTS < sb.nr_sects - NR_SECTS_FOR_LOG,亦即 INSTALL_START_SECT + 0x800 < 0xA000 - 0x800,故其極限應為 0x8FFF。

編譯步驟

$ make image

目錄結構

.
├── 80m.img
├── 80m.vdi
├── a.img
├── boot
│   ├── boot.asm
│   ├── include
│   │   ├── fat12hdr.inc
│   │   ├── load.inc
│   │   └── pm.inc
│   └── loader.asm
├── command
│   ├── echo.c
│   ├── Makefile
│   ├── pwd.c
│   ├── start.asm
├── fs
│   ├── disklog.c
│   ├── link.c
│   ├── main.c
│   ├── misc.c
│   ├── open.c
│   └── read_write.c
├── include
│   ├── stdio.h
│   ├── string.h
│   ├── sys
│   │   ├── config.h
│   │   ├── console.h
│   │   ├── const.h
│   │   ├── fs.h
│   │   ├── global.h
│   │   ├── hd.h
│   │   ├── keyboard.h
│   │   ├── keymap.h
│   │   ├── proc.h
│   │   ├── protect.h
│   │   ├── proto.h
│   │   ├── sconst.inc
│   │   └── tty.h
│   └── type.h
├── kernel
│   ├── clock.c
│   ├── console.c
│   ├── global.c
│   ├── hd.c
│   ├── i8259.c
│   ├── kernel.asm
│   ├── keyboard.c
│   ├── kliba.asm
│   ├── klib.c
│   ├── main.c
│   ├── proc.c
│   ├── protect.c
│   ├── start.c
│   ├── systask.c
│   └── tty.c
├── krnl.map
├── lib
│   ├── close.c
│   ├── exec.c
│   ├── exit.c
│   ├── fork.c
│   ├── getpid.c
│   ├── misc.c
│   ├── open.c
│   ├── printf.c
│   ├── read.c
│   ├── stat.c
│   ├── string.asm
│   ├── syscall.asm
│   ├── syslog.c
│   ├── unlink.c
│   ├── vsprintf.c
│   ├── wait.c
│   └── write.c
├── Makefile
└── mm
    ├── exec.c
    ├── forkexit.c
    └── main.c

留言

這個網誌中的熱門文章

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

VLC c# 順利編譯

原文網址: http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html 原文作者: haibindev 原文標題:c#万能视频播放器 本文的重點在於修正 class VlcPlayer,使其能順利在 VC# Express 2010 .Net Framework 4 下順利編譯。 修正重點在於 CallingConvention = CallingConvention. StdCall 改成 CallingConvention = CallingConvention. Cdecl using System; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace VlcDotNet { class VlcPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VlcPlayer(string pluginPath) { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LibVlcAPI.libvlc_new(arguments); libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_); } public ...

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據 文件 ,Doctrine 有 Pessimistic Lock,又分為兩種: LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE 差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取, 且可以繼續 Select LOCK IN SHARE MODE 。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取, 但不能 Select LOCK IN SHARE MODE 。MySQL 文件有更詳細的比較與情境使用的說明,參考 網址 。 現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock, 但 find 是透過 id 來處理 。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。 因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下: 19 public function depositAction() 20 { 21 22 $em = $this->getDoctrine()->getManager(); 23 24 $em->transactional(function ($em) { 25 $entityName = 'AcmeTrainingBundle:Account'; 26 $lockMode = LockMode::PESSIMISTIC_READ; 27 $orderBy = null; 28 $...