跳到主要內容

記憶體管理-fork

一個新的處理序需要:
  • 自己的程式碼、資料、堆疊
  • 在 proc_table[] 中佔用一個位置
  • 在 GDT 中佔用一個位置,用以存放處理序對應的 LDT 描述符號
問題在於,第一項的程式碼、資料、堆疊從何而來?傳統上,生成一個新處理序時,係直接由某個已執行的處理序處來繼承或複製。 生成子處理序的系統使用及稱之為 fork()。

程式碼

fs/disklog.c

/*************************************************************************//**
 *****************************************************************************
 * @file   disklog.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Thu Nov 20 16:22:45 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"
#include "hd.h"
#include "fs.h"

#define DISKLOG_RD_SECT(dev,sect_nr) rw_sector(DEV_READ, \
            dev,   \
            (sect_nr) * SECTOR_SIZE, \
            SECTOR_SIZE, /* read one sector */ \
            getpid(),  \
            logdiskbuf);
#define DISKLOG_WR_SECT(dev,sect_nr) rw_sector(DEV_WRITE, \
            dev,   \
            (sect_nr) * SECTOR_SIZE, \
            SECTOR_SIZE, /* write one sector */ \
            getpid(),  \
            logdiskbuf);


/* /\***************************************************************************** */
/*  *                                do_disklog */
/*  *****************************************************************************\/ */
/* /\** */
/*  * Perform syslog() system call . */
/*  * */
/*  * @return  */
/*  *****************************************************************************\/ */
/* PUBLIC int do_disklog() */
/* { */
/*  char buf[STR_DEFAULT_LEN]; */

/*  /\* get parameters from the message *\/ */
/*  int str_len = fs_msg.CNT; /\* length of filename *\/ */
/*  int src = fs_msg.source; /\* caller proc nr. *\/ */
/*  assert(str_len < STR_DEFAULT_LEN); */
/*  phys_copy((void*)va2la(TASK_FS, buf),    /\* to   *\/ */
/*     (void*)va2la(src, fs_msg.BUF), /\* from *\/ */
/*     str_len); */
/*  buf[str_len] = 0; /\* terminate the string *\/ */

/*  return disklog(buf); */
/* } */

/*****************************************************************************
 *                                disklog
 *****************************************************************************/
/**
 * <Ring 1> Write log string directly into disk.
 *
 * @param p  Ptr to the MESSAGE.
 *****************************************************************************/
PUBLIC int disklog(char * logstr)
{
 int device = root_inode->i_dev;
 struct super_block * sb = get_super_block(device);
 int nr_log_blk0_nr = sb->nr_sects - NR_SECTS_FOR_LOG; /* 0x9D41-0x800=0x9541 */

 static int pos = 0;
 if (!pos) { /* first time invoking this routine */

#ifdef SET_LOG_SECT_SMAP_AT_STARTUP
  /*
   * set sector-map so that other files cannot use the log sectors
   */

  int bits_per_sect = SECTOR_SIZE * 8; /* 4096 */

  int smap_blk0_nr = 1 + 1 + sb->nr_imap_sects; /* 3 */
  int sect_nr  = smap_blk0_nr + nr_log_blk0_nr / bits_per_sect; /* 3+9=12 */
  int byte_off = (nr_log_blk0_nr % bits_per_sect) / 8; /* 168 */
  int bit_off  = (nr_log_blk0_nr % bits_per_sect) % 8; /* 1 */
  int sect_cnt = NR_SECTS_FOR_LOG / bits_per_sect + 2; /* 1 */
  int bits_left= NR_SECTS_FOR_LOG; /* 2048 */

  int i;
  for (i = 0; i < sect_cnt; i++) {
   DISKLOG_RD_SECT(device, sect_nr + i); /* DISKLOG_RD_SECT(?, 12) */

   for (; byte_off < SECTOR_SIZE && bits_left > 0; byte_off++) {
    for (; bit_off < 8; bit_off++) { /* repeat till enough bits are set */
     assert(((logdiskbuf[byte_off] >> bit_off) & 1) == 0);
     logdiskbuf[byte_off] |= (1 << bit_off);
     if (--bits_left  == 0)
      break;
    }
    bit_off = 0;
   }
   byte_off = 0;
   bit_off = 0;

   DISKLOG_WR_SECT(device, sect_nr + i);

   if (bits_left == 0)
    break;
  }
  assert(bits_left == 0);
#endif /* SET_LOG_SECT_SMAP_AT_STARTUP */

  pos = 0x40;

#ifdef MEMSET_LOG_SECTS
  /* write padding stuff to log sectors */
  int chunk = min(MAX_IO_BYTES, LOGDISKBUF_SIZE >> SECTOR_SIZE_SHIFT);
  assert(chunk == 256);
  int sects_left = NR_SECTS_FOR_LOG;
  for (i = nr_log_blk0_nr;
       i < nr_log_blk0_nr + NR_SECTS_FOR_LOG;
       i += chunk) {
   memset(logdiskbuf, 0x20, chunk*SECTOR_SIZE);
   rw_sector(DEV_WRITE,
      device,
      i * SECTOR_SIZE,
      chunk * SECTOR_SIZE,
      getpid(),
      logdiskbuf);
   sects_left -= chunk;
  }
  if (sects_left != 0)
   panic("sects_left should be 0, current: %d.", sects_left);
#endif /* MEMSET_LOG_SECTS */
 }

 char * p = logstr;
 int bytes_left = strlen(logstr);

 int sect_nr = nr_log_blk0_nr + (pos >> SECTOR_SIZE_SHIFT);

 while (bytes_left) {
  DISKLOG_RD_SECT(device, sect_nr);

  int off = pos % SECTOR_SIZE;
  int bytes = min(bytes_left, SECTOR_SIZE - off);

  memcpy(&logdiskbuf[off], p, bytes);
  off += bytes;
  bytes_left -= bytes;

  DISKLOG_WR_SECT(device, sect_nr);
  sect_nr++;
  pos += bytes;
  p += bytes;
 }

 struct time t;
 MESSAGE msg;
 msg.type = GET_RTC_TIME;
 msg.BUF= &t;
 send_recv(BOTH, TASK_SYS, &msg);

 /* write `pos' and time into the log file header */
 DISKLOG_RD_SECT(device, nr_log_blk0_nr);

 sprintf((char*)logdiskbuf, "%8d\n", pos);
 memset(logdiskbuf+9, ' ', 22);
 logdiskbuf[31] = '\n';

 sprintf((char*)logdiskbuf+32, "<%d-%02d-%02d %02d:%02d:%02d>\n",
  t.year,
  t.month,
  t.day,
  t.hour,
  t.minute,
  t.second);
 memset(logdiskbuf+32+22, ' ', 9);
 logdiskbuf[63] = '\n';

 DISKLOG_WR_SECT(device, nr_log_blk0_nr);
 memset(logdiskbuf+64, logdiskbuf[32+19], 512-64);
 DISKLOG_WR_SECT(device, nr_log_blk0_nr + NR_SECTS_FOR_LOG - 1);

 return pos;
}

/* /\***************************************************************************** */
/*  *                                inode2filename */
/*  *****************************************************************************\/ */
/* /\** */
/*  * Get filename via i-node */
/*  *  */
/*  * @param inode  */
/*  * @param filename  */
/*  *  */
/*  * @return  */
/*  *****************************************************************************\/ */
/* PRIVATE char * inode2filename(int inode, char * filename) */
/* { */
/*  int i, j; */

/*  struct inode * dir_inode = root_inode; */

/*  /\** */
/*   * Search the dir for the file. */
/*   *\/ */
/*  int dir_blk0_nr = dir_inode->i_start_sect; */
/*  int nr_dir_blks = (dir_inode->i_size + SECTOR_SIZE - 1) / SECTOR_SIZE; */
/*  int nr_dir_entries = */
/*    dir_inode->i_size / DIR_ENTRY_SIZE; /\** */
/*             * including unused slots */
/*             * (the file has been deleted */
/*             * but the slot is still there) */
/*             *\/ */
/*  int m = 0; */
/*  struct dir_entry * pde; */
/*  for (i = 0; i < nr_dir_blks; i++) { */
/*   DISKLOG_RD_SECT(dir_inode->i_dev, dir_blk0_nr + i); */
/*   pde = (struct dir_entry *)logdiskbuf; */
/*   for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++,pde++) { */
/*    if (pde->inode_nr == inode) { */
/*     memcpy(filename, pde->name, MAX_FILENAME_LEN); */
/*     filename[MAX_FILENAME_LEN] = 0; */

/*     if (filename[0] == '.') */
/*      filename[0] = '/'; */

/*     return filename; */
/*    } */
/*    if (++m > nr_dir_entries) */
/*     break; */
/*   } */
/*   if (m > nr_dir_entries) /\* all entries have been iterated *\/ */
/*    break; */
/*  } */

/*  /\* file not found *\/ */
/*  return 0; */

/* } */

#define LOG_PROCS   1 /* YES */

#define LOG_FD_TABLE   1
#define LOG_INODE_TABLE   1
#define LOG_SMAP   1
#define LOG_IMAP   1
#define LOG_INODE_ARRAY   1
#define LOG_ROOT_DIR   1

#define LOG_MSG_SRC2DST   1 /* YES */
#define LOG_ARROW_PARENT_CHILD  1 /* YES */

#define LOG_ARROW_PROC_FD  1
#define LOG_ARROW_FD_INODE  1
#define LOG_ARROW_INODE_INODEARRAY 1

#if (LOG_SMAP == 1 || LOG_IMAP == 1 || LOG_INODE_ARRAY || LOG_ROOT_DIR == 1)
static char _buf[SECTOR_SIZE];
#endif



/*****************************************************************************
 *                                dump_fd_graph
 *****************************************************************************/
/**
 * Output a dot graph file.
 *
 *****************************************************************************/
PUBLIC void dump_fd_graph(const char * fmt, ...)
{
 int i;
 int  logbufpos = 0;
 char title[STR_DEFAULT_LEN];

 va_list arg = (va_list)((char*)(&fmt) + 4); /**
           * 4: size of `fmt' in
           *    the stack
           */
 i = vsprintf(title, fmt, arg);
 assert(strlen(title) == i);

 printl("dump_fd_graph: %s\n", title);

 struct proc* p_proc;

 int callerpid = getpid();

 /* assert(getpid() == TASK_MM); */

 printl("<|");

 disable_int();

 int tcks[NR_TASKS + NR_PROCS];
 int prio[NR_TASKS + NR_PROCS];
 p_proc = proc_table;
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p_proc++) {
  if (p_proc->p_flags == FREE_SLOT)
   continue;
  if ((i == TASK_TTY) ||
      (i == TASK_SYS) ||
      (i == TASK_HD)  ||
      /* (i == TASK_FS)  || */
      (i == callerpid))
   continue;

  tcks[i] = p_proc->ticks;
  prio[i] = p_proc->priority;
  p_proc->ticks = 0;
  p_proc->priority = 0;
 }

 static int graph_idx = 0;

#if (LOG_ROOT_DIR == 1)
 char filename[MAX_FILENAME_LEN+1];
#endif

 char * proc_flags[32];
 for (i = 0; i < sizeof(proc_flags) / sizeof(proc_flags[0]); i++) {
  proc_flags[i] = "__NOT_DEFINED__";
 }
 proc_flags[0]         = "RUNNING";
 proc_flags[SENDING]   = "SENDING";
 proc_flags[RECEIVING] = "RECEIVING";
 proc_flags[WAITING]   = "WAITING";
 proc_flags[HANGING]   = "HANGING";
 proc_flags[FREE_SLOT] = "FREE_SLOT";

 proc_flags[SENDING+WAITING]   = "SENDING, WAITING";
 proc_flags[RECEIVING+WAITING] = "RECEIVING, WAITING";
 proc_flags[RECEIVING+HANGING] = "RECEIVING, HANGING";

 /* int inode_list[64]; */
 /* int il_idx = 0; */

#if (LOG_PROCS == 1 || LOG_ARROW_PARENT_CHILD == 1)
 struct proc_parent_map {
  int pid;
  int ppid;
 } ppm[256];
 int ppm_idx = 0;
#endif

#if (LOG_PROCS == 1 || LOG_ARROW_PROC_FD == 1)
 struct proc_fdesc_map {
  int pid; /* PID */
  int filp; /* idx of proc_table[pid].filp[] */
  int desc; /* idx of f_desc_table[] */
 } pfm[256];
 int pfm_idx = 0;
#endif

#if (LOG_FD_TABLE == 1 || LOG_ARROW_FD_INODE == 1)
 struct fdesc_inode_map {
  int desc; /* idx of f_desc_table[] */
  int inode; /* idx of inode_table[] */
 } fim[256];
 int fim_idx = 0;
#endif

 struct msg_src_dst {
  int src;
  int dst;
  int dir;
 } msd[256];
 int msd_idx = 0;
 printl("|_|");

 /* head */
 logbufpos += sprintf(logbuf + logbufpos,
        "digraph filedesc%02d {\n", graph_idx++);
 logbufpos += sprintf(logbuf + logbufpos, "\tgraph [\n");
 logbufpos += sprintf(logbuf + logbufpos, "  rankdir = \"LR\"\n");
 logbufpos += sprintf(logbuf + logbufpos, " ];\n");
 logbufpos += sprintf(logbuf + logbufpos, " node [\n");
 logbufpos += sprintf(logbuf + logbufpos, "  fontsize = \"16\"\n");
 logbufpos += sprintf(logbuf + logbufpos, "  shape = \"ellipse\"\n");
 logbufpos += sprintf(logbuf + logbufpos, " ];\n");
 logbufpos += sprintf(logbuf + logbufpos, " edge [\n");
 logbufpos += sprintf(logbuf + logbufpos, " ];\n");

#if (LOG_PROCS == 1)
 int k;
 p_proc = proc_table;
 logbufpos += sprintf(logbuf + logbufpos, "\n\tsubgraph cluster_0 {\n");
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p_proc++) {
  /* skip unused proc_table entries */
  if (p_proc->p_flags == FREE_SLOT)
   continue;

  /* /\* skip procs which open no files *\/ */
  /* for (k = 0; k < NR_FILES; k++) { */
  /*  if (p_proc->filp[k] != 0) */
  /*   break; */
  /* } */
  /* if (k == NR_FILES) */
  /*  continue; */

  if (p_proc->p_parent != NO_TASK) {
   ppm[ppm_idx].pid = i;
   ppm[ppm_idx].ppid = p_proc->p_parent;
   ppm_idx++;
  }

  if (p_proc->p_flags & RECEIVING) {
   msd[msd_idx].src = p_proc->p_recvfrom;
   msd[msd_idx].dst = proc2pid(p_proc);
   msd[msd_idx].dir = RECEIVING;
   msd_idx++;
  }
  if (p_proc->p_flags & SENDING) {
   msd[msd_idx].src = proc2pid(p_proc);
   msd[msd_idx].dst = p_proc->p_sendto;
   msd[msd_idx].dir = SENDING;
   msd_idx++;
  }

  logbufpos += sprintf(logbuf + logbufpos,
         "\t\t\"proc%d\" [\n"
         "\t\t\tlabel = \"<f0>%s (%d) "
         "|<f1> p_flags:%d(%s)"
         "|<f2> p_parent:%d%s"
         "|<f3> eip:0x%x",
         i,
         p_proc->name,
         i,
         p_proc->p_flags,
         proc_flags[p_proc->p_flags],
         p_proc->p_parent,
         p_proc->p_parent == NO_TASK ? "(NO_TASK)" : "",
         p_proc->regs.eip);

  int fnr = 3;
  for (k = 0; k < NR_FILES; k++) {
   if (p_proc->filp[k] == 0)
    continue;

   int fdesc_tbl_idx = p_proc->filp[k] - f_desc_table;
   logbufpos += sprintf(logbuf + logbufpos, "\t|<f%d> filp[%d]: %d",
          fnr,
          k,
          fdesc_tbl_idx);
   pfm[pfm_idx].pid = i;
   pfm[pfm_idx].filp = fnr;
   pfm[pfm_idx].desc = fdesc_tbl_idx;
   fnr++;
   pfm_idx++;
  }

  logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
  logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
  logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");
 }
 i = ANY;
 logbufpos += sprintf(logbuf + logbufpos, "\t\t\"proc%d\" [\n", i);
 logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0>ANY |<f1> ");
 logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");

 logbufpos += sprintf(logbuf + logbufpos, "\t\tlabel = \"procs\";\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t}\n");
#endif

 printl("0");

#if (LOG_FD_TABLE == 1)
 logbufpos += sprintf(logbuf + logbufpos, "\n\tsubgraph cluster_1 {\n");
 for (i = 0; i < NR_FILE_DESC; i++) {
  if (f_desc_table[i].fd_inode == 0)
   continue;

  int inode_tbl_idx = f_desc_table[i].fd_inode - inode_table;
  logbufpos += sprintf(logbuf + logbufpos, "\t\t\"filedesc%d\" [\n", i);
  logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0>filedesc %d"
         "|<f1> fd_mode:%d"
         "|<f2> fd_pos:%d"
         "|<f3> fd_cnt:%d"
         "|<f4> fd_inode:%d",
         i,
         f_desc_table[i].fd_mode,
         f_desc_table[i].fd_pos,
         f_desc_table[i].fd_cnt,
         inode_tbl_idx);
  fim[fim_idx].desc = i;
  fim[fim_idx].inode = inode_tbl_idx;
  fim_idx++;

  logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
  logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
  logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");
 }
 logbufpos += sprintf(logbuf + logbufpos, "\t\tlabel = \"filedescs\";\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t}\n");
#endif

 printl("1");

#if (LOG_INODE_TABLE == 1)
 logbufpos += sprintf(logbuf + logbufpos, "\n\tsubgraph cluster_2 {\n");
 for (i = 0; i < NR_INODE; i++) {
  if (inode_table[i].i_cnt == 0)
   continue;

  logbufpos += sprintf(logbuf + logbufpos, "\t\t\"inode%d\" [\n", i);
  logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0>inode %d"
         "|<f1> i_mode:0x%x"
         "|<f2> i_size:0x%x"
         "|<f3> i_start_sect:0x%x"
         "|<f4> i_nr_sects:0x%x"
         "|<f5> i_dev:0x%x"
         "|<f6> i_cnt:%d"
         "|<f7> i_num:%d",
         i,
         inode_table[i].i_mode,
         inode_table[i].i_size,
         inode_table[i].i_start_sect,
         inode_table[i].i_nr_sects,
         inode_table[i].i_dev,
         inode_table[i].i_cnt,
         inode_table[i].i_num);

  logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
  logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
  logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");
 }
 logbufpos += sprintf(logbuf + logbufpos, "\t\tlabel = \"inodes\";\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t}\n");
#endif

 printl("2");

 enable_int();

#if (LOG_SMAP == 1)
 logbufpos += sprintf(logbuf + logbufpos, "\n\tsubgraph cluster_3 {\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tstyle=filled;\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tcolor=lightgrey;\n");
 int smap_flag = 0;
 int bit_start = 0;
 /* i:     sector index */
 int j; /* byte index */
 /* k:     bit index */
 struct super_block * sb = get_super_block(root_inode->i_dev);
 int smap_blk0_nr = 1 + 1 + sb->nr_imap_sects;
 for (i = 0; i < sb->nr_smap_sects; i++) { /* smap_blk0_nr + i : current sect nr. */
  DISKLOG_RD_SECT(root_inode->i_dev, smap_blk0_nr + i);
  memcpy(_buf, logdiskbuf, SECTOR_SIZE);
  for (j = 0; j < SECTOR_SIZE; j++) {
   for (k = 0; k < 8; k++) {
    if (!smap_flag) {
     if ((_buf[j] >> k ) & 1) {
      smap_flag = 1;
      bit_start = (i * SECTOR_SIZE + j) * 8 + k;
     }
     else {
      continue;
     }
    }
    else {
     if ((_buf[j] >> k ) & 1) {
      continue;
     }
     else {
      smap_flag = 0;
      int bit_end = (i * SECTOR_SIZE + j) * 8 + k - 1;
      logbufpos += sprintf(logbuf + logbufpos, "\t\t\"sector %xh\" [\n", bit_start);
      logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0>sect %xh-%xh",
             bit_start,
             bit_end);
      logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
      logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
      logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");
     }
    }
   }
  }
 }
 logbufpos += sprintf(logbuf + logbufpos, "\t\tlabel = \"sector map (dev size: %xh)\";\n", sb->nr_sects);
 logbufpos += sprintf(logbuf + logbufpos, "\t}\n");
#endif

 printl("3");

#if (LOG_IMAP == 1)
 logbufpos += sprintf(logbuf + logbufpos, "\n\tsubgraph cluster_4 {\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tstyle=filled;\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tcolor=lightgrey;\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t\t\"imap\" [\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0>bits");
 /* i:     sector index */
 /* j:     byte index */
 /* k:     bit index */
 int imap_blk0_nr = 1 + 1;
 for (i = 0; i < sb->nr_imap_sects; i++) { /* smap_blk0_nr + i : current sect nr. */
  DISKLOG_RD_SECT(root_inode->i_dev, imap_blk0_nr + i);
  memcpy(_buf, logdiskbuf, SECTOR_SIZE);
  for (j = 0; j < SECTOR_SIZE; j++) {
   for (k = 0; k < 8; k++) {
    if ((_buf[j] >> k ) & 1) {
     int bit_nr = (i * SECTOR_SIZE + j) * 8 + k;
     logbufpos += sprintf(logbuf + logbufpos, "| %xh ", bit_nr);
    }
   }
  }
 }
 logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t\tlabel = \"inode map\";\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t}\n");
#endif

 printl("4");

#if (LOG_INODE_ARRAY == 1)
 logbufpos += sprintf(logbuf + logbufpos, "\n\tsubgraph cluster_5 {\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tstyle=filled;\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tcolor=lightgrey;\n");
 sb = get_super_block(root_inode->i_dev);
 int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects;
 DISKLOG_RD_SECT(root_inode->i_dev, blk_nr);
 memcpy(_buf, logdiskbuf, SECTOR_SIZE);

 char * p = _buf;
 for (i = 0; i < SECTOR_SIZE / sizeof(struct inode); i++,p+=INODE_SIZE) {
  struct inode * pinode = (struct inode*)p;
  if (pinode->i_start_sect == 0)
   continue;
  int start_sect;
  int end_sect;
  if (pinode->i_mode) {
   if (pinode->i_start_sect < sb->n_1st_sect) {
    panic("should not happen: %x < %x.",
          pinode->i_start_sect,
          sb->n_1st_sect);
   }
   start_sect =  pinode->i_start_sect - sb->n_1st_sect + 1;
   end_sect = start_sect + pinode->i_nr_sects - 1;
   logbufpos += sprintf(logbuf + logbufpos, "\t\t\"inodearray%d\" [\n", i+1);
   logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0> %d"
          "|<f2> i_size:0x%x"
          "|<f3> sect: %xh-%xh",
          i+1,
          pinode->i_size,
          start_sect,
          end_sect);

   logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
   logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
   logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");
  }
  else {
   start_sect = MAJOR(pinode->i_start_sect);
   end_sect = MINOR(pinode->i_start_sect);
   logbufpos += sprintf(logbuf + logbufpos, "\t\t\"inodearray%d\" [\n", i+1);
   logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0> %d"
          "|<f2> i_size:0x%x"
          "|<f3> dev nr: (%xh,%xh)",
          i+1,
          pinode->i_size,
          start_sect,
          end_sect);

   logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
   logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
   logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");
  }
 }
 logbufpos += sprintf(logbuf + logbufpos, "\t\tlabel = \"inode array\";\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t}\n");
#endif

 printl("5");

#if (LOG_ROOT_DIR == 1)
 logbufpos += sprintf(logbuf + logbufpos, "\n\tsubgraph cluster_6 {\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tstyle=filled;\n");
 logbufpos += sprintf(logbuf + logbufpos, "\n\t\tcolor=lightgrey;\n");
 sb = get_super_block(root_inode->i_dev);
 int dir_blk0_nr = root_inode->i_start_sect;
 int nr_dir_blks = (root_inode->i_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
 int nr_dir_entries =
   root_inode->i_size / DIR_ENTRY_SIZE; /**
            * including unused slots
            * (the file has been deleted
            * but the slot is still there)
            */
 int m = 0;
 struct dir_entry * pde;
 for (i = 0; i < nr_dir_blks; i++) {
  DISKLOG_RD_SECT(root_inode->i_dev, dir_blk0_nr + i);
  memcpy(_buf, logdiskbuf, SECTOR_SIZE);
  pde = (struct dir_entry *)_buf;
  for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++,pde++) {
   if (pde->inode_nr) {
    memcpy(filename, pde->name, MAX_FILENAME_LEN);
    if (filename[0] == '.')
     filename[0] = '/';
    logbufpos += sprintf(logbuf + logbufpos, "\t\t\"rootdirent%d\" [\n", pde->inode_nr);
    logbufpos += sprintf(logbuf + logbufpos, "\t\t\tlabel = \"<f0> %d"
           "|<f2> %s",
           pde->inode_nr,
           filename);
    logbufpos += sprintf(logbuf + logbufpos, "\t\"\n");
    logbufpos += sprintf(logbuf + logbufpos, "\t\t\tshape = \"record\"\n");
    logbufpos += sprintf(logbuf + logbufpos, "\t\t];\n");

    logbufpos += sprintf(logbuf + logbufpos, "\t"
           "\"inodearray%d\":f0"
           " -> "
           "\"rootdirent%d\":f0"
           ";\n",
           pde->inode_nr, pde->inode_nr);
   }
  }
  if (m > nr_dir_entries) /* all entries have been iterated */
   break;
 }

 logbufpos += sprintf(logbuf + logbufpos, "\t\tlabel = \"root dir\";\n");
 logbufpos += sprintf(logbuf + logbufpos, "\t}\n");
#endif

 printl("6");

#if (LOG_MSG_SRC2DST == 1)
 for (i = 0; i < msd_idx; i++) {
  if (msd[i].dir == RECEIVING)
   logbufpos += sprintf(logbuf + logbufpos, "\t\"proc%d\":f0 -> \"proc%d\":f0 "
          "[arrowhead=\"crow\", color=\"green\", label=\"%d\"];\n",
          msd[i].dst,
          msd[i].src,
          i);
  else if (msd[i].dir == SENDING)
   logbufpos += sprintf(logbuf + logbufpos, "\t\"proc%d\":f0 -> \"proc%d\":f0 "
          "[arrowhead=\"vee\", color=\"blue\", label=\"%d\"];\n",
          msd[i].src,
          msd[i].dst,
          i);
  else
   assert(0);
 }
#endif

#if (LOG_ARROW_PARENT_CHILD == 1)
 for (i = 0; i < ppm_idx; i++) {
  logbufpos += sprintf(logbuf + logbufpos, "\t\"proc%d\":f0 -> \"proc%d\":f0 "
         "[arrowhead=\"dot\", color=\"ivory3\"];\n",
         ppm[i].ppid,
         ppm[i].pid);
 }
#endif

#if (LOG_ARROW_PROC_FD == 1)
 for (i = 0; i < pfm_idx; i++) {
  logbufpos += sprintf(logbuf + logbufpos, "\t\"proc%d\":f%d -> \"filedesc%d\":f3;\n",
         pfm[i].pid,
         pfm[i].filp,
         pfm[i].desc);
 }
#endif

#if (LOG_ARROW_FD_INODE == 1)
 for (i = 0; i < fim_idx; i++) {
  logbufpos += sprintf(logbuf + logbufpos, "\t\"filedesc%d\":f4 -> \"inode%d\":f6;\n",
         fim[i].desc,
         fim[i].inode);
 }
#endif

#if (LOG_ARROW_INODE_INODEARRAY == 1)
 for (i = 0; i < NR_INODE; i++) {
  if (inode_table[i].i_cnt != 0)
   logbufpos += sprintf(logbuf + logbufpos, "\t\"inode%d\":f7 -> \"inodearray%d\":f0;\n",
          i,
          inode_table[i].i_num);
 }
#endif
 /* for (i = 0; i < il_idx; i++) { */
 /*  logbufpos += sprintf(logbuf + logbufpos, "\t\"inode%d\":f7 -> \"inodearray%d\":f0;\n", */
 /*         inode_list[i], inode_list[i]); */
 /* } */

 /* tail */
 logbufpos += sprintf(logbuf + logbufpos, "\tlabel = \"%s\";\n", title);
 logbufpos += sprintf(logbuf + logbufpos, "}\n");

 /* separator */
 logbufpos += sprintf(logbuf + logbufpos, "--separator--\n");

 assert(logbufpos < LOGBUF_SIZE);

 logbuf[logbufpos] = 0;
 char tmp[STR_DEFAULT_LEN/2];
 int bytes_left = logbufpos;
 int pos = 0;
 while (bytes_left) {
  int bytes = min(bytes_left, STR_DEFAULT_LEN/2 - 1);
  memcpy(tmp, logbuf + pos, bytes);
  tmp[bytes] = 0;
  disklog(tmp);
  pos += bytes;
  bytes_left -= bytes;
 }

 disable_int();
 p_proc = proc_table;
 for (i = 0; i < NR_TASKS + NR_PROCS; i++,p_proc++) {
  if (p_proc->p_flags == FREE_SLOT)
   continue;
  if ((i == TASK_TTY) ||
      (i == TASK_SYS) ||
      (i == TASK_HD)  ||
      /* (i == TASK_FS)  || */
      (i == getpid()))
   continue;
  p_proc->ticks = tcks[i];
  p_proc->priority = prio[i];
 }
 enable_int();

 printl("|>");

 /* int pos = logbufpos += sprintf(logbuf + logbufpos, "--separator--\n"); */
 /* printl("dump_fd_graph(%s)::logbufpos:%d\n", title, logbufpos); */
}

fs/open.c

/*************************************************************************//**
 *****************************************************************************
 * @file   fs/open.c
 * The file contains:
 *   - do_open()
 *   - do_close()
 *   - do_lseek()
 *   - create_file()
 * @author Forrest Yu
 * @date   2007
 *****************************************************************************
 *****************************************************************************/

#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 struct inode * create_file(char * path, int flags);
PRIVATE int alloc_imap_bit(int dev);
PRIVATE int alloc_smap_bit(int dev, int nr_sects_to_alloc);
PRIVATE struct inode * new_inode(int dev, int inode_nr, int start_sect);
PRIVATE void new_dir_entry(struct inode * dir_inode, int inode_nr, char * filename);

/*****************************************************************************
 *                                do_open
 *****************************************************************************/
/**
 * Open a file and return the file descriptor.
 *
 * @return File descriptor if successful, otherwise a negative error code.
 *****************************************************************************/
PUBLIC int do_open()
{
 int fd = -1;  /* return value */

 char pathname[MAX_PATH];

 /* get parameters from the message */
 int flags = fs_msg.FLAGS; /* access mode */
 int name_len = fs_msg.NAME_LEN; /* length of filename */
 int src = fs_msg.source; /* caller proc nr. */
 assert(name_len < MAX_PATH);
 phys_copy((void*)va2la(TASK_FS, pathname),
    (void*)va2la(src, fs_msg.PATHNAME),
    name_len);
 pathname[name_len] = 0;

 /* find a free slot in PROCESS::filp[] */
 int i;
 for (i = 0; i < NR_FILES; i++) {
  if (pcaller->filp[i] == 0) {
   fd = i;
   break;
  }
 }
 if ((fd < 0) || (fd >= NR_FILES))
  panic("filp[] is full (PID:%d)", proc2pid(pcaller));

 /* find a free slot in f_desc_table[] */
 for (i = 0; i < NR_FILE_DESC; i++)
  if (f_desc_table[i].fd_inode == 0)
   break;
 if (i >= NR_FILE_DESC)
  panic("f_desc_table[] is full (PID:%d)", proc2pid(pcaller));

 int inode_nr = search_file(pathname);

 struct inode * pin = 0;
 if (flags & O_CREAT) {
  if (inode_nr) {
   printl("{FS} file exists.\n");
   return -1;
  }
  else {
   pin = create_file(pathname, flags);
  }
 }
 else {
  assert(flags & O_RDWR);

  char filename[MAX_PATH];
  struct inode * dir_inode;
  if (strip_path(filename, pathname, &dir_inode) != 0)
   return -1;
  pin = get_inode(dir_inode->i_dev, inode_nr);
 }

 if (pin) {
  /* connects proc with file_descriptor */
  pcaller->filp[fd] = &f_desc_table[i];

  /* connects file_descriptor with inode */
  f_desc_table[i].fd_inode = pin;

  f_desc_table[i].fd_mode = flags;
  f_desc_table[i].fd_cnt = 1;
  f_desc_table[i].fd_pos = 0;

  int imode = pin->i_mode & I_TYPE_MASK;

  if (imode == I_CHAR_SPECIAL) {
   MESSAGE driver_msg;
   driver_msg.type = DEV_OPEN;
   int dev = pin->i_start_sect;
   driver_msg.DEVICE = MINOR(dev);
   assert(MAJOR(dev) == 4);
   assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
   send_recv(BOTH,
      dd_map[MAJOR(dev)].driver_nr,
      &driver_msg);
  }
  else if (imode == I_DIRECTORY) {
   assert(pin->i_num == ROOT_INODE);
  }
  else {
   assert(pin->i_mode == I_REGULAR);
  }
 }
 else {
  return -1;
 }

 return fd;
}

/*****************************************************************************
 *                                create_file
 *****************************************************************************/
/**
 * Create a file and return it's inode ptr.
 *
 * @param[in] path   The full path of the new file
 * @param[in] flags  Attribiutes of the new file
 *
 * @return           Ptr to i-node of the new file if successful, otherwise 0.
 *
 * @see open()
 * @see do_open()
 *
 * @todo return values of routines called, return values of self.
 *****************************************************************************/
PRIVATE struct inode * create_file(char * path, int flags)
{
 char filename[MAX_PATH];
 struct inode * dir_inode;
 if (strip_path(filename, path, &dir_inode) != 0)
  return 0;

 int inode_nr = alloc_imap_bit(dir_inode->i_dev);
 int free_sect_nr = alloc_smap_bit(dir_inode->i_dev,
       NR_DEFAULT_FILE_SECTS);
 struct inode *newino = new_inode(dir_inode->i_dev, inode_nr,
      free_sect_nr);

 new_dir_entry(dir_inode, newino->i_num, filename);

 return newino;
}

/*****************************************************************************
 *                                do_close
 *****************************************************************************/
/**
 * Handle the message CLOSE.
 *
 * @return Zero if success.
 *****************************************************************************/
PUBLIC int do_close()
{
 int fd = fs_msg.FD;
 put_inode(pcaller->filp[fd]->fd_inode);
 if (--pcaller->filp[fd]->fd_cnt == 0)
  pcaller->filp[fd]->fd_inode = 0;
 pcaller->filp[fd] = 0;

 return 0;
}

/*****************************************************************************
 *                                do_lseek
 *****************************************************************************/
/**
 * Handle the message LSEEK.
 *
 * @return The new offset in bytes from the beginning of the file if successful,
 *         otherwise a negative number.
 *****************************************************************************/
PUBLIC int do_lseek()
{
 int fd = fs_msg.FD;
 int off = fs_msg.OFFSET;
 int whence = fs_msg.WHENCE;

 int pos = pcaller->filp[fd]->fd_pos;
 int f_size = pcaller->filp[fd]->fd_inode->i_size;

 switch (whence) {
 case SEEK_SET:
  pos = off;
  break;
 case SEEK_CUR:
  pos += off;
  break;
 case SEEK_END:
  pos = f_size + off;
  break;
 default:
  return -1;
  break;
 }
 if ((pos > f_size) || (pos < 0)) {
  return -1;
 }
 pcaller->filp[fd]->fd_pos = pos;
 return pos;
}

/*****************************************************************************
 *                                alloc_imap_bit
 *****************************************************************************/
/**
 * Allocate a bit in inode-map.
 *
 * @param dev  In which device the inode-map is located.
 *
 * @return  I-node nr.
 *****************************************************************************/
PRIVATE int alloc_imap_bit(int dev)
{
 int inode_nr = 0;
 int i, j, k;

 int imap_blk0_nr = 1 + 1; /* 1 boot sector & 1 super block */
 struct super_block * sb = get_super_block(dev);

 for (i = 0; i < sb->nr_imap_sects; i++) {
  RD_SECT(dev, imap_blk0_nr + i);

  for (j = 0; j < SECTOR_SIZE; j++) {
   /* skip `11111111' bytes */
   if (fsbuf[j] == 0xFF)
    continue;
   /* skip `1' bits */
   for (k = 0; ((fsbuf[j] >> k) & 1) != 0; k++) {}
   /* i: sector index; j: byte index; k: bit index */
   inode_nr = (i * SECTOR_SIZE + j) * 8 + k;
   fsbuf[j] |= (1 << k);
   /* write the bit to imap */
   WR_SECT(dev, imap_blk0_nr + i);
   break;
  }

  return inode_nr;
 }

 /* no free bit in imap */
 panic("inode-map is probably full.\n");

 return 0;
}

/*****************************************************************************
 *                                alloc_smap_bit
 *****************************************************************************/
/**
 * Allocate a bit in sector-map.
 *
 * @param dev  In which device the sector-map is located.
 * @param nr_sects_to_alloc  How many sectors are allocated.
 *
 * @return  The 1st sector nr allocated.
 *****************************************************************************/
PRIVATE int alloc_smap_bit(int dev, int nr_sects_to_alloc)
{
 /* int nr_sects_to_alloc = NR_DEFAULT_FILE_SECTS; */

 int i; /* sector index */
 int j; /* byte index */
 int k; /* bit index */

 struct super_block * sb = get_super_block(dev);

 int smap_blk0_nr = 1 + 1 + sb->nr_imap_sects;
 int free_sect_nr = 0;

 for (i = 0; i < sb->nr_smap_sects; i++) { /* smap_blk0_nr + i :
           current sect nr. */
  RD_SECT(dev, smap_blk0_nr + i);

  /* byte offset in current sect */
  for (j = 0; j < SECTOR_SIZE && nr_sects_to_alloc > 0; j++) {
   k = 0;
   if (!free_sect_nr) {
    /* loop until a free bit is found */
    if (fsbuf[j] == 0xFF) continue;
    for (; ((fsbuf[j] >> k) & 1) != 0; k++) {}
    free_sect_nr = (i * SECTOR_SIZE + j) * 8 +
     k - 1 + sb->n_1st_sect;
   }

   for (; k < 8; k++) { /* repeat till enough bits are set */
    assert(((fsbuf[j] >> k) & 1) == 0);
    fsbuf[j] |= (1 << k);
    if (--nr_sects_to_alloc == 0)
     break;
   }
  }

  if (free_sect_nr) /* free bit found, write the bits to smap */
   WR_SECT(dev, smap_blk0_nr + i);

  if (nr_sects_to_alloc == 0)
   break;
 }

 assert(nr_sects_to_alloc == 0);

 return free_sect_nr;
}

/*****************************************************************************
 *                                new_inode
 *****************************************************************************/
/**
 * Generate a new i-node and write it to disk.
 *
 * @param dev  Home device of the i-node.
 * @param inode_nr  I-node nr.
 * @param start_sect  Start sector of the file pointed by the new i-node.
 *
 * @return  Ptr of the new i-node.
 *****************************************************************************/
PRIVATE struct inode * new_inode(int dev, int inode_nr, int start_sect)
{
 struct inode * new_inode = get_inode(dev, inode_nr);

 new_inode->i_mode = I_REGULAR;
 new_inode->i_size = 0;
 new_inode->i_start_sect = start_sect;
 new_inode->i_nr_sects = NR_DEFAULT_FILE_SECTS;

 new_inode->i_dev = dev;
 new_inode->i_cnt = 1;
 new_inode->i_num = inode_nr;

 /* write to the inode array */
 sync_inode(new_inode);

 return new_inode;
}

/*****************************************************************************
 *                                new_dir_entry
 *****************************************************************************/
/**
 * Write a new entry into the directory.
 * 
 * @param dir_inode  I-node of the directory.
 * @param inode_nr   I-node nr of the new file.
 * @param filename   Filename of the new file.
 *****************************************************************************/
PRIVATE void new_dir_entry(struct inode *dir_inode,int inode_nr,char *filename)
{
 /* write the dir_entry */
 int dir_blk0_nr = dir_inode->i_start_sect;
 int nr_dir_blks = (dir_inode->i_size + SECTOR_SIZE) / SECTOR_SIZE;
 int nr_dir_entries =
  dir_inode->i_size / DIR_ENTRY_SIZE; /**
           * including unused slots
           * (the file has been
           * deleted but the slot
           * is still there)
           */
 int m = 0;
 struct dir_entry * pde;
 struct dir_entry * new_de = 0;

 int i, j;
 for (i = 0; i < nr_dir_blks; i++) {
  RD_SECT(dir_inode->i_dev, dir_blk0_nr + i);

  pde = (struct dir_entry *)fsbuf;
  for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++,pde++) {
   if (++m > nr_dir_entries)
    break;

   if (pde->inode_nr == 0) { /* it's a free slot */
    new_de = pde;
    break;
   }
  }
  if (m > nr_dir_entries ||/* all entries have been iterated or */
      new_de)              /* free slot is found */
   break;
 }
 if (!new_de) { /* reached the end of the dir */
  new_de = pde;
  dir_inode->i_size += DIR_ENTRY_SIZE;
 }
 new_de->inode_nr = inode_nr;
 strcpy(new_de->name, filename);

 /* write dir block -- ROOT dir block */
 WR_SECT(dir_inode->i_dev, dir_blk0_nr + i);

 /* update dir inode */
 sync_inode(dir_inode);
}

include/sys/config.h

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

#define MINOR_BOOT   MINOR_hd2a

/**
 * 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

/*
 * 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 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 STATUS  u.m3.m3i1
#define RETVAL  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/fs.h

/*************************************************************************//**
 *****************************************************************************
 * @file   include/sys/fs.h
 * @brief  Header file for File System.
 * @author Forrest Yu
 * @date   2008
 *****************************************************************************
 *****************************************************************************/

#ifndef _ORANGES_FS_H_
#define _ORANGES_FS_H_

/**
 * @struct dev_drv_map fs.h "include/sys/fs.h"
 * @brief  The Device_nr.\ - Driver_nr.\ MAP.
 */
struct dev_drv_map {
 int driver_nr; /**< The proc nr.\ of the device driver. */
};

/**
 * @def   MAGIC_V1
 * @brief Magic number of FS v1.0
 */
#define MAGIC_V1 0x111

/**
 * @struct super_block fs.h "include/fs.h"
 * @brief  The 2nd sector of the FS
 *
 * Remember to change SUPER_BLOCK_SIZE if the members are changed.
 */
struct super_block {
 u32 magic;    /**< Magic number */
 u32 nr_inodes;   /**< How many inodes */
 u32 nr_sects;   /**< How many sectors */
 u32 nr_imap_sects;   /**< How many inode-map sectors */
 u32 nr_smap_sects;   /**< How many sector-map sectors */
 u32 n_1st_sect;   /**< Number of the 1st data sector */
 u32 nr_inode_sects;   /**< How many inode sectors */
 u32 root_inode;       /**< Inode nr of root directory */
 u32 inode_size;       /**< INODE_SIZE */
 u32 inode_isize_off;  /**< Offset of `struct inode::i_size' */
 u32 inode_start_off;  /**< Offset of `struct inode::i_start_sect' */
 u32 dir_ent_size;     /**< DIR_ENTRY_SIZE */
 u32 dir_ent_inode_off;/**< Offset of `struct dir_entry::inode_nr' */
 u32 dir_ent_fname_off;/**< Offset of `struct dir_entry::name' */

 /*
  * the following item(s) are only present in memory
  */
 int sb_dev;  /**< the super block's home device */
};

/**
 * @def   SUPER_BLOCK_SIZE
 * @brief The size of super block \b in \b the \b device.
 *
 * Note that this is the size of the struct in the device, \b NOT in memory.
 * The size in memory is larger because of some more members.
 */
#define SUPER_BLOCK_SIZE 56

/**
 * @struct inode
 * @brief  i-node
 *
 * The \c start_sect and\c nr_sects locate the file in the device,
 * and the size show how many bytes is used.
 * If <tt> size < (nr_sects * SECTOR_SIZE) </tt>, the rest bytes
 * are wasted and reserved for later writing.
 *
 * \b NOTE: Remember to change INODE_SIZE if the members are changed
 */
struct inode {
 u32 i_mode;  /**< Accsess mode */
 u32 i_size;  /**< File size */
 u32 i_start_sect; /**< The first sector of the data */
 u32 i_nr_sects; /**< How many sectors the file occupies */
 u8 _unused[16]; /**< Stuff for alignment */

 /* the following items are only present in memory */
 int i_dev;
 int i_cnt;  /**< How many procs share this inode  */
 int i_num;  /**< inode nr.  */
};

/**
 * @def   INODE_SIZE
 * @brief The size of i-node stored \b in \b the \b device.
 *
 * Note that this is the size of the struct in the device, \b NOT in memory.
 * The size in memory is larger because of some more members.
 */
#define INODE_SIZE 32

/**
 * @def   MAX_FILENAME_LEN
 * @brief Max len of a filename
 * @see   dir_entry
 */
#define MAX_FILENAME_LEN 12

/**
 * @struct dir_entry
 * @brief  Directory Entry
 */
struct dir_entry {
 int inode_nr;  /**< inode nr. */
 char name[MAX_FILENAME_LEN]; /**< Filename */
};

/**
 * @def   DIR_ENTRY_SIZE
 * @brief The size of directory entry in the device.
 *
 * It is as same as the size in memory.
 */
#define DIR_ENTRY_SIZE sizeof(struct dir_entry)

/**
 * @struct file_desc
 * @brief  File Descriptor
 */
struct file_desc {
 int  fd_mode; /**< R or W */
 int  fd_pos;  /**< Current position for R/W. */
 int  fd_cnt;  /**< How many procs share this desc */
 struct inode* fd_inode; /**< Ptr to the i-node */
};


/**
 * Since all invocations of `rw_sector()' in FS look similar (most of the
 * params are the same), we use this macro to make code more readable.
 */
#define RD_SECT(dev,sect_nr) rw_sector(DEV_READ, \
           dev,    \
           (sect_nr) * SECTOR_SIZE,  \
           SECTOR_SIZE, /* read one sector */ \
           TASK_FS,    \
           fsbuf);
#define WR_SECT(dev,sect_nr) rw_sector(DEV_WRITE, \
           dev,    \
           (sect_nr) * SECTOR_SIZE,  \
           SECTOR_SIZE, /* write one sector */ \
           TASK_FS,    \
           fsbuf);


#endif /* _ORANGES_FS_H_ */

include/sys/global.h

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

/* EXTERN is defined as extern except in global.c */
#ifdef GLOBAL_VARIABLES_HERE
#undef EXTERN
#define EXTERN
#endif

EXTERN int ticks;

EXTERN int disp_pos;

EXTERN u8   gdt_ptr[6]; /* 0~15:Limit  16~47:Base */
EXTERN struct descriptor gdt[GDT_SIZE];
EXTERN u8   idt_ptr[6]; /* 0~15:Limit  16~47:Base */
EXTERN struct gate  idt[IDT_SIZE];

EXTERN u32 k_reenter;
EXTERN int current_console;

EXTERN int key_pressed; /**
         * used for clock_handler
         * to wake up TASK_TTY when
         * a key is pressed
         */

EXTERN struct tss tss;
EXTERN struct proc* p_proc_ready;

extern char  task_stack[];
extern struct proc proc_table[];
extern  struct task task_table[];
extern  struct task user_proc_table[];
extern irq_handler irq_table[];
extern TTY  tty_table[];
extern  CONSOLE  console_table[];

/* MM */
EXTERN MESSAGE   mm_msg;
extern u8 *   mmbuf;
extern const int  MMBUF_SIZE;
EXTERN int   memory_size;

/* FS */
EXTERN struct file_desc f_desc_table[NR_FILE_DESC];
EXTERN struct inode  inode_table[NR_INODE];
EXTERN struct super_block super_block[NR_SUPER_BLOCK];
extern u8 *   fsbuf;
extern const int  FSBUF_SIZE;
EXTERN MESSAGE   fs_msg;
EXTERN struct proc *  pcaller;
EXTERN struct inode *  root_inode;
extern struct dev_drv_map dd_map[];

/* for test only */
extern char *   logbuf;
extern const int  LOGBUF_SIZE;
extern char *   logdiskbuf;
extern const int  LOGDISKBUF_SIZE;

include/sys/proc.h

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


struct stackframe { /* proc_ptr points here    ↑ Low   */
 u32 gs;  /* ┓      │   */
 u32 fs;  /* ┃      │   */
 u32 es;  /* ┃      │   */
 u32 ds;  /* ┃      │   */
 u32 edi;  /* ┃      │   */
 u32 esi;  /* ┣ pushed by save()    │   */
 u32 ebp;  /* ┃      │   */
 u32 kernel_esp; /* <- 'popad' will ignore it   │   */
 u32 ebx;  /* ┃      ↑棧從高地址往低地址增長*/
 u32 edx;  /* ┃      │   */
 u32 ecx;  /* ┃      │   */
 u32 eax;  /* ┛      │   */
 u32 retaddr; /* return address for assembly code save() │   */
 u32 eip;  /*  ┓      │   */
 u32 cs;  /*  ┃      │   */
 u32 eflags;  /*  ┣ these are pushed by CPU during interrupt │   */
 u32 esp;  /*  ┃      │   */
 u32 ss;  /*  ┛      ┷High   */
};


struct proc {
 struct stackframe regs;    /* process registers saved in stack frame */

 u16 ldt_sel;               /* gdt selector giving ldt base and limit */
 struct descriptor ldts[LDT_SIZE]; /* local descs for code and data */

        int ticks;                 /* remained ticks */
        int priority;

 /* u32 pid;                   /\* process id passed in from MM *\/ */
 char name[16];     /* name of the process */

 int  p_flags;              /**
        * process flags.
        * A proc is runnable iff p_flags==0
        */

 MESSAGE * p_msg;
 int p_recvfrom;
 int p_sendto;

 int has_int_msg;           /**
        * nonzero if an INTERRUPT occurred when
        * the task is not ready to deal with it.
        */

 struct proc * q_sending;   /**
        * queue of procs sending messages to
        * this proc
        */
 struct proc * next_sending;/**
        * next proc in the sending
        * queue (q_sending)
        */

 int p_parent; /**< pid of parent process */

 int exit_status; /**< for parent */

 struct file_desc * filp[NR_FILES];
};

struct task {
 task_f initial_eip;
 int stacksize;
 char name[32];
};

#define proc2pid(x) (x - proc_table)

/* Number of tasks & processes */
#define NR_TASKS  5
#define NR_PROCS  32
#define NR_NATIVE_PROCS  4
#define FIRST_PROC  proc_table[0]
#define LAST_PROC  proc_table[NR_TASKS + NR_PROCS - 1]

/**
 * All forked proc will use memory above PROCS_BASE.
 *
 * @attention make sure PROCS_BASE is higher than any buffers, such as
 *            fsbuf, mmbuf, etc
 * @see global.c
 * @see global.h
 */
#define PROCS_BASE  0xA00000 /* 10 MB */
#define PROC_IMAGE_SIZE_DEFAULT 0x100000 /*  1 MB */
#define PROC_ORIGIN_STACK 0x400    /*  1 KB */

/* stacks of tasks */
#define STACK_SIZE_DEFAULT 0x4000 /* 16 KB */
#define STACK_SIZE_TTY  STACK_SIZE_DEFAULT
#define STACK_SIZE_SYS  STACK_SIZE_DEFAULT
#define STACK_SIZE_HD  STACK_SIZE_DEFAULT
#define STACK_SIZE_FS  STACK_SIZE_DEFAULT
#define STACK_SIZE_MM  STACK_SIZE_DEFAULT
#define STACK_SIZE_INIT  STACK_SIZE_DEFAULT
#define STACK_SIZE_TESTA STACK_SIZE_DEFAULT
#define STACK_SIZE_TESTB STACK_SIZE_DEFAULT
#define STACK_SIZE_TESTC STACK_SIZE_DEFAULT

#define STACK_SIZE_TOTAL (STACK_SIZE_TTY + \
    STACK_SIZE_SYS + \
    STACK_SIZE_HD + \
    STACK_SIZE_FS + \
    STACK_SIZE_MM + \
    STACK_SIZE_INIT + \
    STACK_SIZE_TESTA + \
    STACK_SIZE_TESTB + \
    STACK_SIZE_TESTC)

include/sys/protect.h

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

#ifndef _ORANGES_PROTECT_H_
#define _ORANGES_PROTECT_H_


/* 存儲段描述符/系統段描述符 */
struct descriptor  /* 共 8 個字節 */
{
 u16 limit_low;  /* Limit */
 u16 base_low;  /* Base */
 u8 base_mid;  /* Base */
 u8 attr1;   /* P(1) DPL(2) DT(1) TYPE(4) */
 u8 limit_high_attr2; /* G(1) D(1) 0(1) AVL(1) LimitHigh(4) */
 u8 base_high;  /* Base */
};

#define reassembly(high, high_shift, mid, mid_shift, low) \
 (((high) << (high_shift)) +    \
  ((mid)  << (mid_shift)) +    \
  (low))

/* 門描述符 */
struct gate
{
 u16 offset_low; /* Offset Low */
 u16 selector; /* Selector */
 u8 dcount;  /* 該字段只在調用門描述符中有效。
    如果在利用調用門調用子程序時引起特權級的轉換和堆棧的改變,需要將外層堆棧中的參數複製到內層堆棧。
    該雙字計數字段就是用於說明這種情況發生時,要複製的雙字參數的數量。 */
 u8 attr;  /* P(1) DPL(2) DT(1) TYPE(4) */
 u16 offset_high; /* Offset High */
};

struct tss {
 u32 backlink;
 u32 esp0;  /* stack pointer to use during interrupt */
 u32 ss0;  /*   "   segment  "  "    "        "     */
 u32 esp1;
 u32 ss1;
 u32 esp2;
 u32 ss2;
 u32 cr3;
 u32 eip;
 u32 flags;
 u32 eax;
 u32 ecx;
 u32 edx;
 u32 ebx;
 u32 esp;
 u32 ebp;
 u32 esi;
 u32 edi;
 u32 es;
 u32 cs;
 u32 ss;
 u32 ds;
 u32 fs;
 u32 gs;
 u32 ldt;
 u16 trap;
 u16 iobase; /* I/O位圖基址大於或等於TSS段界限,就表示沒有I/O許可位圖 */
 /*u8 iomap[2];*/
};

/* GDT */
/* 描述符索引 */
#define INDEX_DUMMY  0 /* ┓                          */
#define INDEX_FLAT_C  1 /* ┣ LOADER 裡面已經確定了的. */
#define INDEX_FLAT_RW  2 /* ┃                          */
#define INDEX_VIDEO  3 /* ┛                          */
#define INDEX_TSS  4
#define INDEX_LDT_FIRST  5
/* 選擇子 */
#define SELECTOR_DUMMY     0  /* ┓                          */
#define SELECTOR_FLAT_C  0x08  /* ┣ LOADER 裡面已經確定了的. */
#define SELECTOR_FLAT_RW 0x10  /* ┃                          */
#define SELECTOR_VIDEO  (0x18+3) /* ┛<-- RPL=3                 */
#define SELECTOR_TSS  0x20  /* TSS. 從外層跳到內存時 SS 和 ESP 的值從裡面獲得. */
#define SELECTOR_LDT_FIRST 0x28

#define SELECTOR_KERNEL_CS SELECTOR_FLAT_C
#define SELECTOR_KERNEL_DS SELECTOR_FLAT_RW
#define SELECTOR_KERNEL_GS SELECTOR_VIDEO

/* 每個任務有一個單獨的 LDT, 每個 LDT 中的描述符個數: */
#define LDT_SIZE  2
/* descriptor indices in LDT */
#define INDEX_LDT_C             0
#define INDEX_LDT_RW            1

/* 描述符類型值說明 */
#define DA_32   0x4000 /* 32 位段    */
#define DA_LIMIT_4K  0x8000 /* 段界限粒度為 4K 字節   */
#define LIMIT_4K_SHIFT    12
#define DA_DPL0   0x00 /* DPL = 0    */
#define DA_DPL1   0x20 /* DPL = 1    */
#define DA_DPL2   0x40 /* DPL = 2    */
#define DA_DPL3   0x60 /* DPL = 3    */
/* 存儲段描述符類型值說明 */
#define DA_DR   0x90 /* 存在的只讀數據段類型值  */
#define DA_DRW   0x92 /* 存在的可讀寫數據段屬性值  */
#define DA_DRWA   0x93 /* 存在的已訪問可讀寫數據段類型值 */
#define DA_C   0x98 /* 存在的只執行代碼段屬性值  */
#define DA_CR   0x9A /* 存在的可執行可讀代碼段屬性值  */
#define DA_CCO   0x9C /* 存在的只執行一致代碼段屬性值  */
#define DA_CCOR   0x9E /* 存在的可執行可讀一致代碼段屬性值 */
/* 系統段描述符類型值說明 */
#define DA_LDT   0x82 /* 局部描述符表段類型值   */
#define DA_TaskGate  0x85 /* 任務門類型值    */
#define DA_386TSS  0x89 /* 可用 386 任務狀態段類型值  */
#define DA_386CGate  0x8C /* 386 調用門類型值   */
#define DA_386IGate  0x8E /* 386 中斷門類型值   */
#define DA_386TGate  0x8F /* 386 陷阱門類型值   */

/* 選擇子類型值說明 */
/* 其中, SA_ : Selector Attribute */
#define SA_RPL_MASK 0xFFFC
#define SA_RPL0  0
#define SA_RPL1  1
#define SA_RPL2  2
#define SA_RPL3  3

#define SA_TI_MASK 0xFFFB
#define SA_TIG  0
#define SA_TIL  4

/* 中斷向量 */
#define INT_VECTOR_DIVIDE  0x0
#define INT_VECTOR_DEBUG  0x1
#define INT_VECTOR_NMI   0x2
#define INT_VECTOR_BREAKPOINT  0x3
#define INT_VECTOR_OVERFLOW  0x4
#define INT_VECTOR_BOUNDS  0x5
#define INT_VECTOR_INVAL_OP  0x6
#define INT_VECTOR_COPROC_NOT  0x7
#define INT_VECTOR_DOUBLE_FAULT  0x8
#define INT_VECTOR_COPROC_SEG  0x9
#define INT_VECTOR_INVAL_TSS  0xA
#define INT_VECTOR_SEG_NOT  0xB
#define INT_VECTOR_STACK_FAULT  0xC
#define INT_VECTOR_PROTECTION  0xD
#define INT_VECTOR_PAGE_FAULT  0xE
#define INT_VECTOR_COPROC_ERR  0x10

/* 中斷向量 */
#define INT_VECTOR_IRQ0   0x20
#define INT_VECTOR_IRQ8   0x28

/* 系統調用 */
#define INT_VECTOR_SYS_CALL             0x90

/* 宏 */
/* 線性地址 → 物理地址 */
//#define vir2phys(seg_base, vir) (u32)(((u32)seg_base) + (u32)(vir))

/* seg:off -> linear addr */
#define makelinear(seg,off) (u32)(((u32)(seg2linear(seg))) + (u32)(off))

#endif /* _ORANGES_PROTECT_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);

include/stdio.h

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

/* 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 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/syslog.c */
PUBLIC int syslog  (const char *fmt, ...);

include/type.h

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

#ifndef _ORANGES_TYPE_H_
#define _ORANGES_TYPE_H_

/* routine types */
#define PUBLIC  /* PUBLIC is the opposite of PRIVATE */
#define PRIVATE static /* PRIVATE x limits the scope of x */

typedef unsigned long long u64;
typedef unsigned int  u32;
typedef unsigned short  u16;
typedef unsigned char  u8;

typedef char *   va_list;

typedef void (*int_handler) ();
typedef void (*task_f) ();
typedef void (*irq_handler) (int irq);

typedef void* system_call;


/**
 * MESSAGE mechanism is borrowed from MINIX
 */
struct mess1 {
 int m1i1;
 int m1i2;
 int m1i3;
 int m1i4;
};
struct mess2 {
 void* m2p1;
 void* m2p2;
 void* m2p3;
 void* m2p4;
};
struct mess3 {
 int m3i1;
 int m3i2;
 int m3i3;
 int m3i4;
 u64 m3l1;
 u64 m3l2;
 void* m3p1;
 void* m3p2;
};
typedef struct {
 int source;
 int type;
 union {
  struct mess1 m1;
  struct mess2 m2;
  struct mess3 m3;
 } u;
} MESSAGE;

/* i have no idea of where to put this struct, so i put it here */
struct boot_params {
 int  mem_size; /* memory size */
 unsigned char * kernel_file; /* addr of kernel file */
};


#endif /* _ORANGES_TYPE_H_ */

kernel/global.c

/*************************************************************************//**
 *****************************************************************************
 * @file   global.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2005
 *****************************************************************************
 *****************************************************************************/

#define GLOBAL_VARIABLES_HERE

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


PUBLIC struct proc proc_table[NR_TASKS + NR_PROCS];

/* 注意下面的 TASK 的順序要與 const.h 中對應 */
PUBLIC struct task task_table[NR_TASKS] = {
 /* entry        stack size        task name */
 /* -----        ----------        --------- */
 {task_tty,      STACK_SIZE_TTY,   "TTY"       },
 {task_sys,      STACK_SIZE_SYS,   "SYS"       },
 {task_hd,       STACK_SIZE_HD,    "HD"        },
 {task_fs,       STACK_SIZE_FS,    "FS"        },
 {task_mm,       STACK_SIZE_MM,    "MM"        }};

PUBLIC struct task user_proc_table[NR_NATIVE_PROCS] = {
 /* entry    stack size     proc name */
 /* -----    ----------     --------- */
 {Init,   STACK_SIZE_INIT,  "INIT" },
 {TestA,  STACK_SIZE_TESTA, "TestA"},
 {TestB,  STACK_SIZE_TESTB, "TestB"},
 {TestC,  STACK_SIZE_TESTC, "TestC"}};
/* PUBLIC struct task user_proc_table[NR_PROCS] = { */
/*  {TestA, STACK_SIZE_TESTA, "TestA"}, */
/*  {TestB, STACK_SIZE_TESTB, "TestB"}, */
/*  {TestC, STACK_SIZE_TESTC, "TestC"}}; */

PUBLIC char  task_stack[STACK_SIZE_TOTAL];

PUBLIC TTY  tty_table[NR_CONSOLES];
PUBLIC CONSOLE  console_table[NR_CONSOLES];

PUBLIC irq_handler irq_table[NR_IRQ];

PUBLIC system_call sys_call_table[NR_SYS_CALL] = {sys_printx,
             sys_sendrec};

/* FS related below */
/*****************************************************************************/
/**
 * For dd_map[k],
 * `k' is the device nr.\ dd_map[k].driver_nr is the driver nr.
 *
 * Remeber to modify include/const.h if the order is changed.
 *****************************************************************************/
struct dev_drv_map dd_map[] = {
 /* driver nr.  major device nr.
    ----------  ---------------- */
 {INVALID_DRIVER}, /**< 0 : Unused */
 {INVALID_DRIVER}, /**< 1 : Reserved for floppy driver */
 {INVALID_DRIVER}, /**< 2 : Reserved for cdrom driver */
 {TASK_HD},  /**< 3 : Hard disk */
 {TASK_TTY},  /**< 4 : TTY */
 {INVALID_DRIVER} /**< 5 : Reserved for scsi disk driver */
};

/**
 * 6MB~7MB: buffer for FS
 */
PUBLIC u8 *  fsbuf  = (u8*)0x600000;
PUBLIC const int FSBUF_SIZE = 0x100000;


/**
 * 7MB~8MB: buffer for MM
 */
PUBLIC u8 *  mmbuf  = (u8*)0x700000;
PUBLIC const int MMBUF_SIZE = 0x100000;


/**
 * 8MB~10MB: buffer for log (debug)
 */
PUBLIC char *  logbuf  = (char*)0x800000;
PUBLIC const int LOGBUF_SIZE = 0x100000;
PUBLIC char *  logdiskbuf = (char*)0x900000;
PUBLIC const int LOGDISKBUF_SIZE = 0x100000;

kernel/hd.c

/*************************************************************************//**
 *****************************************************************************
 * @file   hd.c
 * @brief  Hard disk (winchester) driver.
 * The `device nr' in this file means minor device nr.
 * @author Forrest Y. Yu
 * @date   2005~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"
#include "hd.h"


PRIVATE void init_hd   ();
PRIVATE void hd_open   (int device);
PRIVATE void hd_close  (int device);
PRIVATE void hd_rdwt   (MESSAGE * p);
PRIVATE void hd_ioctl  (MESSAGE * p);
PRIVATE void hd_cmd_out  (struct hd_cmd* cmd);
PRIVATE void get_part_table  (int drive, int sect_nr, struct part_ent * entry);
PRIVATE void partition  (int device, int style);
PRIVATE void print_hdinfo  (struct hd_info * hdi);
PRIVATE int waitfor   (int mask, int val, int timeout);
PRIVATE void interrupt_wait  ();
PRIVATE void hd_identify  (int drive);
PRIVATE void print_identify_info (u16* hdinfo);

PRIVATE u8  hd_status;
PRIVATE u8  hdbuf[SECTOR_SIZE * 2];
PRIVATE struct hd_info hd_info[1];

#define DRV_OF_DEV(dev) (dev <= MAX_PRIM ? \
    dev / NR_PRIM_PER_DRIVE : \
    (dev - MINOR_hd1a) / NR_SUB_PER_DRIVE)

/*****************************************************************************
 *                                task_hd
 *****************************************************************************/
/**
 * Main loop of HD driver.
 *
 *****************************************************************************/
PUBLIC void task_hd()
{
 MESSAGE msg;

 init_hd();

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

  int src = msg.source;

  switch (msg.type) {
  case DEV_OPEN:
   hd_open(msg.DEVICE);
   break;

  case DEV_CLOSE:
   hd_close(msg.DEVICE);
   break;

  case DEV_READ:
  case DEV_WRITE:
   hd_rdwt(&msg);
   break;

  case DEV_IOCTL:
   hd_ioctl(&msg);
   break;

  default:
   dump_msg("HD driver::unknown msg", &msg);
   spin("FS::main_loop (invalid msg.type)");
   break;
  }

  send_recv(SEND, src, &msg);
 }
}

/*****************************************************************************
 *                                init_hd
 *****************************************************************************/
/**
 * <Ring 1> Check hard drive, set IRQ handler, enable IRQ and initialize data
 *          structures.
 *****************************************************************************/
PRIVATE void init_hd()
{
 int i;

 /* Get the number of drives from the BIOS data area */
 u8 * pNrDrives = (u8*)(0x475);
 printl("{HD} NrDrives:%d.\n", *pNrDrives);
 assert(*pNrDrives);

 put_irq_handler(AT_WINI_IRQ, hd_handler);
 enable_irq(CASCADE_IRQ);
 enable_irq(AT_WINI_IRQ);

 for (i = 0; i < (sizeof(hd_info) / sizeof(hd_info[0])); i++)
  memset(&hd_info[i], 0, sizeof(hd_info[0]));
 hd_info[0].open_cnt = 0;
}

/*****************************************************************************
 *                                hd_open
 *****************************************************************************/
/**
 * <Ring 1> This routine handles DEV_OPEN message. It identify the drive
 * of the given device and read the partition table of the drive if it
 * has not been read.
 *
 * @param device The device to be opened.
 *****************************************************************************/
PRIVATE void hd_open(int device)
{
 int drive = DRV_OF_DEV(device);
 assert(drive == 0); /* only one drive */

 hd_identify(drive);

 if (hd_info[drive].open_cnt++ == 0) {
  partition(drive * (NR_PART_PER_DRIVE + 1), P_PRIMARY);
  print_hdinfo(&hd_info[drive]);
 }
}

/*****************************************************************************
 *                                hd_close
 *****************************************************************************/
/**
 * <Ring 1> This routine handles DEV_CLOSE message.
 *
 * @param device The device to be opened.
 *****************************************************************************/
PRIVATE void hd_close(int device)
{
 int drive = DRV_OF_DEV(device);
 assert(drive == 0); /* only one drive */

 hd_info[drive].open_cnt--;
}


/*****************************************************************************
 *                                hd_rdwt
 *****************************************************************************/
/**
 * <Ring 1> This routine handles DEV_READ and DEV_WRITE message.
 *
 * @param p Message ptr.
 *****************************************************************************/
PRIVATE void hd_rdwt(MESSAGE * p)
{
 int drive = DRV_OF_DEV(p->DEVICE);

 u64 pos = p->POSITION;
 assert((pos >> SECTOR_SIZE_SHIFT) < (1 << 31));

 /**
  * We only allow to R/W from a SECTOR boundary:
  */
 assert((pos & 0x1FF) == 0);

 u32 sect_nr = (u32)(pos >> SECTOR_SIZE_SHIFT); /* pos / SECTOR_SIZE */
 int logidx = (p->DEVICE - MINOR_hd1a) % NR_SUB_PER_DRIVE;
 sect_nr += p->DEVICE < MAX_PRIM ?
  hd_info[drive].primary[p->DEVICE].base :
  hd_info[drive].logical[logidx].base;

 struct hd_cmd cmd;
 cmd.features = 0;
 cmd.count = (p->CNT + SECTOR_SIZE - 1) / SECTOR_SIZE;
 cmd.lba_low = sect_nr & 0xFF;
 cmd.lba_mid = (sect_nr >>  8) & 0xFF;
 cmd.lba_high = (sect_nr >> 16) & 0xFF;
 cmd.device = MAKE_DEVICE_REG(1, drive, (sect_nr >> 24) & 0xF);
 cmd.command = (p->type == DEV_READ) ? ATA_READ : ATA_WRITE;
 hd_cmd_out(&cmd);

 int bytes_left = p->CNT;
 void * la = (void*)va2la(p->PROC_NR, p->BUF);

 while (bytes_left) {
  int bytes = min(SECTOR_SIZE, bytes_left);
  if (p->type == DEV_READ) {
   interrupt_wait();
   port_read(REG_DATA, hdbuf, SECTOR_SIZE);
   phys_copy(la, (void*)va2la(TASK_HD, hdbuf), bytes);
  }
  else {
   if (!waitfor(STATUS_DRQ, STATUS_DRQ, HD_TIMEOUT))
    panic("hd writing error.");

   port_write(REG_DATA, la, bytes);
   interrupt_wait();
  }
  bytes_left -= SECTOR_SIZE;
  la += SECTOR_SIZE;
 }
}


/*****************************************************************************
 *                                hd_ioctl
 *****************************************************************************/
/**
 * <Ring 1> This routine handles the DEV_IOCTL message.
 *
 * @param p  Ptr to the MESSAGE.
 *****************************************************************************/
PRIVATE void hd_ioctl(MESSAGE * p)
{
 int device = p->DEVICE;
 int drive = DRV_OF_DEV(device);

 struct hd_info * hdi = &hd_info[drive];

 if (p->REQUEST == DIOCTL_GET_GEO) {
  void * dst = va2la(p->PROC_NR, p->BUF);
  void * src = va2la(TASK_HD,
       device < MAX_PRIM ?
       &hdi->primary[device] :
       &hdi->logical[(device - MINOR_hd1a) %
      NR_SUB_PER_DRIVE]);

  phys_copy(dst, src, sizeof(struct part_info));
 }
 else {
  assert(0);
 }
}

/*****************************************************************************
 *                                get_part_table
 *****************************************************************************/
/**
 * <Ring 1> Get a partition table of a drive.
 *
 * @param drive   Drive nr (0 for the 1st disk, 1 for the 2nd, ...)n
 * @param sect_nr The sector at which the partition table is located.
 * @param entry   Ptr to part_ent struct.
 *****************************************************************************/
PRIVATE void get_part_table(int drive, int sect_nr, struct part_ent * entry)
{
 struct hd_cmd cmd;
 cmd.features = 0;
 cmd.count = 1;
 cmd.lba_low = sect_nr & 0xFF;
 cmd.lba_mid = (sect_nr >>  8) & 0xFF;
 cmd.lba_high = (sect_nr >> 16) & 0xFF;
 cmd.device = MAKE_DEVICE_REG(1, /* LBA mode*/
       drive,
       (sect_nr >> 24) & 0xF);
 cmd.command = ATA_READ;
 hd_cmd_out(&cmd);
 interrupt_wait();

 port_read(REG_DATA, hdbuf, SECTOR_SIZE);
 memcpy(entry,
        hdbuf + PARTITION_TABLE_OFFSET,
        sizeof(struct part_ent) * NR_PART_PER_DRIVE);
}

/*****************************************************************************
 *                                partition
 *****************************************************************************/
/**
 * <Ring 1> This routine is called when a device is opened. It reads the
 * partition table(s) and fills the hd_info struct.
 *
 * @param device Device nr.
 * @param style  P_PRIMARY or P_EXTENDED.
 *****************************************************************************/
PRIVATE void partition(int device, int style)
{
 int i;
 int drive = DRV_OF_DEV(device);
 struct hd_info * hdi = &hd_info[drive];

 struct part_ent part_tbl[NR_SUB_PER_DRIVE];

 if (style == P_PRIMARY) {
  get_part_table(drive, drive, part_tbl);

  int nr_prim_parts = 0;
  for (i = 0; i < NR_PART_PER_DRIVE; i++) { /* 0~3 */
   if (part_tbl[i].sys_id == NO_PART)
    continue;

   nr_prim_parts++;
   int dev_nr = i + 1;    /* 1~4 */
   hdi->primary[dev_nr].base = part_tbl[i].start_sect;
   hdi->primary[dev_nr].size = part_tbl[i].nr_sects;

   if (part_tbl[i].sys_id == EXT_PART) /* extended */
    partition(device + dev_nr, P_EXTENDED);
  }
  assert(nr_prim_parts != 0);
 }
 else if (style == P_EXTENDED) {
  int j = device % NR_PRIM_PER_DRIVE; /* 1~4 */
  int ext_start_sect = hdi->primary[j].base;
  int s = ext_start_sect;
  int nr_1st_sub = (j - 1) * NR_SUB_PER_PART; /* 0/16/32/48 */

  for (i = 0; i < NR_SUB_PER_PART; i++) {
   int dev_nr = nr_1st_sub + i;/* 0~15/16~31/32~47/48~63 */

   get_part_table(drive, s, part_tbl);

   hdi->logical[dev_nr].base = s + part_tbl[0].start_sect;
   hdi->logical[dev_nr].size = part_tbl[0].nr_sects;

   s = ext_start_sect + part_tbl[1].start_sect;

   /* no more logical partitions
      in this extended partition */
   if (part_tbl[1].sys_id == NO_PART)
    break;
  }
 }
 else {
  assert(0);
 }
}

/*****************************************************************************
 *                                print_hdinfo
 *****************************************************************************/
/**
 * <Ring 1> Print disk info.
 *
 * @param hdi  Ptr to struct hd_info.
 *****************************************************************************/
PRIVATE void print_hdinfo(struct hd_info * hdi)
{
 int i;
 for (i = 0; i < NR_PART_PER_DRIVE + 1; i++) {
  printl("{HD} %sPART_%d: base %d(0x%x), size %d(0x%x) (in sector)\n",
         i == 0 ? " " : "     ",
         i,
         hdi->primary[i].base,
         hdi->primary[i].base,
         hdi->primary[i].size,
         hdi->primary[i].size);
 }
 for (i = 0; i < NR_SUB_PER_DRIVE; i++) {
  if (hdi->logical[i].size == 0)
   continue;
  printl("{HD}          "
         "%d: base %d(0x%x), size %d(0x%x) (in sector)\n",
         i,
         hdi->logical[i].base,
         hdi->logical[i].base,
         hdi->logical[i].size,
         hdi->logical[i].size);
 }
}

/*****************************************************************************
 *                                hd_identify
 *****************************************************************************/
/**
 * <Ring 1> Get the disk information.
 *
 * @param drive  Drive Nr.
 *****************************************************************************/
PRIVATE void hd_identify(int drive)
{
 struct hd_cmd cmd;
 cmd.device  = MAKE_DEVICE_REG(0, drive, 0);
 cmd.command = ATA_IDENTIFY;
 hd_cmd_out(&cmd);
 interrupt_wait();
 port_read(REG_DATA, hdbuf, SECTOR_SIZE);

 print_identify_info((u16*)hdbuf);

 u16* hdinfo = (u16*)hdbuf;

 hd_info[drive].primary[0].base = 0;
 /* Total Nr of User Addressable Sectors */
 hd_info[drive].primary[0].size = ((int)hdinfo[61] << 16) + hdinfo[60];
}

/*****************************************************************************
 *                            print_identify_info
 *****************************************************************************/
/**
 * <Ring 1> Print the hdinfo retrieved via ATA_IDENTIFY command.
 *
 * @param hdinfo  The buffer read from the disk i/o port.
 *****************************************************************************/
PRIVATE void print_identify_info(u16* hdinfo)
{
 int i, k;
 char s[64];

 struct iden_info_ascii {
  int idx;
  int len;
  char * desc;
 } iinfo[] = {{10, 20, "HD SN"}, /* Serial number in ASCII */
       {27, 40, "HD Model"} /* Model number in ASCII */ };

 for (k = 0; k < sizeof(iinfo)/sizeof(iinfo[0]); k++) {
  char * p = (char*)&hdinfo[iinfo[k].idx];
  for (i = 0; i < iinfo[k].len/2; i++) {
   s[i*2+1] = *p++;
   s[i*2] = *p++;
  }
  s[i*2] = 0;
  printl("{HD} %s: %s\n", iinfo[k].desc, s);
 }

 int capabilities = hdinfo[49];
 printl("{HD} LBA supported: %s\n",
        (capabilities & 0x0200) ? "Yes" : "No");

 int cmd_set_supported = hdinfo[83];
 printl("{HD} LBA48 supported: %s\n",
        (cmd_set_supported & 0x0400) ? "Yes" : "No");

 int sectors = ((int)hdinfo[61] << 16) + hdinfo[60];
 printl("{HD} HD size: %dMB\n", sectors * 512 / 1000000);
}

/*****************************************************************************
 *                                hd_cmd_out
 *****************************************************************************/
/**
 * <Ring 1> Output a command to HD controller.
 *
 * @param cmd  The command struct ptr.
 *****************************************************************************/
PRIVATE void hd_cmd_out(struct hd_cmd* cmd)
{
 /**
  * For all commands, the host must first check if BSY=1,
  * and should proceed no further unless and until BSY=0
  */
 if (!waitfor(STATUS_BSY, 0, HD_TIMEOUT))
  panic("hd error.");

 /* Activate the Interrupt Enable (nIEN) bit */
 out_byte(REG_DEV_CTRL, 0);
 /* Load required parameters in the Command Block Registers */
 out_byte(REG_FEATURES, cmd->features);
 out_byte(REG_NSECTOR,  cmd->count);
 out_byte(REG_LBA_LOW,  cmd->lba_low);
 out_byte(REG_LBA_MID,  cmd->lba_mid);
 out_byte(REG_LBA_HIGH, cmd->lba_high);
 out_byte(REG_DEVICE,   cmd->device);
 /* Write the command code to the Command Register */
 out_byte(REG_CMD,     cmd->command);
}

/*****************************************************************************
 *                                interrupt_wait
 *****************************************************************************/
/**
 * <Ring 1> Wait until a disk interrupt occurs.
 *
 *****************************************************************************/
PRIVATE void interrupt_wait()
{
 MESSAGE msg;
 send_recv(RECEIVE, INTERRUPT, &msg);
}

/*****************************************************************************
 *                                waitfor
 *****************************************************************************/
/**
 * <Ring 1> Wait for a certain status.
 *
 * @param mask    Status mask.
 * @param val     Required status.
 * @param timeout Timeout in milliseconds.
 *
 * @return One if sucess, zero if timeout.
 *****************************************************************************/
PRIVATE int waitfor(int mask, int val, int timeout)
{
 int t = get_ticks();

 while(((get_ticks() - t) * 1000 / HZ) < timeout)
  if ((in_byte(REG_STATUS) & mask) == val)
   return 1;

 return 0;
}

/*****************************************************************************
 *                                hd_handler
 *****************************************************************************/
/**
 * <Ring 0> Interrupt handler.
 *
 * @param irq  IRQ nr of the disk interrupt.
 *****************************************************************************/
PUBLIC void hd_handler(int irq)
{
 /*
  * Interrupts are cleared when the host
  *   - reads the Status Register,
  *   - issues a reset, or
  *   - writes to the Command Register.
  */
 hd_status = in_byte(REG_STATUS);

 inform_int(TASK_HD);
}

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


/*****************************************************************************
 *                                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");

 int pid = fork();
 if (pid != 0) { /* parent process */
  printf("parent is running, child pid:%d\n", pid);
  spin("parent");
 }
 else { /* child process */
  printf("child is running, pid:%d\n", getpid());
  spin("child");
 }
}


/*======================================================================*
                               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;
}

/*****************************************************************************
 *                                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;
}

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

kernel/protect.c

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

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


/* 本文件內函數聲明 */
PRIVATE void init_idt_desc(unsigned char vector, u8 desc_type, int_handler handler, unsigned char privilege);


/* 中斷處理函數 */
void divide_error();
void single_step_exception();
void nmi();
void breakpoint_exception();
void overflow();
void bounds_check();
void inval_opcode();
void copr_not_available();
void double_fault();
void copr_seg_overrun();
void inval_tss();
void segment_not_present();
void stack_exception();
void general_protection();
void page_fault();
void copr_error();
void hwint00();
void hwint01();
void hwint02();
void hwint03();
void hwint04();
void hwint05();
void hwint06();
void hwint07();
void hwint08();
void hwint09();
void hwint10();
void hwint11();
void hwint12();
void hwint13();
void hwint14();
void hwint15();


/*======================================================================*
                            init_prot
 *----------------------------------------------------------------------*
 初始化 IDT
 *======================================================================*/
PUBLIC void init_prot()
{
 init_8259A();

 /* 全部初始化成中斷門(沒有陷阱門) */
 init_idt_desc(INT_VECTOR_DIVIDE, DA_386IGate,
        divide_error,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_DEBUG,  DA_386IGate,
        single_step_exception, PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_NMI,  DA_386IGate,
        nmi,   PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_BREAKPOINT, DA_386IGate,
        breakpoint_exception, PRIVILEGE_USER);

 init_idt_desc(INT_VECTOR_OVERFLOW, DA_386IGate,
        overflow,   PRIVILEGE_USER);

 init_idt_desc(INT_VECTOR_BOUNDS, DA_386IGate,
        bounds_check,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_INVAL_OP, DA_386IGate,
        inval_opcode,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_COPROC_NOT, DA_386IGate,
        copr_not_available, PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_DOUBLE_FAULT, DA_386IGate,
        double_fault,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_COPROC_SEG, DA_386IGate,
        copr_seg_overrun,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_INVAL_TSS, DA_386IGate,
        inval_tss,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_SEG_NOT, DA_386IGate,
        segment_not_present, PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_STACK_FAULT, DA_386IGate,
        stack_exception,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_PROTECTION, DA_386IGate,
        general_protection, PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_PAGE_FAULT, DA_386IGate,
        page_fault,  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_COPROC_ERR, DA_386IGate,
        copr_error,  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 0,      DA_386IGate,
                      hwint00,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 1,      DA_386IGate,
                      hwint01,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 2,      DA_386IGate,
                      hwint02,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 3,      DA_386IGate,
                      hwint03,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 4,      DA_386IGate,
                      hwint04,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 5,      DA_386IGate,
                      hwint05,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 6,      DA_386IGate,
                      hwint06,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ0 + 7,      DA_386IGate,
                      hwint07,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 0,      DA_386IGate,
                      hwint08,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 1,      DA_386IGate,
                      hwint09,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 2,      DA_386IGate,
                      hwint10,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 3,      DA_386IGate,
                      hwint11,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 4,      DA_386IGate,
                      hwint12,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 5,      DA_386IGate,
                      hwint13,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 6,      DA_386IGate,
                      hwint14,                  PRIVILEGE_KRNL);

        init_idt_desc(INT_VECTOR_IRQ8 + 7,      DA_386IGate,
                      hwint15,                  PRIVILEGE_KRNL);

 init_idt_desc(INT_VECTOR_SYS_CALL, DA_386IGate,
        sys_call,   PRIVILEGE_USER);

 /* Fill the TSS descriptor in GDT */
 memset(&tss, 0, sizeof(tss));
 tss.ss0 = SELECTOR_KERNEL_DS;
 init_desc(&gdt[INDEX_TSS],
    makelinear(SELECTOR_KERNEL_DS, &tss),
    sizeof(tss) - 1,
    DA_386TSS);
 tss.iobase = sizeof(tss); /* No IO permission bitmap */

 /* Fill the LDT descriptors of each proc in GDT  */
 int i;
 for (i = 0; i < NR_TASKS + NR_PROCS; i++) {
  memset(&proc_table[i], 0, sizeof(struct proc));

  proc_table[i].ldt_sel = SELECTOR_LDT_FIRST + (i << 3);
  assert(INDEX_LDT_FIRST + i < GDT_SIZE);
  init_desc(&gdt[INDEX_LDT_FIRST + i],
     makelinear(SELECTOR_KERNEL_DS, proc_table[i].ldts),
     LDT_SIZE * sizeof(struct descriptor) - 1,
     DA_LDT);
 }
}


/*======================================================================*
                             init_idt_desc
 *----------------------------------------------------------------------*
 初始化 386 中斷門
 *======================================================================*/
PUBLIC void init_idt_desc(unsigned char vector, u8 desc_type, int_handler handler, unsigned char privilege)
{
 struct gate * p_gate = &idt[vector];
 u32 base = (u32)handler;
 p_gate->offset_low = base & 0xFFFF;
 p_gate->selector = SELECTOR_KERNEL_CS;
 p_gate->dcount  = 0;
 p_gate->attr  = desc_type | (privilege << 5);
 p_gate->offset_high = (base >> 16) & 0xFFFF;
}


/*======================================================================*
                           seg2phys
 *----------------------------------------------------------------------*
 由段名求絕對地址
 *======================================================================*/
PUBLIC u32 seg2linear(u16 seg)
{
 struct descriptor* p_dest = &gdt[seg >> 3];

 return (p_dest->base_high << 24) | (p_dest->base_mid << 16) | (p_dest->base_low);
}

/*======================================================================*
                           init_descriptor
 *----------------------------------------------------------------------*
 初始化段描述符
 *======================================================================*/
PUBLIC void init_desc(struct descriptor * p_desc, u32 base, u32 limit, u16 attribute)
{
 p_desc->limit_low = limit & 0x0FFFF;  /* 段界限 1  (2 字節) */
 p_desc->base_low = base & 0x0FFFF;  /* 段基址 1  (2 字節) */
 p_desc->base_mid = (base >> 16) & 0x0FF;  /* 段基址 2  (1 字節) */
 p_desc->attr1  = attribute & 0xFF;  /* 屬性 1 */
 p_desc->limit_high_attr2= ((limit >> 16) & 0x0F) |
      ((attribute >> 8) & 0xF0); /* 段界限 2 + 屬性 2 */
 p_desc->base_high = (base >> 24) & 0x0FF;  /* 段基址 3  (1 字節) */
}

/*======================================================================*
                            exception_handler
 *----------------------------------------------------------------------*
 異常處理
 *======================================================================*/
PUBLIC void exception_handler(int vec_no, int err_code, int eip, int cs, int eflags)
{
 int i;
 int text_color = 0x74; /* 灰底紅字 */
 char err_description[][64] = { "#DE Divide Error",
     "#DB RESERVED",
     "—  NMI Interrupt",
     "#BP Breakpoint",
     "#OF Overflow",
     "#BR BOUND Range Exceeded",
     "#UD Invalid Opcode (Undefined Opcode)",
     "#NM Device Not Available (No Math Coprocessor)",
     "#DF Double Fault",
     "    Coprocessor Segment Overrun (reserved)",
     "#TS Invalid TSS",
     "#NP Segment Not Present",
     "#SS Stack-Segment Fault",
     "#GP General Protection",
     "#PF Page Fault",
     "—  (Intel reserved. Do not use.)",
     "#MF x87 FPU Floating-Point Error (Math Fault)",
     "#AC Alignment Check",
     "#MC Machine Check",
     "#XF SIMD Floating-Point Exception"
    };

 /* 通過打印空格的方式清空屏幕的前五行,並把 disp_pos 清零 */
 disp_pos = 0;
 for(i=0;i<80*5;i++){
  disp_str(" ");
 }
 disp_pos = 0;

 disp_color_str("Exception! --> ", text_color);
 disp_color_str(err_description[vec_no], text_color);
 disp_color_str("\n\n", text_color);
 disp_color_str("EFLAGS:", text_color);
 disp_int(eflags);
 disp_color_str("CS:", text_color);
 disp_int(cs);
 disp_color_str("EIP:", text_color);
 disp_int(eip);

 if(err_code != 0xFFFFFFFF){
  disp_color_str("Error code:", text_color);
  disp_int(err_code);
 }
}

kernel/systask.c

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

#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 int read_register(char reg_addr);
PRIVATE u32 get_rtc_time(struct time *t);

/*****************************************************************************
 *                                task_sys
 *****************************************************************************/
/**
 * <Ring 1> The main loop of TASK SYS.
 *
 *****************************************************************************/
PUBLIC void task_sys()
{
 MESSAGE msg;
 struct time t;

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

  switch (msg.type) {
  case GET_TICKS:
   msg.RETVAL = ticks;
   send_recv(SEND, src, &msg);
   break;
  case GET_PID:
   msg.type = SYSCALL_RET;
   msg.PID = src;
   send_recv(SEND, src, &msg);
   break;
  case GET_RTC_TIME:
   msg.type = SYSCALL_RET;
   get_rtc_time(&t);
   phys_copy(va2la(src, msg.BUF),
      va2la(TASK_SYS, &t),
      sizeof(t));
   send_recv(SEND, src, &msg);
   break;
  default:
   panic("unknown msg type");
   break;
  }
 }
}


/*****************************************************************************
 *                                get_rtc_time
 *****************************************************************************/
/**
 * Get RTC time from the CMOS
 *
 * @return Zero.
 *****************************************************************************/
PRIVATE u32 get_rtc_time(struct time *t)
{
 t->year = read_register(YEAR);
 t->month = read_register(MONTH);
 t->day = read_register(DAY);
 t->hour = read_register(HOUR);
 t->minute = read_register(MINUTE);
 t->second = read_register(SECOND);

 if ((read_register(CLK_STATUS) & 0x04) == 0) {
  /* Convert BCD to binary (default RTC mode) */
  t->year = BCD_TO_DEC(t->year);
  t->month = BCD_TO_DEC(t->month);
  t->day = BCD_TO_DEC(t->day);
  t->hour = BCD_TO_DEC(t->hour);
  t->minute = BCD_TO_DEC(t->minute);
  t->second = BCD_TO_DEC(t->second);
 }

 t->year += 2000;

 return 0;
}

/*****************************************************************************
 *                                read_register
 *****************************************************************************/
/**
 * Read register from CMOS.
 *
 * @param reg_addr
 *
 * @return
 *****************************************************************************/
PRIVATE int read_register(char reg_addr)
{
 out_byte(CLK_ELE, reg_addr);
 return in_byte(CLK_IO);
}

lib/klib.c

/*************************************************************************//**
 *****************************************************************************
 * @file   klib.c
 * @brief
 * @author Forrest Y. Yu
 * @date   2005
 *****************************************************************************
 *****************************************************************************/

#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 "elf.h"


/*****************************************************************************
 *                                get_boot_params
 *****************************************************************************/
/**
 * <Ring 0~1> The boot parameters have been saved by LOADER.
 *            We just read them out.
 *
 * @param pbp  Ptr to the boot params structure
 *****************************************************************************/
PUBLIC void get_boot_params(struct boot_params * pbp)
{
 /**
  * Boot params should have been saved at BOOT_PARAM_ADDR.
  */
 int * p = (int*)BOOT_PARAM_ADDR;
 assert(p[BI_MAG] == BOOT_PARAM_MAGIC);

 pbp->mem_size = p[BI_MEM_SIZE];
 pbp->kernel_file = (unsigned char *)(p[BI_KERNEL_FILE]);

 /**
  * the kernel file should be a ELF executable,
  * check it's magic number
  */
 assert(memcmp(pbp->kernel_file, ELFMAG, SELFMAG) == 0);
}


/*****************************************************************************
 *                                get_kernel_map
 *****************************************************************************/
/**
 * <Ring 0~1> Parse the kernel file, get the memory range of the kernel image.
 *
 * - The meaning of `base': base => first_valid_byte
 * - The meaning of `limit': base + limit => last_valid_byte
 *
 * @param b   Memory base of kernel.
 * @param l   Memory limit of kernel.
 *****************************************************************************/
PUBLIC int get_kernel_map(unsigned int * b, unsigned int * l)
{
 struct boot_params bp;
 get_boot_params(&bp);

 Elf32_Ehdr* elf_header = (Elf32_Ehdr*)(bp.kernel_file);

 /* the kernel file should be in ELF format */
 if (memcmp(elf_header->e_ident, ELFMAG, SELFMAG) != 0)
  return -1;

 *b = ~0;
 unsigned int t = 0;
 int i;
 for (i = 0; i < elf_header->e_shnum; i++) {
  Elf32_Shdr* section_header =
   (Elf32_Shdr*)(bp.kernel_file +
          elf_header->e_shoff +
          i * elf_header->e_shentsize);

  if (section_header->sh_flags & SHF_ALLOC) {
   int bottom = section_header->sh_addr;
   int top = section_header->sh_addr +
    section_header->sh_size;

   if (*b > bottom)
    *b = bottom;
   if (t < top)
    t = top;
  }
 }
 assert(*b < t);
 *l = t - *b - 1;

 return 0;
}

/*======================================================================*
                               itoa
 *======================================================================*/
PUBLIC char * itoa(char * str, int num)/* 數字前面的 0 不被顯示出來, 比如 0000B800 被顯示成 B800 */
{
 char * p = str;
 char ch;
 int i;
 int flag = 0;

 *p++ = '0';
 *p++ = 'x';

 if(num == 0){
  *p++ = '0';
 }
 else{
  for(i=28;i>=0;i-=4){
   ch = (num >> i) & 0xF;
   if(flag || (ch > 0)){
    flag = 1;
    ch += '0';
    if(ch > '9'){
     ch += 7;
    }
    *p++ = ch;
   }
  }
 }

 *p = 0;

 return str;
}


/*======================================================================*
                               disp_int
 *======================================================================*/
PUBLIC void disp_int(int input)
{
 char output[16];
 itoa(output, input);
 disp_str(output);
}

/*======================================================================*
                               delay
 *======================================================================*/
PUBLIC void delay(int time)
{
 int i, j, k;
 for(k=0;k<time;k++){
  /*for(i=0;i<10000;i++){ for Virtual PC */
  for(i=0;i<10;i++){/* for Bochs */
   for(j=0;j<10000;j++){}
  }
 }
}

lib/syslog.c

/*************************************************************************//**
 *****************************************************************************
 * @file   syslog.c
 * @brief
 * @author Forrest Y. Yu
 * @date   Thu Nov 20 17:02:42 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"


/*****************************************************************************
 *                                syslog
 *****************************************************************************/
/**
 * Write log directly to the disk by sending message to FS.
 *
 * @param fmt The format string.
 *
 * @return How many chars have been printed.
 *****************************************************************************/
PUBLIC int syslog(const char *fmt, ...)
{
 int i;
 char buf[STR_DEFAULT_LEN];

 va_list arg = (va_list)((char*)(&fmt) + 4); /**
           * 4: size of `fmt' in
           *    the stack
           */
 i = vsprintf(buf, fmt, arg);
 assert(strlen(buf) == i);

 return disklog(buf);
}

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);
 /* int child_limit = caller_T_limit; */
 printl("{MM} 0x%x <- 0x%x (0x%x bytes)\n",
        child_base, caller_T_base, 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> tell TASK_SYS (TODO)
 *     <3> free A's memory
 *     <4> set A.exit_status, which is for the parent
 *     <5> 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
 *                   {P's wait() call is done}
 *                 - release A's proc_table[] slot
 *                   {A's exit() call is done}
 *           (2) not WAITING
 *                 - set A's HANGING bit
 *                   {things will be done at do_wait()::comment::<1>}
 *     <6> 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
 *                   {INIT's wait() call is done}
 *                 - release B's proc_table[] slot
 *                   {B's exit() call is done}
 *               else
 *                 if INIT is WAITING but B is not HANGING, then
 *                     - B will call exit() and things will be done at
 *                       do_exit()::comment::<5>::(1)
 *                 if B is HANGING but INIT is not WAITING, then
 *                     - INIT will call wait() and things will be doen at
 *                       do_wait()::comment::<1>
 *
 * 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
 *         - @see Linux manpage for more details:
 *             - man 2 exit
 *             - man 3 exit
 *             - man 2 wait
 *
 * @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];
 /* struct proc * p_parent = &proc_table[parent_pid]; */

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

 /**
  * @todo should also send a message to TASK_SYS to do some cleanup work.
  *       e.g. if the proc is killed by another proc, TASK_SYS should
  *            check if the proc happens to be SENDING a message, if so,
  *            the proc should be removed from the sending queue.
  * @see MINIX::src/kernel/system.c:do_xit()
  */

 free_mem(pid);

 p->exit_status = status;

 if (proc_table[parent_pid].p_flags & WAITING) { /* parent is waiting */
  printl("{MM} ((--do_exit():: %s (%d) is WAITING, %s (%d) will be cleaned up.--))\n",
         proc_table[parent_pid].name, parent_pid,
         p->name, pid);
  /* dump_fd_graph("((--do_exit():: %s (%d) is WAITING, %s (%d) will be cleaned up.--))", */
  /*        proc_table[parent_pid].name, parent_pid, */
  /*        p->name, pid); */
  /* assert(proc_table[parent_pid].p_flags & RECEIVING); */
  printl("{MM} ((--do_exit():1: proc_table[parent_pid].p_flags: 0x%x--))\n",
         proc_table[parent_pid].p_flags);
  /* dump_fd_graph("((--do_exit():1: proc_table[parent_pid].p_flags: 0x%x--))", */
  /*        proc_table[parent_pid].p_flags); */
  proc_table[parent_pid].p_flags &= ~WAITING;
  cleanup(&proc_table[pid]);
 }
 else { /* parent is not waiting */
  printl("{MM} ((--do_exit():: %s (%d) is not WAITING, %s (%d) will be HANGING--))\n",
         proc_table[parent_pid].name, parent_pid,
         p->name, pid);
  /* dump_fd_graph("((--do_exit():: %s (%d) is not WAITING, %s (%d) will be HANGING--))", */
  /*        proc_table[parent_pid].name, parent_pid, */
  /*        p->name, pid); */
  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; /* FIXME: make sure INIT always waits */
   printl("{MM} %s (%d) exit(), so %s (%d) is INIT's child now\n",
          p->name, pid, proc_table[i].name, i);
   /* dump_fd_graph("%s (%d) exit(), so %s (%d) is INIT's child now", */
   /*        p->name, pid, proc_table[i].name, i); */
   printl("{MM} ((--do_exit():2: proc_table[INIT].p_flags: 0x%x--))\n",
          proc_table[INIT].p_flags);
   /* dump_fd_graph("((--do_exit():2: proc_table[INIT].p_flags: 0x%x--))", */
   /*        proc_table[INIT].p_flags); */
   if ((proc_table[INIT].p_flags & WAITING) &&
       (proc_table[i].p_flags & HANGING)) {
    /* assert(proc_table[INIT].p_flags & RECEIVING); */
    proc_table[INIT].p_flags &= ~WAITING;
    cleanup(&proc_table[i]);
    assert(0);
   }
   else {
    /* assert(0); */
   }
  }
 }
}

/*****************************************************************************
 *                                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;

 printl("{MM} ((--cleanup():: %s (%d) has been cleaned up.--))\n", proc->name, proc2pid(proc));
 /* dump_fd_graph("((--cleanup():: %s (%d) has been cleaned up.--))", proc->name, proc2pid(proc)); */
}

/*****************************************************************************
 *                                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)
 *             {P's wait() call is done}
 *           - release A's proc_table[] entry
 *             {A's exit() call is done}
 *           - return (MM will go on with the next message loop)
 *     <2> if no child of P is HANGING
 *           - set P's WAITING bit
 *             {things will be done at do_exit()::comment::<5>::(1)}
 *     <3> if P has no child at all
 *           - reply to P with error
 *             {P's wait() call is done}
 *     <4> return (MM will go on with the next message loop)
 *
 *****************************************************************************/
PUBLIC void do_wait()
{
 printl("{MM} ((--do_wait()--))");
 /* dump_fd_graph("((--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) {
    printl("{MM} ((--do_wait():: %s (%d) is HANGING, "
           "so let's clean it up.--))",
           p_proc->name, i);
    /* dump_fd_graph("((--do_wait():: %s (%d) is HANGING, " */
    /*        "so let's clean it up.--))", */
    /*        p_proc->name, i); */
    cleanup(p_proc);
    return;
   }
  }
 }

 if (children) {
  /* has children, but no child is HANGING */
  proc_table[pid].p_flags |= WAITING;
  printl("{MM} ((--do_wait():: %s (%d) is WAITING for child "
         "to exit().--))\n", proc_table[pid].name, pid);
  /* dump_fd_graph("((--do_wait():: %s (%d) is WAITING for child " */
  /*        "to exit().--))", proc_table[pid].name, pid); */
 }
 else {
  /* no child at all */
  printl("{MM} ((--do_wait():: %s (%d) has no child at all.--))\n",
         proc_table[pid].name, pid);
  /* dump_fd_graph("((--do_wait():: %s (%d) is has no child at all.--))", */
  /*        proc_table[pid].name, pid); */
  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;
}

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

# This Program
ORANGESBOOT = boot/boot.bin boot/loader.bin
ORANGESKERNEL = kernel.bin
OBJS  = kernel/kernel.o lib/syscall.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\
   lib/printf.o lib/vsprintf.o\
   lib/kliba.o lib/klib.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/syslog.o\
   fs/main.o fs/open.o fs/misc.o fs/read_write.o\
   lib/fork.o lib/exit.o lib/wait.o\
   mm/main.o mm/forkexit.o\
   fs/link.o\
   fs/disklog.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

clean :
 rm -f $(OBJS)

realclean :
 rm -f $(OBJS) $(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

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)
 $(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $(OBJS)

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 $@ $<

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

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

lib/kliba.o : lib/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/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 $@ $<
mm/main.o: mm/main.c
 $(CC) $(CFLAGS) -o $@ $<

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

lib/syslog.o: lib/syslog.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 $@ $<

boot/loader.asm

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               loader.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                     Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


org  0100h

 jmp LABEL_START  ; Start

; 下面是 FAT12 磁碟的頭, 之所以包含它是因為下面用到了磁碟的一些訊息
%include "fat12hdr.inc"
%include "load.inc"
%include "pm.inc"


; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------
;                                                段基址            段界限     , 屬性
LABEL_GDT:   Descriptor             0,                    0, 0      ; 空描述符
LABEL_DESC_FLAT_C:  Descriptor             0,              0fffffh, DA_CR  | DA_32 | DA_LIMIT_4K   ; 0 ~ 4G
LABEL_DESC_FLAT_RW:  Descriptor             0,              0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K   ; 0 ~ 4G
LABEL_DESC_VIDEO:  Descriptor  0B8000h,               0ffffh, DA_DRW                         | DA_DPL3 ; 顯示卡記憶體首位址
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------

GdtLen  equ $ - LABEL_GDT
GdtPtr  dw GdtLen - 1    ; 段界限
  dd LOADER_PHY_ADDR + LABEL_GDT  ; 基位址 (讓基位址八字元對齊將起到優化速度之效果,目前懶得改)
; The GDT is not a segment itself; instead, it is a data structure in linear address space.
; The base linear address and limit of the GDT must be loaded into the GDTR register. -- IA-32 Software Developer’s Manual, Vol.3A


; GDT 選擇子 ----------------------------------------------------------------------------------
SelectorFlatC  equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW  equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo  equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3
; GDT 選擇子 ----------------------------------------------------------------------------------


BaseOfStack equ 0100h


LABEL_START:   ; <--- 從這裡開始 *************
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, BaseOfStack

 mov dh, 0   ; "Loading  "
 call DispStrRealMode  ; 顯示字元串

 ; 得到記憶體數
 mov ebx, 0   ; ebx = 後續值, 開始時需為 0
 mov di, _MemChkBuf  ; es:di 指向一個位址範圍描述符結構(Address Range Descriptor Structure)
.MemChkLoop:
 mov eax, 0E820h  ; eax = 0000E820h
 mov ecx, 20   ; ecx = 位址範圍描述符結構的大小
 mov edx, 0534D4150h  ; edx = 'SMAP'
 int 15h   ; int 15h
 jc .MemChkFail
 add di, 20
 inc dword [_dwMCRNumber] ; dwMCRNumber = ARDS 的個數
 cmp ebx, 0
 jne .MemChkLoop
 jmp .MemChkOK
.MemChkFail:
 mov dword [_dwMCRNumber], 0
.MemChkOK:

 ; 下面在 A 碟的根目錄尋找 KERNEL.BIN
 mov word [wSectorNo], SectorNoOfRootDirectory
 xor ah, ah ; ┓
 xor dl, dl ; ┣ 軟體驅動復位
 int 13h ; ┛
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
 cmp word [wRootDirSizeForLoop], 0 ; ┓
 jz LABEL_NO_KERNELBIN  ; ┣ 判斷根目錄區是不是已經讀完, 如果讀完表示沒有找到 KERNEL.BIN
 dec word [wRootDirSizeForLoop] ; ┛
 mov ax, KERNEL_FILE_SEG
 mov es, ax   ; es <- KERNEL_FILE_SEG
 mov bx, KERNEL_FILE_OFF ; bx <- KERNEL_FILE_OFF 於是, es:bx = KERNEL_FILE_SEG:KERNEL_FILE_OFF = KERNEL_FILE_SEG * 10h + KERNEL_FILE_OFF
 mov ax, [wSectorNo]  ; ax <- Root Directory 中的某 Sector 號
 mov cl, 1
 call ReadSector

 mov si, KernelFileName ; ds:si -> "KERNEL  BIN"
 mov di, KERNEL_FILE_OFF ; es:di -> KERNEL_FILE_SEG:???? = KERNEL_FILE_SEG*10h+????
 cld
 mov dx, 10h
LABEL_SEARCH_FOR_KERNELBIN:
 cmp dx, 0     ; ┓
 jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ; ┣ 循環次數控制, 如果已經讀完了一個 Sector, 就跳到下一個 Sector
 dec dx     ; ┛
 mov cx, 11
LABEL_CMP_FILENAME:
 cmp cx, 0   ; ┓
 jz LABEL_FILENAME_FOUND ; ┣ 循環次數控制, 如果比較了 11 個字元都相等, 表示找到
 dec cx   ; ┛
 lodsb    ; ds:si -> al
 cmp al, byte [es:di] ; if al == es:di
 jz LABEL_GO_ON
 jmp LABEL_DIFFERENT
LABEL_GO_ON:
 inc di
 jmp LABEL_CMP_FILENAME ; 繼續循環

LABEL_DIFFERENT:
 and di, 0FFE0h  ; else┓ 這時di的值不知道是什麼, di &= e0 為了讓它是 20h 的倍數
 add di, 20h   ;     ┃
 mov si, KernelFileName ;     ┣ di += 20h  下一個目錄項目
 jmp LABEL_SEARCH_FOR_KERNELBIN;   ┛

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
 add word [wSectorNo], 1
 jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN

LABEL_NO_KERNELBIN:
 mov dh, 3   ; "No KERNEL."
 call DispStrRealMode  ; 顯示字元串
 jmp $   ; 沒有找到 KERNEL.BIN, 死循環在這裡

LABEL_FILENAME_FOUND:   ; 找到 KERNEL.BIN 後便來到這裡繼續
 mov ax, RootDirSectors
 and di, 0FFF0h  ; di -> 當前項目的開始

 push eax
 mov eax, [es : di + 01Ch]  ; ┓
 mov dword [dwKernelSize], eax ; ┛保存 KERNEL.BIN 檔案大小
 cmp eax, KERNEL_VALID_SPACE
 ja .1
 pop eax
 jmp .2
.1:
 mov dh, 4   ; "Too Large"
 call DispStrRealMode  ; 顯示字元串
 jmp $   ; KERNEL.BIN 太大,死循環在這裡
.2:
 add di, 01Ah  ; di -> 首 Sector
 mov cx, word [es:di]
 push cx   ; 保存此 Sector 在 FAT 中的序號
 add cx, ax
 add cx, DeltaSectorNo ; 這時 cl 裡面是 LOADER.BIN 的起始磁區號 (從 0 開始數的序號)
 mov ax, KERNEL_FILE_SEG
 mov es, ax   ; es <- KERNEL_FILE_SEG
 mov bx, KERNEL_FILE_OFF ; bx <- KERNEL_FILE_OFF 於是, es:bx = KERNEL_FILE_SEG:KERNEL_FILE_OFF = KERNEL_FILE_SEG * 10h + KERNEL_FILE_OFF
 mov ax, cx   ; ax <- Sector 號

LABEL_GOON_LOADING_FILE:
 push ax   ; ┓
 push bx   ; ┃
 mov ah, 0Eh   ; ┃ 每讀一個磁區就在 "Loading  " 後面
 mov al, '.'   ; ┃ 打一個點, 形成這樣的效果:
 mov bl, 0Fh   ; ┃ Loading ......
 int 10h   ; ┃
 pop bx   ; ┃
 pop ax   ; ┛

 mov cl, 1
 call ReadSector
 pop ax   ; 取出此 Sector 在 FAT 中的序號
 call GetFATEntry
 cmp ax, 0FFFh
 jz LABEL_FILE_LOADED
 push ax   ; 保存 Sector 在 FAT 中的序號
 mov dx, RootDirSectors
 add ax, dx
 add ax, DeltaSectorNo
 add bx, [BPB_BytsPerSec]
 jc .1   ; 如果 bx 重新變成 0,說明內核大於 64K
 jmp .2
.1:
 push ax   ; es += 0x1000  ← es 指向下一個段
 mov ax, es
 add ax, 1000h
 mov es, ax
 pop ax
.2:
 jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED:

 call KillMotor  ; 關閉軟體驅動馬達

;;;  ;; 取硬碟訊息
;;;  xor eax, eax
;;;  mov ah, 08h  ; Code for drive parameters
;;;  mov dx, 80h  ; hard drive
;;;  int 0x13
;;;  jb .hderr  ; No such drive?
;;;  ;; cylinder number
;;;  xor ax, ax  ; ax <- 0
;;;  mov ah, cl  ; ax <- cl
;;;  shr ah, 6
;;;  and ah, 3     ; cl bits 7-6: high two bits of maximum cylinder number
;;;  mov al, ch  ; CH = low eight bits of maximum cylinder number
;;;  ;; sector number
;;;  and cl, 3Fh  ; cl bits 5-0: max sector number (1-origin)
;;;  ;; head number
;;;  inc dh  ; dh = 1 + max head number (0-origin)
;;;  mov [_dwNrHead], dh
;;;  mov [_dwNrSector], cl
;;;  mov [_dwNrCylinder], ax
;;;  jmp .hdok
;;; .hderr:
;;;  mov dword [_dwNrHead], 0FFFFh
;;; .hdok:
 ;; 將硬碟引導磁區內容讀入記憶體 0500h 處
 xor     ax, ax
 mov     es, ax
 mov     ax, 0201h       ; AH = 02
                         ; AL = number of sectors to read (must be nonzero)
 mov     cx, 1           ; CH = low eight bits of cylinder number
                         ; CL = sector number 1-63 (bits 0-5)
                         ;      high two bits of cylinder (bits 6-7, hard disk only)
 mov     dx, 80h         ; DH = head number
                         ; DL = drive number (bit 7 set for hard disk)
 mov     bx, 500h        ; ES:BX -> data buffer
 int     13h
 ;; 硬碟操作完畢

 mov dh, 2   ; "Ready."
 call DispStrRealMode  ; 顯示字元串


; 下面準備跳入保護模式 -------------------------------------------

; 加載 GDTR
 lgdt [GdtPtr]

; 關中斷
 cli

; 打開位址線A20
 in al, 92h
 or al, 00000010b
 out 92h, al

; 準備切換到保護模式
 mov eax, cr0
 or eax, 1
 mov cr0, eax

; 真正進入保護模式
 jmp dword SelectorFlatC:(LOADER_PHY_ADDR+LABEL_PM_START)


;============================================================================
;變量
;----------------------------------------------------------------------------
wRootDirSizeForLoop dw RootDirSectors ; Root Directory 占用的磁區數
wSectorNo  dw 0  ; 要讀取的磁區號
bOdd   db 0  ; 奇數還是偶數
dwKernelSize  dd 0  ; KERNEL.BIN 檔案大小

;============================================================================
;字元串
;----------------------------------------------------------------------------
KernelFileName  db "KERNEL  BIN", 0 ; KERNEL.BIN 之檔案名
; 為簡化程式碼, 下面每個字元串的長度均為 MessageLength
MessageLength  equ 9
LoadMessage:  db "Loading  "
Message1  db "         "
Message2  db "Ready.   "
Message3  db "No KERNEL"
Message4  db "Too Large"
;============================================================================

;----------------------------------------------------------------------------
; 函數名: DispStrRealMode
;----------------------------------------------------------------------------
; 運行環境:
; 實模式(保護模式下顯示字元串由函數 DispStr 完成)
; 作用:
; 顯示一個字元串, 函數開始時 dh 中應該是字元串序號(0-based)
DispStrRealMode:
 mov ax, MessageLength
 mul dh
 add ax, LoadMessage
 mov bp, ax   ; ┓
 mov ax, ds   ; ┣ ES:BP = 串位址
 mov es, ax   ; ┛
 mov cx, MessageLength ; CX = 串長度
 mov ax, 01301h  ; AH = 13,  AL = 01h
 mov bx, 0007h  ; 頁號為0(BH = 0) 黑底白字(BL = 07h)
 mov dl, 0
 add dh, 3   ; 從第 3 行往下顯示
 int 10h   ; int 10h
 ret
;----------------------------------------------------------------------------
; 函數名: ReadSector
;----------------------------------------------------------------------------
; 作用:
; 從序號(Directory Entry 中的 Sector 號)為 ax 的的 Sector 開始, 將 cl 個 Sector 讀入 es:bx 中
ReadSector:
 ; -----------------------------------------------------------------------
 ; 怎樣由磁區號求磁區在磁碟中的位置 (磁區號 -> 柱面號, 起始磁區, 磁頭號)
 ; -----------------------------------------------------------------------
 ; 設磁區號為 x
 ;                           ┌ 柱面號 = y >> 1
 ;       x           ┌ 商 y ┤
 ; -------------- => ┤      └ 磁頭號 = y & 1
 ;  每磁道磁區數     │
 ;                   └ 余 z => 起始磁區號 = z + 1
 push bp
 mov bp, sp
 sub esp, 2   ; 辟出兩個字元的堆疊區域保存要讀的磁區數: byte [bp-2]

 mov byte [bp-2], cl
 push bx   ; 保存 bx
 mov bl, [BPB_SecPerTrk] ; bl: 除數
 div bl   ; y 在 al 中, z 在 ah 中
 inc ah   ; z ++
 mov cl, ah   ; cl <- 起始磁區號
 mov dh, al   ; dh <- y
 shr al, 1   ; y >> 1 (其實是 y/BPB_NumHeads, 這裡BPB_NumHeads=2)
 mov ch, al   ; ch <- 柱面號
 and dh, 1   ; dh & 1 = 磁頭號
 pop bx   ; 恢復 bx
 ; 至此, "柱面號, 起始磁區, 磁頭號" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
 mov dl, [BS_DrvNum]  ; 驅動器號 (0 表示 A 碟)
.GoOnReading:
 mov ah, 2   ; 讀
 mov al, byte [bp-2]  ; 讀 al 個磁區
 int 13h
 jc .GoOnReading  ; 如果讀取錯誤 CF 會被置為 1, 這時就不停地讀, 直到正確為止

 add esp, 2
 pop bp

 ret

;----------------------------------------------------------------------------
; 函數名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
; 找到序號為 ax 的 Sector 在 FAT 中的項目, 結果放在 ax 中
; 需要注意的是, 中間需要讀 FAT 的磁區到 es:bx 處, 所以函數一開始保存了 es 和 bx
GetFATEntry:
 push es
 push bx
 push ax
 mov ax, KERNEL_FILE_SEG ; ┓
 sub ax, 0100h  ; ┣ 在 KERNEL_FILE_SEG 後面留出 4K 空間用於存放 FAT
 mov es, ax   ; ┛
 pop ax
 mov byte [bOdd], 0
 mov bx, 3
 mul bx   ; dx:ax = ax * 3
 mov bx, 2
 div bx   ; dx:ax / 2  ==>  ax <- 商, dx <- 餘數
 cmp dx, 0
 jz LABEL_EVEN
 mov byte [bOdd], 1
LABEL_EVEN:;偶數
 xor dx, dx   ; 現在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面來計算 FATEntry 在哪個磁區中(FAT占用不止一個磁區)
 mov bx, [BPB_BytsPerSec]
 div bx   ; dx:ax / BPB_BytsPerSec  ==> ax <- 商   (FATEntry 所在的磁區相對於 FAT 來說的磁區號)
     ;    dx <- 餘數 (FATEntry 在磁區內的偏移)。
 push dx
 mov bx, 0   ; bx <- 0 於是, es:bx = (KERNEL_FILE_SEG - 100):00 = (KERNEL_FILE_SEG - 100) * 10h
 add ax, SectorNoOfFAT1 ; 此句執行之後的 ax 就是 FATEntry 所在的磁區號
 mov cl, 2
 call ReadSector  ; 讀取 FATEntry 所在的磁區, 一次讀兩個, 避免在邊界發生錯誤, 因為一個 FATEntry 可能跨越兩個磁區
 pop dx
 add bx, dx
 mov ax, [es:bx]
 cmp byte [bOdd], 1
 jnz LABEL_EVEN_2
 shr ax, 4
LABEL_EVEN_2:
 and ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:

 pop bx
 pop es
 ret
;----------------------------------------------------------------------------


;----------------------------------------------------------------------------
; 函數名: KillMotor
;----------------------------------------------------------------------------
; 作用:
; 關閉軟體驅動馬達
KillMotor:
 push dx
 mov dx, 03F2h
 mov al, 0
 out dx, al
 pop dx
 ret
;----------------------------------------------------------------------------


; 從此以後的程式碼在保護模式下執行 ----------------------------------------------------
; 32 位程式碼段. 由實模式跳入 ---------------------------------------------------------
[SECTION .s32]

ALIGN 32

[BITS 32]

LABEL_PM_START:
 mov ax, SelectorVideo
 mov gs, ax
 mov ax, SelectorFlatRW
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov ss, ax
 mov esp, TopOfStack

 call DispMemInfo
;;;  call DispReturn
;;;  call DispHDInfo ; int 13h 讀出的硬碟 geometry 好像有點不對頭,不知道為什麼,乾脆不管它了
 call SetupPaging

 ;mov ah, 0Fh    ; 0000: 黑底    1111: 白字
 ;mov al, 'P'
 ;mov [gs:((80 * 0 + 39) * 2)], ax ; 螢幕第 0 行, 第 39 列。

 call InitKernel

 ;jmp $

 ;; fill in BootParam[]
 mov dword [BOOT_PARAM_ADDR], BOOT_PARAM_MAGIC ; Magic Number
 mov eax, [dwMemSize]
 mov [BOOT_PARAM_ADDR + 4], eax ; memory size
 mov eax, KERNEL_FILE_SEG
 shl eax, 4
 add eax, KERNEL_FILE_OFF
 mov [BOOT_PARAM_ADDR + 8], eax ; phy-addr of kernel.bin

 ;***************************************************************
 jmp SelectorFlatC:KRNL_ENT_PT_PHY_ADDR ; 正式进入内核 *
 ;***************************************************************
 ; 内存看上去是这样的:
 ;              ┃                                    ┃
 ;              ┃                 .                  ┃
 ;              ┃                 .                  ┃
 ;              ┃                 .                  ┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■Page  Tables■■■■■■┃
 ;              ┃■■■■■(大小由LOADER决定)■■■■┃
 ;    00101000h ┃■■■■■■■■■■■■■■■■■■┃ PAGE_TBL_BASE
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;    00100000h ┃■■■■Page Directory Table■■■■┃ PAGE_DIR_BASE  <- 1M
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       F0000h ┃□□□□□□□System ROM□□□□□□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       E0000h ┃□□□□Expansion of system ROM □□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       C0000h ┃□□□Reserved for ROM expansion□□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃ B8000h ← gs
 ;       A0000h ┃□□□Display adapter reserved□□□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       9FC00h ┃□□extended BIOS data area (EBDA)□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;       90000h ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;       70000h ┃■■■■■■■KERNEL.BIN■■■■■■┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃ 7C00h~7DFFh : BOOT SECTOR, overwritten by the kernel
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;        1000h ┃■■■■■■■■KERNEL■■■■■■■┃ 1000h ← KERNEL 入口 (KRNL_ENT_PT_PHY_ADDR)
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃                                    ┃
 ;         500h ┃              F  R  E  E            ┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;         400h ┃□□□□ROM BIOS parameter area □□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇┃
 ;           0h ┃◇◇◇◇◇◇Int  Vectors◇◇◇◇◇◇┃
 ;              ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
 ;
 ;
 ;  ┏━━━┓  ┏━━━┓
 ;  ┃■■■┃ 我们使用  ┃□□□┃ 不能使用的内存
 ;  ┗━━━┛  ┗━━━┛
 ;  ┏━━━┓  ┏━━━┓
 ;  ┃      ┃ 未使用空间 ┃◇◇◇┃ 可以覆盖的内存
 ;  ┗━━━┛  ┗━━━┛
 ;
 ; 注:KERNEL 的位置实际上是很灵活的,可以通过同时改变 LOAD.INC 中的 KRNL_ENT_PT_PHY_ADDR 和 MAKEFILE 中参数 -Ttext 的值来改变。
 ;     比如,如果把 KRNL_ENT_PT_PHY_ADDR 和 -Ttext 的值都改为 0x400400,则 KERNEL 就会被加载到内存 0x400000(4M) 处,入口在 0x400400。
 ;




; ------------------------------------------------------------------------
; 显示 AL 中的数字
; ------------------------------------------------------------------------
DispAL:
 push ecx
 push edx
 push edi

 mov edi, [dwDispPos]

 mov ah, 0Fh   ; 0000b: 黑底    1111b: 白字
 mov dl, al
 shr al, 4
 mov ecx, 2
.begin:
 and al, 01111b
 cmp al, 9
 ja .1
 add al, '0'
 jmp .2
.1:
 sub al, 0Ah
 add al, 'A'
.2:
 mov [gs:edi], ax
 add edi, 2

 mov al, dl
 loop .begin
 ;add edi, 2

 mov [dwDispPos], edi

 pop edi
 pop edx
 pop ecx

 ret
; DispAL 结束-------------------------------------------------------------


; ------------------------------------------------------------------------
; 显示一个整形数
; ------------------------------------------------------------------------
DispInt:
 mov eax, [esp + 4]
 shr eax, 24
 call DispAL

 mov eax, [esp + 4]
 shr eax, 16
 call DispAL

 mov eax, [esp + 4]
 shr eax, 8
 call DispAL

 mov eax, [esp + 4]
 call DispAL

 mov ah, 07h   ; 0000b: 黑底    0111b: 灰字
 mov al, 'h'
 push edi
 mov edi, [dwDispPos]
 mov [gs:edi], ax
 add edi, 4
 mov [dwDispPos], edi
 pop edi

 ret
; DispInt 结束------------------------------------------------------------

; ------------------------------------------------------------------------
; 显示一个字元串
; ------------------------------------------------------------------------
DispStr:
 push ebp
 mov ebp, esp
 push ebx
 push esi
 push edi

 mov esi, [ebp + 8] ; pszInfo
 mov edi, [dwDispPos]
 mov ah, 0Fh
.1:
 lodsb
 test al, al
 jz .2
 cmp al, 0Ah ; 是回车吗?
 jnz .3
 push eax
 mov eax, edi
 mov bl, 160
 div bl
 and eax, 0FFh
 inc eax
 mov bl, 160
 mul bl
 mov edi, eax
 pop eax
 jmp .1
.3:
 mov [gs:edi], ax
 add edi, 2
 jmp .1

.2:
 mov [dwDispPos], edi

 pop edi
 pop esi
 pop ebx
 pop ebp
 ret
; DispStr 结束------------------------------------------------------------

; ------------------------------------------------------------------------
; 换行
; ------------------------------------------------------------------------
DispReturn:
 push szReturn
 call DispStr   ;printf("\n");
 add esp, 4

 ret
; DispReturn 结束---------------------------------------------------------


; ------------------------------------------------------------------------
; 内存拷贝,仿 memcpy
; ------------------------------------------------------------------------
; void* MemCpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
MemCpy:
 push ebp
 mov ebp, esp

 push esi
 push edi
 push ecx

 mov edi, [ebp + 8] ; Destination
 mov esi, [ebp + 12] ; Source
 mov ecx, [ebp + 16] ; Counter
.1:
 cmp ecx, 0  ; 判断计数器
 jz .2  ; 计数器为零时跳出

 mov al, [ds:esi]  ; ┓
 inc esi   ; ┃
     ; ┣ 逐字节移动
 mov byte [es:edi], al ; ┃
 inc edi   ; ┛

 dec ecx  ; 计数器减一
 jmp .1  ; 循环
.2:
 mov eax, [ebp + 8] ; 返回值

 pop ecx
 pop edi
 pop esi
 mov esp, ebp
 pop ebp

 ret   ; 函数结束,返回
; MemCpy 结束-------------------------------------------------------------




; 显示内存訊息 --------------------------------------------------------------
DispMemInfo:
 push esi
 push edi
 push ecx

 push szMemChkTitle
 call DispStr
 add esp, 4

 mov esi, MemChkBuf
 mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构
.loop:     ;{
 mov edx, 5   ; for(int j=0;j<5;j++) // 每次得到一个ARDS中的成员,共5个成员
 mov edi, ARDStruct  ; {   // 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
.1:     ;
 push dword [esi]  ;
 call DispInt   ;  DispInt(MemChkBuf[j*4]); // 显示一个成员
 pop eax   ;
 stosd    ;  ARDStruct[j*4] = MemChkBuf[j*4];
 add esi, 4   ;
 dec edx   ;
 cmp edx, 0   ;
 jnz .1   ; }
 call DispReturn  ; printf("\n");
 cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
 jne .2   ; {
 mov eax, [dwBaseAddrLow] ;
 add eax, [dwLengthLow] ;
 cmp eax, [dwMemSize] ;  if(BaseAddrLow + LengthLow > MemSize)
 jb .2   ;
 mov [dwMemSize], eax ;   MemSize = BaseAddrLow + LengthLow;
.2:     ; }
 loop .loop   ;}
     ;
 call DispReturn  ;printf("\n");
 push szRAMSize  ;
 call DispStr   ;printf("RAM size:");
 add esp, 4   ;
     ;
 push dword [dwMemSize] ;
 call DispInt   ;DispInt(MemSize);
 add esp, 4   ;

 pop ecx
 pop edi
 pop esi
 ret
; ---------------------------------------------------------------------------


;;; ; 显示内存訊息 --------------------------------------------------------------
;;; DispHDInfo:
;;;  push eax

;;;  cmp dword [dwNrHead], 0FFFFh
;;;  je .nohd

;;;  push szCylinder
;;;  call DispStr   ; printf("C:");
;;;  add esp, 4

;;;  push dword [dwNrCylinder]  ; NR Cylinder
;;;  call DispInt
;;;  pop eax

;;;  push szHead
;;;  call DispStr   ; printf(" H:");
;;;  add esp, 4

;;;  push dword [dwNrHead]  ; NR Head
;;;  call DispInt
;;;  pop eax

;;;  push szSector
;;;  call DispStr   ; printf(" S:");
;;;  add esp, 4

;;;  push dword [dwNrSector]  ; NR Sector
;;;  call DispInt
;;;  pop eax

;;;  jmp .hdinfo_finish

;;; .nohd:
;;;  push szNOHD
;;;  call DispStr   ; printf("No hard drive. System halt.");
;;;  add esp, 4
;;;  jmp $   ; 没有硬盘,死在这里

;;; .hdinfo_finish:
;;;  call DispReturn

;;;  pop eax
;;;  ret
;;; ; ---------------------------------------------------------------------------


; 启动分页机制 --------------------------------------------------------------
SetupPaging:
 ; 根据内存大小计算应初始化多少PDE以及多少页表
 xor edx, edx
 mov eax, [dwMemSize]
 mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
 div ebx
 mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
 test edx, edx
 jz .no_remainder
 inc ecx  ; 如果余数不为 0 就需增加一个页表
.no_remainder:
 push ecx  ; 暂存页表个数

 ; 为简化处理, 所有线性位址对应相等的物理位址. 并且不考虑内存空洞.

 ; 首先初始化页目录
 mov ax, SelectorFlatRW
 mov es, ax
 mov edi, PAGE_DIR_BASE ; 此段首位址为 PAGE_DIR_BASE
 xor eax, eax
 mov eax, PAGE_TBL_BASE | PG_P  | PG_USU | PG_RWW
.1:
 stosd
 add eax, 4096  ; 为了简化, 所有页表在内存中是连续的.
 loop .1

 ; 再初始化所有页表
 pop eax   ; 页表个数
 mov ebx, 1024  ; 每个页表 1024 个 PTE
 mul ebx
 mov ecx, eax  ; PTE个数 = 页表个数 * 1024
 mov edi, PAGE_TBL_BASE ; 此段首位址为 PAGE_TBL_BASE
 xor eax, eax
 mov eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 add eax, 4096  ; 每一页指向 4K 的空间
 loop .2

 mov eax, PAGE_DIR_BASE
 mov cr3, eax
 mov eax, cr0
 or eax, 80000000h
 mov cr0, eax
 jmp short .3
.3:
 nop

 ret
; 分页机制启动完毕 ----------------------------------------------------------



; InitKernel ---------------------------------------------------------------------------------
; 将 KERNEL.BIN 的内容经过整理对齐后放到新的位置
; --------------------------------------------------------------------------------------------
InitKernel: ; 遍历每一个 Program Header,根据 Program Header 中的訊息来确定把什么放进内存,放到什么位置,以及放多少。
 xor esi, esi
 mov cx, word [KERNEL_FILE_PHY_ADDR + 2Ch]; ┓ ecx <- pELFHdr->e_phnum
 movzx ecx, cx     ; ┛
 mov esi, [KERNEL_FILE_PHY_ADDR + 1Ch] ; esi <- pELFHdr->e_phoff
 add esi, KERNEL_FILE_PHY_ADDR  ; esi <- OffsetOfKernel + pELFHdr->e_phoff
.Begin:
 mov eax, [esi + 0]
 cmp eax, 0    ; PT_NULL
 jz .NoAction
 push dword [esi + 010h]  ; size ┓
 mov eax, [esi + 04h]  ; ┃
 add eax, KERNEL_FILE_PHY_ADDR ; ┣ ::memcpy( (void*)(pPHdr->p_vaddr),
 push eax    ; src ┃  uchCode + pPHdr->p_offset,
 push dword [esi + 08h]  ; dst ┃  pPHdr->p_filesz;
 call MemCpy    ; ┃
 add esp, 12    ; ┛
.NoAction:
 add esi, 020h   ; esi += pELFHdr->e_phentsize
 dec ecx
 jnz .Begin

 ret
; InitKernel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


; SECTION .data1 之开始 ---------------------------------------------------------------------------------------------
[SECTION .data1]

ALIGN 32

LABEL_DATA:
; 实模式下使用这些符号
; 字元串
_szMemChkTitle:   db "BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0
_szRAMSize:   db "RAM size: ", 0
;;; _szCylinder   db "HD Info : C=", 0
;;; _szHead    db " H=", 0
;;; _szSector   db " S=", 0
;;; _szNOHD    db "No hard drive. System halt.", 0
_szReturn:   db 0Ah, 0
;; 变量
;;; _dwNrCylinder   dd 0
;;; _dwNrHead   dd 0
;;; _dwNrSector   dd 0
_dwMCRNumber:   dd 0 ; Memory Check Result
_dwDispPos:   dd (80 * 7 + 0) * 2 ; 螢幕第 7 行, 第 0 列。
_dwMemSize:   dd 0
_ARDStruct:   ; Address Range Descriptor Structure
 _dwBaseAddrLow:  dd 0
 _dwBaseAddrHigh: dd 0
 _dwLengthLow:  dd 0
 _dwLengthHigh:  dd 0
 _dwType:  dd 0
_MemChkBuf: times 256 db 0
;
;; 保护模式下使用这些符号
szMemChkTitle  equ LOADER_PHY_ADDR + _szMemChkTitle
szRAMSize  equ LOADER_PHY_ADDR + _szRAMSize
;;; szCylinder  equ LOADER_PHY_ADDR + _szCylinder
;;; szHead   equ LOADER_PHY_ADDR + _szHead
;;; szSector  equ LOADER_PHY_ADDR + _szSector
;;; szNOHD   equ LOADER_PHY_ADDR + _szNOHD
szReturn  equ LOADER_PHY_ADDR + _szReturn
;;; dwNrCylinder  equ LOADER_PHY_ADDR + _dwNrCylinder
;;; dwNrHead  equ LOADER_PHY_ADDR + _dwNrHead
;;; dwNrSector  equ LOADER_PHY_ADDR + _dwNrSector
dwDispPos  equ LOADER_PHY_ADDR + _dwDispPos
dwMemSize  equ LOADER_PHY_ADDR + _dwMemSize
dwMCRNumber  equ LOADER_PHY_ADDR + _dwMCRNumber
ARDStruct  equ LOADER_PHY_ADDR + _ARDStruct
 dwBaseAddrLow equ LOADER_PHY_ADDR + _dwBaseAddrLow
 dwBaseAddrHigh equ LOADER_PHY_ADDR + _dwBaseAddrHigh
 dwLengthLow equ LOADER_PHY_ADDR + _dwLengthLow
 dwLengthHigh equ LOADER_PHY_ADDR + _dwLengthHigh
 dwType  equ LOADER_PHY_ADDR + _dwType
MemChkBuf  equ LOADER_PHY_ADDR + _MemChkBuf


; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ LOADER_PHY_ADDR + $ ; 栈顶
; SECTION .data1 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

注意:如果採用 64 位元 Linux Mint,可能需要安裝 gcc-multilib,以解決以下問題:
gcc -I include/ -I include/sys/ -m32 -c -fno-builtin -fno-stack-protector -o lib/klib.o lib/klib.c
In file included from /usr/include/elf.h:24:0,
                 from lib/klib.c:24:
/usr/include/features.h:324:26: 嚴重錯誤: bits/predefs.h:沒有此一檔案或目錄

執行畫面

目錄結構

.
├── 80m.img
├── 80m.vdi
├── a.img
├── boot
│   ├── boot.asm
│   ├── include
│   │   ├── fat12hdr.inc
│   │   ├── load.inc
│   │   └── pm.inc
│   └── loader.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
│   ├── main.c
│   ├── proc.c
│   ├── protect.c
│   ├── start.c
│   ├── systask.c
│   └── tty.c
├── lib
│   ├── close.c
│   ├── exit.c
│   ├── fork.c
│   ├── getpid.c
│   ├── kliba.asm
│   ├── klib.c
│   ├── misc.c
│   ├── open.c
│   ├── printf.c
│   ├── read.c
│   ├── string.asm
│   ├── syscall.asm
│   ├── syslog.c
│   ├── unlink.c
│   ├── vsprintf.c
│   ├── wait.c
│   └── write.c
├── Makefile
└── mm
    ├── 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 $...