exec 係用以將現在的處理序映射替換成另一個。亦即我們可從硬碟讀取另一個可執行檔,用它替換掉剛剛被 fork 出來的子處理序。
為測試 exec,勢必先有一個簡單的可執行檔(echo)。因此,我們將製作一個類似於 C 的 Run-time:把過去可供給應用程式使用的函數單獨連結成一個檔,以後直接連結即可。 目前包括:
最後,想「安裝」一些應用程式到我們的檔案系統中,有如下工作:
為測試 exec,勢必先有一個簡單的可執行檔(echo)。因此,我們將製作一個類似於 C 的 Run-time:把過去可供給應用程式使用的函數單獨連結成一個檔,以後直接連結即可。 目前包括:
- 兩個真正的系統使用 sendrec 和 printx
- lib/syscall.asm
- 字串操作 memcopy、memset、strcpy、strlen
- lib/string.asm
- FS 的介面
- lib/open.c
- lib/read.c
- lib/write.c
- lib/close.c
- lib/unlink.c
- MM 的介面
- lib/fork.c
- lib/exit.c
- lib/wait.c
- SYS 的介面
- lib/getpid.c
- 其他
- lib/misc.c
- lib/vsprintf.c
- lib/printf.c
ar rcs lib/orangescrt.a lib/syscall.asm lib/string.asm \ lib/open.c lib/read.c lib/write.c lib/close.c lib/unlink.c \ lib/fork.c lib/exit.c lib/wait.c \ lib/getpid.c \ lib/misc.c lib/vsprintf.c lib/printf.c並寫一個最簡單的 echo(與 pwd)。
最後,想「安裝」一些應用程式到我們的檔案系統中,有如下工作:
- 編寫應用程式,並編譯連結
- 將連結好的應用程式打成一個 tar 包:inst.tar
- 將 inst.tar 用工具 dd 寫入磁片(映射)的某段特定磁區(假設這一段的第一個扇區的磁區號為 X)
- 啟動系統,這時 mkfs() 會在檔案系統中建立一個新檔 cmd.tar,它的 inode 中的 i_start_sect 成員會被設為 X
- 在某個處理序中-比如 Init-將 cmd.tar 解包,將其中包含的檔存入文件系統
程式碼
command/echo.c
#include "stdio.h"
int main(int argc, char * argv[])
{
int i;
for (i = 1; i < argc; i++)
printf("%s%s", i == 1 ? "" : " ", argv[i]);
printf("\n");
return 0;
}
command/Makefile
# commands/Makefile
#ENTRYPOINT = 0x1000
HD = ../80m.img
ASM = nasm
DASM = objdump
CC = gcc
LD = ld
ASMFLAGS = -I ../include/ -f elf
CFLAGS = -I ../include/ -c -fno-builtin -fno-stack-protector -Wall
LDFLAGS = -Ttext 0x1000
DASMFLAGS = -D
LIB = ../lib/orangescrt.a
BIN = echo pwd
# All Phony Targets
.PHONY : everything final clean realclean disasm all install
# Default starting position
everything : $(BIN)
install : all clean
cp ../kernel.bin ./ -v
tar vcf inst.tar kernel.bin $(BIN)
dd if=inst.tar of=$(HD) seek=`echo "obase=10;ibase=16;(\`egrep -e '^ROOT_BASE' ../boot/include/load.inc | sed -e 's/.*0x//g'\`+\`egrep -e '#define[[:space:]]*INSTALL_START_SECT' ../include/sys/config.h | sed -e 's/.*0x//g'\`)*200" | bc` bs=1 count=`ls -l inst.tar | awk -F " " '{print $$5}'` conv=notrunc
all : realclean everything
final : all clean
clean :
rm -f *.o
realclean :
rm -f $(BIN) *.o
kernel.bin :
cp ../kernel.bin ./
start.o : start.asm
$(ASM) $(ASMFLAGS) -o $@ $<
echo.o: echo.c ../include/type.h ../include/stdio.h
$(CC) $(CFLAGS) -o $@ $<
echo : echo.o start.o $(LIB)
$(LD) $(LDFLAGS) -o $@ $?
pwd.o: pwd.c ../include/type.h ../include/stdio.h
$(CC) $(CFLAGS) -o $@ $<
pwd : pwd.o start.o $(LIB)
$(LD) $(LDFLAGS) -o $@ $?
command/pwd.c
#include "type.h"
#include "stdio.h"
int main(int argc, char * argv[])
{
printf("/\n");
return 0;
}
command/start.asm
;; start.asm extern main extern exit bits 32 [section .text] global _start _start: push eax push ecx call main ;; need not clean up the stack here push eax call exit hlt ; should never arrive here
kernel/main.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"
/*****************************************************************************
* kernel_main
*****************************************************************************/
/**
* jmp from kernel.asm::_start.
*
*****************************************************************************/
PUBLIC int kernel_main()
{
disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
int i, j, eflags, prio;
u8 rpl;
u8 priv; /* privilege */
struct task * t;
struct proc * p = proc_table;
char * stk = task_stack + STACK_SIZE_TOTAL;
for (i = 0; i < NR_TASKS + NR_PROCS; i++,p++,t++) {
if (i >= NR_TASKS + NR_NATIVE_PROCS) {
p->p_flags = FREE_SLOT;
continue;
}
if (i < NR_TASKS) { /* TASK */
t = task_table + i;
priv = PRIVILEGE_TASK;
rpl = RPL_TASK;
eflags = 0x1202;/* IF=1, IOPL=1, bit 2 is always 1 */
prio = 15;
}
else { /* USER PROC */
t = user_proc_table + (i - NR_TASKS);
priv = PRIVILEGE_USER;
rpl = RPL_USER;
eflags = 0x202; /* IF=1, bit 2 is always 1 */
prio = 5;
}
strcpy(p->name, t->name); /* name of the process */
p->p_parent = NO_TASK;
if (strcmp(t->name, "INIT") != 0) {
p->ldts[INDEX_LDT_C] = gdt[SELECTOR_KERNEL_CS >> 3];
p->ldts[INDEX_LDT_RW] = gdt[SELECTOR_KERNEL_DS >> 3];
/* change the DPLs */
p->ldts[INDEX_LDT_C].attr1 = DA_C | priv << 5;
p->ldts[INDEX_LDT_RW].attr1 = DA_DRW | priv << 5;
}
else { /* INIT process */
unsigned int k_base;
unsigned int k_limit;
int ret = get_kernel_map(&k_base, &k_limit);
assert(ret == 0);
init_desc(&p->ldts[INDEX_LDT_C],
0, /* bytes before the entry point
* are useless (wasted) for the
* INIT process, doesn't matter
*/
(k_base + k_limit) >> LIMIT_4K_SHIFT,
DA_32 | DA_LIMIT_4K | DA_C | priv << 5);
init_desc(&p->ldts[INDEX_LDT_RW],
0, /* bytes before the entry point
* are useless (wasted) for the
* INIT process, doesn't matter
*/
(k_base + k_limit) >> LIMIT_4K_SHIFT,
DA_32 | DA_LIMIT_4K | DA_DRW | priv << 5);
}
p->regs.cs = INDEX_LDT_C << 3 | SA_TIL | rpl;
p->regs.ds =
p->regs.es =
p->regs.fs =
p->regs.ss = INDEX_LDT_RW << 3 | SA_TIL | rpl;
p->regs.gs = (SELECTOR_KERNEL_GS & SA_RPL_MASK) | rpl;
p->regs.eip = (u32)t->initial_eip;
p->regs.esp = (u32)stk;
p->regs.eflags = eflags;
p->ticks = p->priority = prio;
p->p_flags = 0;
p->p_msg = 0;
p->p_recvfrom = NO_TASK;
p->p_sendto = NO_TASK;
p->has_int_msg = 0;
p->q_sending = 0;
p->next_sending = 0;
for (j = 0; j < NR_FILES; j++)
p->filp[j] = 0;
stk -= t->stacksize;
}
k_reenter = 0;
ticks = 0;
p_proc_ready = proc_table;
init_clock();
init_keyboard();
restart();
while(1){}
}
/*****************************************************************************
* get_ticks
*****************************************************************************/
PUBLIC int get_ticks()
{
MESSAGE msg;
reset_msg(&msg);
msg.type = GET_TICKS;
send_recv(BOTH, TASK_SYS, &msg);
return msg.RETVAL;
}
/**
* @struct posix_tar_header
* Borrowed from GNU `tar'
*/
struct posix_tar_header
{ /* byte offset */
char name[100]; /* 0 */
char mode[8]; /* 100 */
char uid[8]; /* 108 */
char gid[8]; /* 116 */
char size[12]; /* 124 */
char mtime[12]; /* 136 */
char chksum[8]; /* 148 */
char typeflag; /* 156 */
char linkname[100]; /* 157 */
char magic[6]; /* 257 */
char version[2]; /* 263 */
char uname[32]; /* 265 */
char gname[32]; /* 297 */
char devmajor[8]; /* 329 */
char devminor[8]; /* 337 */
char prefix[155]; /* 345 */
/* 500 */
};
/*****************************************************************************
* untar
*****************************************************************************/
/**
* Extract the tar file and store them.
*
* @param filename The tar file.
*****************************************************************************/
void untar(const char * filename)
{
printf("[extract `%s'\n", filename);
int fd = open(filename, O_RDWR);
assert(fd != -1);
char buf[SECTOR_SIZE * 16];
int chunk = sizeof(buf);
while (1) {
read(fd, buf, SECTOR_SIZE);
if (buf[0] == 0)
break;
struct posix_tar_header * phdr = (struct posix_tar_header *)buf;
/* calculate the file size */
char * p = phdr->size;
int f_len = 0;
while (*p)
f_len = (f_len * 8) + (*p++ - '0'); /* octal */
int bytes_left = f_len;
int fdout = open(phdr->name, O_CREAT | O_RDWR);
if (fdout == -1) {
printf(" failed to extract file: %s\n", phdr->name);
printf(" aborted]");
return;
}
printf(" %s (%d bytes)\n", phdr->name, f_len);
while (bytes_left) {
int iobytes = min(chunk, bytes_left);
read(fd, buf,
((iobytes - 1) / SECTOR_SIZE + 1) * SECTOR_SIZE);
write(fdout, buf, iobytes);
bytes_left -= iobytes;
}
close(fdout);
}
close(fd);
printf(" done]\n");
}
/*****************************************************************************
* Init
*****************************************************************************/
/**
* The hen.
*
*****************************************************************************/
void Init()
{
int fd_stdin = open("/dev_tty0", O_RDWR);
assert(fd_stdin == 0);
int fd_stdout = open("/dev_tty0", O_RDWR);
assert(fd_stdout == 1);
printf("Init() is running ...\n");
/* extract `cmd.tar' */
untar("/cmd.tar");
int pid = fork();
if (pid != 0) { /* parent process */
int s;
int child = wait(&s);
printf("child (%d) exited with status: %d.\n", child, s);
}
else { /* child process */
execl("/echo", "echo", "hello", "world", 0);
}
while (1) {
int s;
int child = wait(&s);
printf("child (%d) exited with status: %d.\n", child, s);
}
assert(0);
}
/*======================================================================*
TestA
*======================================================================*/
void TestA()
{
for(;;);
}
/*======================================================================*
TestB
*======================================================================*/
void TestB()
{
for(;;);
}
/*======================================================================*
TestB
*======================================================================*/
void TestC()
{
for(;;);
}
/*****************************************************************************
* panic
*****************************************************************************/
PUBLIC void panic(const char *fmt, ...)
{
int i;
char buf[256];
/* 4 is the size of fmt in the stack */
va_list arg = (va_list)((char*)&fmt + 4);
i = vsprintf(buf, fmt, arg);
printl("%c !!panic!! %s", MAG_CH_PANIC, buf);
/* should never arrive here */
__asm__ __volatile__("ud2");
}
kernel/proc.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
proc.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "tty.h"
#include "console.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "global.h"
#include "proto.h"
PRIVATE void block(struct proc* p);
PRIVATE void unblock(struct proc* p);
PRIVATE int msg_send(struct proc* current, int dest, MESSAGE* m);
PRIVATE int msg_receive(struct proc* current, int src, MESSAGE* m);
PRIVATE int deadlock(int src, int dest);
/*****************************************************************************
* schedule
*****************************************************************************/
/**
* <Ring 0> Choose one proc to run.
*
*****************************************************************************/
PUBLIC void schedule()
{
struct proc* p;
int greatest_ticks = 0;
while (!greatest_ticks) {
for (p = &FIRST_PROC; p <= &LAST_PROC; p++) {
if (p->p_flags == 0) {
if (p->ticks > greatest_ticks) {
greatest_ticks = p->ticks;
p_proc_ready = p;
}
}
}
if (!greatest_ticks)
for (p = &FIRST_PROC; p <= &LAST_PROC; p++)
if (p->p_flags == 0)
p->ticks = p->priority;
}
}
/*****************************************************************************
* sys_sendrec
*****************************************************************************/
/**
* <Ring 0> The core routine of system call `sendrec()'.
*
* @param function SEND or RECEIVE
* @param src_dest To/From whom the message is transferred.
* @param m Ptr to the MESSAGE body.
* @param p The caller proc.
*
* @return Zero if success.
*****************************************************************************/
PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p)
{
assert(k_reenter == 0); /* make sure we are not in ring0 */
assert((src_dest >= 0 && src_dest < NR_TASKS + NR_PROCS) ||
src_dest == ANY ||
src_dest == INTERRUPT);
int ret = 0;
int caller = proc2pid(p);
MESSAGE* mla = (MESSAGE*)va2la(caller, m);
mla->source = caller;
assert(mla->source != src_dest);
/**
* Actually we have the third message type: BOTH. However, it is not
* allowed to be passed to the kernel directly. Kernel doesn't know
* it at all. It is transformed into a SEND followed by a RECEIVE
* by `send_recv()'.
*/
if (function == SEND) {
ret = msg_send(p, src_dest, m);
if (ret != 0)
return ret;
}
else if (function == RECEIVE) {
ret = msg_receive(p, src_dest, m);
if (ret != 0)
return ret;
}
else {
panic("{sys_sendrec} invalid function: "
"%d (SEND:%d, RECEIVE:%d).", function, SEND, RECEIVE);
}
return 0;
}
/*****************************************************************************
* ldt_seg_linear
*****************************************************************************/
/**
* <Ring 0~1> Calculate the linear address of a certain segment of a given
* proc.
*
* @param p Whose (the proc ptr).
* @param idx Which (one proc has more than one segments).
*
* @return The required linear address.
*****************************************************************************/
PUBLIC int ldt_seg_linear(struct proc* p, int idx)
{
struct descriptor * d = &p->ldts[idx];
return d->base_high << 24 | d->base_mid << 16 | d->base_low;
}
/*****************************************************************************
* va2la
*****************************************************************************/
/**
* <Ring 0~1> Virtual addr --> Linear addr.
*
* @param pid PID of the proc whose address is to be calculated.
* @param va Virtual address.
*
* @return The linear address for the given virtual address.
*****************************************************************************/
PUBLIC void* va2la(int pid, void* va)
{
struct proc* p = &proc_table[pid];
u32 seg_base = ldt_seg_linear(p, INDEX_LDT_RW);
u32 la = seg_base + (u32)va;
if (pid < NR_TASKS + NR_NATIVE_PROCS) {
assert(la == (u32)va);
}
return (void*)la;
}
/*****************************************************************************
* reset_msg
*****************************************************************************/
/**
* <Ring 0~3> Clear up a MESSAGE by setting each byte to 0.
*
* @param p The message to be cleared.
*****************************************************************************/
PUBLIC void reset_msg(MESSAGE* p)
{
memset(p, 0, sizeof(MESSAGE));
}
/*****************************************************************************
* block
*****************************************************************************/
/**
* <Ring 0> This routine is called after `p_flags' has been set (!= 0), it
* calls `schedule()' to choose another proc as the `proc_ready'.
*
* @attention This routine does not change `p_flags'. Make sure the `p_flags'
* of the proc to be blocked has been set properly.
*
* @param p The proc to be blocked.
*****************************************************************************/
PRIVATE void block(struct proc* p)
{
assert(p->p_flags);
schedule();
}
/*****************************************************************************
* unblock
*****************************************************************************/
/**
* <Ring 0> This is a dummy routine. It does nothing actually. When it is
* called, the `p_flags' should have been cleared (== 0).
*
* @param p The unblocked proc.
*****************************************************************************/
PRIVATE void unblock(struct proc* p)
{
assert(p->p_flags == 0);
}
/*****************************************************************************
* deadlock
*****************************************************************************/
/**
* <Ring 0> Check whether it is safe to send a message from src to dest.
* The routine will detect if the messaging graph contains a cycle. For
* instance, if we have procs trying to send messages like this:
* A -> B -> C -> A, then a deadlock occurs, because all of them will
* wait forever. If no cycles detected, it is considered as safe.
*
* @param src Who wants to send message.
* @param dest To whom the message is sent.
*
* @return Zero if success.
*****************************************************************************/
PRIVATE int deadlock(int src, int dest)
{
struct proc* p = proc_table + dest;
while (1) {
if (p->p_flags & SENDING) {
if (p->p_sendto == src) {
/* print the chain */
p = proc_table + dest;
printl("=_=%s", p->name);
do {
assert(p->p_msg);
p = proc_table + p->p_sendto;
printl("->%s", p->name);
} while (p != proc_table + src);
printl("=_=");
return 1;
}
p = proc_table + p->p_sendto;
}
else {
break;
}
}
return 0;
}
/*****************************************************************************
* msg_send
*****************************************************************************/
/**
* <Ring 0> Send a message to the dest proc. If dest is blocked waiting for
* the message, copy the message to it and unblock dest. Otherwise the caller
* will be blocked and appended to the dest's sending queue.
*
* @param current The caller, the sender.
* @param dest To whom the message is sent.
* @param m The message.
*
* @return Zero if success.
*****************************************************************************/
PRIVATE int msg_send(struct proc* current, int dest, MESSAGE* m)
{
struct proc* sender = current;
struct proc* p_dest = proc_table + dest; /* proc dest */
assert(proc2pid(sender) != dest);
/* check for deadlock here */
if (deadlock(proc2pid(sender), dest)) {
panic(">>DEADLOCK<< %s->%s", sender->name, p_dest->name);
}
if ((p_dest->p_flags & RECEIVING) && /* dest is waiting for the msg */
(p_dest->p_recvfrom == proc2pid(sender) ||
p_dest->p_recvfrom == ANY)) {
assert(p_dest->p_msg);
assert(m);
phys_copy(va2la(dest, p_dest->p_msg),
va2la(proc2pid(sender), m),
sizeof(MESSAGE));
p_dest->p_msg = 0;
p_dest->p_flags &= ~RECEIVING; /* dest has received the msg */
p_dest->p_recvfrom = NO_TASK;
unblock(p_dest);
assert(p_dest->p_flags == 0);
assert(p_dest->p_msg == 0);
assert(p_dest->p_recvfrom == NO_TASK);
assert(p_dest->p_sendto == NO_TASK);
assert(sender->p_flags == 0);
assert(sender->p_msg == 0);
assert(sender->p_recvfrom == NO_TASK);
assert(sender->p_sendto == NO_TASK);
}
else { /* dest is not waiting for the msg */
sender->p_flags |= SENDING;
assert(sender->p_flags == SENDING);
sender->p_sendto = dest;
sender->p_msg = m;
/* append to the sending queue */
struct proc * p;
if (p_dest->q_sending) {
p = p_dest->q_sending;
while (p->next_sending)
p = p->next_sending;
p->next_sending = sender;
}
else {
p_dest->q_sending = sender;
}
sender->next_sending = 0;
block(sender);
assert(sender->p_flags == SENDING);
assert(sender->p_msg != 0);
assert(sender->p_recvfrom == NO_TASK);
assert(sender->p_sendto == dest);
}
return 0;
}
/*****************************************************************************
* msg_receive
*****************************************************************************/
/**
* <Ring 0> Try to get a message from the src proc. If src is blocked sending
* the message, copy the message from it and unblock src. Otherwise the caller
* will be blocked.
*
* @param current The caller, the proc who wanna receive.
* @param src From whom the message will be received.
* @param m The message ptr to accept the message.
*
* @return Zero if success.
*****************************************************************************/
PRIVATE int msg_receive(struct proc* current, int src, MESSAGE* m)
{
struct proc* p_who_wanna_recv = current; /**
* This name is a little bit
* wierd, but it makes me
* think clearly, so I keep
* it.
*/
struct proc* p_from = 0; /* from which the message will be fetched */
struct proc* prev = 0;
int copyok = 0;
assert(proc2pid(p_who_wanna_recv) != src);
if ((p_who_wanna_recv->has_int_msg) &&
((src == ANY) || (src == INTERRUPT))) {
/* There is an interrupt needs p_who_wanna_recv's handling and
* p_who_wanna_recv is ready to handle it.
*/
MESSAGE msg;
reset_msg(&msg);
msg.source = INTERRUPT;
msg.type = HARD_INT;
assert(m);
phys_copy(va2la(proc2pid(p_who_wanna_recv), m), &msg,
sizeof(MESSAGE));
p_who_wanna_recv->has_int_msg = 0;
assert(p_who_wanna_recv->p_flags == 0);
assert(p_who_wanna_recv->p_msg == 0);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->has_int_msg == 0);
return 0;
}
/* Arrives here if no interrupt for p_who_wanna_recv. */
if (src == ANY) {
/* p_who_wanna_recv is ready to receive messages from
* ANY proc, we'll check the sending queue and pick the
* first proc in it.
*/
if (p_who_wanna_recv->q_sending) {
p_from = p_who_wanna_recv->q_sending;
copyok = 1;
assert(p_who_wanna_recv->p_flags == 0);
assert(p_who_wanna_recv->p_msg == 0);
assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->q_sending != 0);
assert(p_from->p_flags == SENDING);
assert(p_from->p_msg != 0);
assert(p_from->p_recvfrom == NO_TASK);
assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
}
}
else {
/* p_who_wanna_recv wants to receive a message from
* a certain proc: src.
*/
p_from = &proc_table[src];
if ((p_from->p_flags & SENDING) &&
(p_from->p_sendto == proc2pid(p_who_wanna_recv))) {
/* Perfect, src is sending a message to
* p_who_wanna_recv.
*/
copyok = 1;
struct proc* p = p_who_wanna_recv->q_sending;
assert(p); /* p_from must have been appended to the
* queue, so the queue must not be NULL
*/
while (p) {
assert(p_from->p_flags & SENDING);
if (proc2pid(p) == src) { /* if p is the one */
p_from = p;
break;
}
prev = p;
p = p->next_sending;
}
assert(p_who_wanna_recv->p_flags == 0);
assert(p_who_wanna_recv->p_msg == 0);
assert(p_who_wanna_recv->p_recvfrom == NO_TASK);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->q_sending != 0);
assert(p_from->p_flags == SENDING);
assert(p_from->p_msg != 0);
assert(p_from->p_recvfrom == NO_TASK);
assert(p_from->p_sendto == proc2pid(p_who_wanna_recv));
}
}
if (copyok) {
/* It's determined from which proc the message will
* be copied. Note that this proc must have been
* waiting for this moment in the queue, so we should
* remove it from the queue.
*/
if (p_from == p_who_wanna_recv->q_sending) { /* the 1st one */
assert(prev == 0);
p_who_wanna_recv->q_sending = p_from->next_sending;
p_from->next_sending = 0;
}
else {
assert(prev);
prev->next_sending = p_from->next_sending;
p_from->next_sending = 0;
}
assert(m);
assert(p_from->p_msg);
/* copy the message */
phys_copy(va2la(proc2pid(p_who_wanna_recv), m),
va2la(proc2pid(p_from), p_from->p_msg),
sizeof(MESSAGE));
p_from->p_msg = 0;
p_from->p_sendto = NO_TASK;
p_from->p_flags &= ~SENDING;
unblock(p_from);
}
else { /* nobody's sending any msg */
/* Set p_flags so that p_who_wanna_recv will not
* be scheduled until it is unblocked.
*/
p_who_wanna_recv->p_flags |= RECEIVING;
p_who_wanna_recv->p_msg = m;
if (src == ANY)
p_who_wanna_recv->p_recvfrom = ANY;
else
p_who_wanna_recv->p_recvfrom = proc2pid(p_from);
block(p_who_wanna_recv);
assert(p_who_wanna_recv->p_flags == RECEIVING);
assert(p_who_wanna_recv->p_msg != 0);
assert(p_who_wanna_recv->p_recvfrom != NO_TASK);
assert(p_who_wanna_recv->p_sendto == NO_TASK);
assert(p_who_wanna_recv->has_int_msg == 0);
}
return 0;
}
/*****************************************************************************
* inform_int
*****************************************************************************/
/**
* <Ring 0> Inform a proc that an interrupt has occured.
*
* @param task_nr The task which will be informed.
*****************************************************************************/
PUBLIC void inform_int(int task_nr)
{
struct proc* p = proc_table + task_nr;
if ((p->p_flags & RECEIVING) && /* dest is waiting for the msg */
((p->p_recvfrom == INTERRUPT) || (p->p_recvfrom == ANY))) {
p->p_msg->source = INTERRUPT;
p->p_msg->type = HARD_INT;
p->p_msg = 0;
p->has_int_msg = 0;
p->p_flags &= ~RECEIVING; /* dest has received the msg */
p->p_recvfrom = NO_TASK;
assert(p->p_flags == 0);
unblock(p);
assert(p->p_flags == 0);
assert(p->p_msg == 0);
assert(p->p_recvfrom == NO_TASK);
assert(p->p_sendto == NO_TASK);
}
else {
p->has_int_msg = 1;
}
}
/*****************************************************************************
* dump_proc
*****************************************************************************/
PUBLIC void dump_proc(struct proc* p)
{
char info[STR_DEFAULT_LEN];
int i;
int text_color = MAKE_COLOR(GREEN, RED);
int dump_len = sizeof(struct proc);
out_byte(CRTC_ADDR_REG, START_ADDR_H);
out_byte(CRTC_DATA_REG, 0);
out_byte(CRTC_ADDR_REG, START_ADDR_L);
out_byte(CRTC_DATA_REG, 0);
sprintf(info, "byte dump of proc_table[%d]:\n", p - proc_table); disp_color_str(info, text_color);
for (i = 0; i < dump_len; i++) {
sprintf(info, "%x.", ((unsigned char *)p)[i]);
disp_color_str(info, text_color);
}
/* printl("^^"); */
disp_color_str("\n\n", text_color);
sprintf(info, "ANY: 0x%x.\n", ANY); disp_color_str(info, text_color);
sprintf(info, "NO_TASK: 0x%x.\n", NO_TASK); disp_color_str(info, text_color);
disp_color_str("\n", text_color);
sprintf(info, "ldt_sel: 0x%x. ", p->ldt_sel); disp_color_str(info, text_color);
sprintf(info, "ticks: 0x%x. ", p->ticks); disp_color_str(info, text_color);
sprintf(info, "priority: 0x%x. ", p->priority); disp_color_str(info, text_color);
/* sprintf(info, "pid: 0x%x. ", p->pid); disp_color_str(info, text_color); */
sprintf(info, "name: %s. ", p->name); disp_color_str(info, text_color);
disp_color_str("\n", text_color);
sprintf(info, "p_flags: 0x%x. ", p->p_flags); disp_color_str(info, text_color);
sprintf(info, "p_recvfrom: 0x%x. ", p->p_recvfrom); disp_color_str(info, text_color);
sprintf(info, "p_sendto: 0x%x. ", p->p_sendto); disp_color_str(info, text_color);
/* sprintf(info, "nr_tty: 0x%x. ", p->nr_tty); disp_color_str(info, text_color); */
disp_color_str("\n", text_color);
sprintf(info, "has_int_msg: 0x%x. ", p->has_int_msg); disp_color_str(info, text_color);
}
/*****************************************************************************
* dump_msg
*****************************************************************************/
PUBLIC void dump_msg(const char * title, MESSAGE* m)
{
int packed = 0;
printl("{%s}<0x%x>{%ssrc:%s(%d),%stype:%d,%s(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)%s}%s", //, (0x%x, 0x%x, 0x%x)}",
title,
(int)m,
packed ? "" : "\n ",
proc_table[m->source].name,
m->source,
packed ? " " : "\n ",
m->type,
packed ? " " : "\n ",
m->u.m3.m3i1,
m->u.m3.m3i2,
m->u.m3.m3i3,
m->u.m3.m3i4,
(int)m->u.m3.m3p1,
(int)m->u.m3.m3p2,
packed ? "" : "\n",
packed ? "" : "\n"/* , */
);
}
include/stdio.h
/*************************************************************************//**
*****************************************************************************
* @file stdio.h
* @brief
* @author Forrest Y. Yu
* @date 2008
*****************************************************************************
*****************************************************************************/
#ifndef _ORANGES_STDIO_H_
#define _ORANGES_STDIO_H_
#include "type.h"
/* the assert macro */
#define ASSERT
#ifdef ASSERT
void assertion_failure(char *exp, char *file, char *base_file, int line);
#define assert(exp) if (exp) ; \
else assertion_failure(#exp, __FILE__, __BASE_FILE__, __LINE__)
#else
#define assert(exp)
#endif
/* EXTERN */
#define EXTERN extern /* EXTERN is defined as extern except in global.c */
/* string */
#define STR_DEFAULT_LEN 1024
#define O_CREAT 1
#define O_RDWR 2
#define SEEK_SET 1
#define SEEK_CUR 2
#define SEEK_END 3
#define MAX_PATH 128
/**
* @struct stat
* @brief File status, returned by syscall stat();
*/
struct stat {
int st_dev; /* major/minor device number */
int st_ino; /* i-node number */
int st_mode; /* file mode, protection bits, etc. */
int st_rdev; /* device ID (if special file) */
int st_size; /* file size */
};
/**
* @struct time
* @brief RTC time from CMOS.
*/
struct time {
u32 year;
u32 month;
u32 day;
u32 hour;
u32 minute;
u32 second;
};
#define BCD_TO_DEC(x) ( (x >> 4) * 10 + (x & 0x0f) )
/*========================*
* printf, printl, printx *
*========================*
*
* printf:
*
* [send msg] WRITE DEV_WRITE
* USER_PROC ------------→ FS -------------→ TTY
* ↖______________↙↖_______________/
* [recv msg] SYSCALL_RET SYSCALL_RET
*
*----------------------------------------------------------------------
*
* printl: variant-parameter-version printx
*
* calls vsprintf, then printx (trap into kernel directly)
*
*----------------------------------------------------------------------
*
* printx: low level print without using IPC
*
* trap directly
* USER_PROC -- -- -- -- -- --> KERNEL
*
*
*----------------------------------------------------------------------
*/
/* printf.c */
PUBLIC int printf(const char *fmt, ...);
PUBLIC int printl(const char *fmt, ...);
/* vsprintf.c */
PUBLIC int vsprintf(char *buf, const char *fmt, va_list args);
PUBLIC int sprintf(char *buf, const char *fmt, ...);
/*--------*/
/* 庫函數 */
/*--------*/
#ifdef ENABLE_DISK_LOG
#define SYSLOG syslog
#endif
/* lib/open.c */
PUBLIC int open (const char *pathname, int flags);
/* lib/close.c */
PUBLIC int close (int fd);
/* lib/read.c */
PUBLIC int read (int fd, void *buf, int count);
/* lib/write.c */
PUBLIC int write (int fd, const void *buf, int count);
/* lib/unlink.c */
PUBLIC int unlink (const char *pathname);
/* lib/getpid.c */
PUBLIC int getpid ();
/* lib/fork.c */
PUBLIC int fork ();
/* lib/exit.c */
PUBLIC void exit (int status);
/* lib/wait.c */
PUBLIC int wait (int * status);
/* lib/exec.c */
PUBLIC int exec (const char * path);
PUBLIC int execl (const char * path, const char *arg, ...);
PUBLIC int execv (const char * path, char * argv[]);
/* lib/stat.c */
PUBLIC int stat (const char *path, struct stat *buf);
/* lib/syslog.c */
PUBLIC int syslog (const char *fmt, ...);
#endif /* _ORANGES_STDIO_H_ */
include/sys/config.h
/*************************************************************************//** ***************************************************************************** * @file config.h * @brief * @author Forrest Y. Yu * @date 2008 ***************************************************************************** *****************************************************************************/ /** * Some sector are reserved for us (the gods of the os) to copy a tar file * there, which will be extracted and used by the OS. * * @attention INSTALL_NR_SECTS should be a multiple of NR_DEFAULT_FILE_SECTS: * INSTALL_NR_SECTS = n * NR_DEFAULT_FILE_SECTS (n=1,2,3,...) */ #define INSTALL_START_SECT 0x8000 #define INSTALL_NR_SECTS 0x800 /** * boot parameters are stored by the loader, they should be * there when kernel is running and should not be overwritten * since kernel might use them at any time. */ #define BOOT_PARAM_ADDR 0x900 /* physical address */ #define BOOT_PARAM_MAGIC 0xB007 /* magic number */ #define BI_MAG 0 #define BI_MEM_SIZE 1 #define BI_KERNEL_FILE 2 /** * corresponding with boot/include/load.inc::ROOT_BASE, which should * be changed if this macro is changed. */ #define MINOR_BOOT MINOR_hd2a /* * disk log */ #define ENABLE_DISK_LOG #define SET_LOG_SECT_SMAP_AT_STARTUP #define MEMSET_LOG_SECTS #define NR_SECTS_FOR_LOG NR_DEFAULT_FILE_SECTS
include/sys/const.h
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
const.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#ifndef _ORANGES_CONST_H_
#define _ORANGES_CONST_H_
/* max() & min() */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
/* Color */
/*
* e.g. MAKE_COLOR(BLUE, RED)
* MAKE_COLOR(BLACK, RED) | BRIGHT
* MAKE_COLOR(BLACK, RED) | BRIGHT | FLASH
*/
#define BLACK 0x0 /* 0000 */
#define WHITE 0x7 /* 0111 */
#define RED 0x4 /* 0100 */
#define GREEN 0x2 /* 0010 */
#define BLUE 0x1 /* 0001 */
#define FLASH 0x80 /* 1000 0000 */
#define BRIGHT 0x08 /* 0000 1000 */
#define MAKE_COLOR(x,y) ((x<<4) | y) /* MAKE_COLOR(Background,Foreground) */
/* GDT 和 IDT 中描述符的個數 */
#define GDT_SIZE 128
#define IDT_SIZE 256
/* 權限 */
#define PRIVILEGE_KRNL 0
#define PRIVILEGE_TASK 1
#define PRIVILEGE_USER 3
/* RPL */
#define RPL_KRNL SA_RPL0
#define RPL_TASK SA_RPL1
#define RPL_USER SA_RPL3
/* Process */
#define SENDING 0x02 /* set when proc trying to send */
#define RECEIVING 0x04 /* set when proc trying to recv */
#define WAITING 0x08 /* set when proc waiting for the child to terminate */
#define HANGING 0x10 /* set when proc exits without being waited by parent */
#define FREE_SLOT 0x20 /* set when proc table entry is not used
* (ok to allocated to a new process)
*/
/* TTY */
#define NR_CONSOLES 3 /* consoles */
/* 8259A interrupt controller ports. */
#define INT_M_CTL 0x20 /* I/O port for interrupt controller <Master> */
#define INT_M_CTLMASK 0x21 /* setting bits in this port disables ints <Master> */
#define INT_S_CTL 0xA0 /* I/O port for second interrupt controller <Slave> */
#define INT_S_CTLMASK 0xA1 /* setting bits in this port disables ints <Slave> */
/* 8253/8254 PIT (Programmable Interval Timer) */
#define TIMER0 0x40 /* I/O port for timer channel 0 */
#define TIMER_MODE 0x43 /* I/O port for timer mode control */
#define RATE_GENERATOR 0x34 /* 00-11-010-0 :
* Counter0 - LSB then MSB - rate generator - binary
*/
#define TIMER_FREQ 1193182L/* clock frequency for timer in PC and AT */
#define HZ 100 /* clock freq (software settable on IBM-PC) */
/* AT keyboard */
/* 8042 ports */
#define KB_DATA 0x60 /* I/O port for keyboard data
Read : Read Output Buffer
Write: Write Input Buffer(8042 Data&8048 Command) */
#define KB_CMD 0x64 /* I/O port for keyboard command
Read : Read Status Register
Write: Write Input Buffer(8042 Command) */
#define LED_CODE 0xED
#define KB_ACK 0xFA
/* VGA */
#define CRTC_ADDR_REG 0x3D4 /* CRT Controller Registers - Addr Register */
#define CRTC_DATA_REG 0x3D5 /* CRT Controller Registers - Data Register */
#define START_ADDR_H 0xC /* reg index of video mem start addr (MSB) */
#define START_ADDR_L 0xD /* reg index of video mem start addr (LSB) */
#define CURSOR_H 0xE /* reg index of cursor position (MSB) */
#define CURSOR_L 0xF /* reg index of cursor position (LSB) */
#define V_MEM_BASE 0xB8000 /* base of color video memory */
#define V_MEM_SIZE 0x8000 /* 32K: B8000H -> BFFFFH */
/* CMOS */
#define CLK_ELE 0x70 /* CMOS RAM address register port (write only)
* Bit 7 = 1 NMI disable
* 0 NMI enable
* Bits 6-0 = RAM address
*/
#define CLK_IO 0x71 /* CMOS RAM data register port (read/write) */
#define YEAR 9 /* Clock register addresses in CMOS RAM */
#define MONTH 8
#define DAY 7
#define HOUR 4
#define MINUTE 2
#define SECOND 0
#define CLK_STATUS 0x0B /* Status register B: RTC configuration */
#define CLK_HEALTH 0x0E /* Diagnostic status: (should be set by Power
* On Self-Test [POST])
* Bit 7 = RTC lost power
* 6 = Checksum (for addr 0x10-0x2d) bad
* 5 = Config. Info. bad at POST
* 4 = Mem. size error at POST
* 3 = I/O board failed initialization
* 2 = CMOS time invalid
* 1-0 = reserved
*/
/* Hardware interrupts */
#define NR_IRQ 16 /* Number of IRQs */
#define CLOCK_IRQ 0
#define KEYBOARD_IRQ 1
#define CASCADE_IRQ 2 /* cascade enable for 2nd AT controller */
#define ETHER_IRQ 3 /* default ethernet interrupt vector */
#define SECONDARY_IRQ 3 /* RS232 interrupt vector for port 2 */
#define RS232_IRQ 4 /* RS232 interrupt vector for port 1 */
#define XT_WINI_IRQ 5 /* xt winchester */
#define FLOPPY_IRQ 6 /* floppy disk */
#define PRINTER_IRQ 7
#define AT_WINI_IRQ 14 /* at winchester */
/* tasks */
/* 注意 TASK_XXX 的定義要與 global.c 中對應 */
#define INVALID_DRIVER -20
#define INTERRUPT -10
#define TASK_TTY 0
#define TASK_SYS 1
#define TASK_HD 2
#define TASK_FS 3
#define TASK_MM 4
#define INIT 5
#define ANY (NR_TASKS + NR_PROCS + 10)
#define NO_TASK (NR_TASKS + NR_PROCS + 20)
#define MAX_TICKS 0x7FFFABCD
/* system call */
#define NR_SYS_CALL 3
/* ipc */
#define SEND 1
#define RECEIVE 2
#define BOTH 3 /* BOTH = (SEND | RECEIVE) */
/* magic chars used by `printx' */
#define MAG_CH_PANIC '\002'
#define MAG_CH_ASSERT '\003'
/**
* @enum msgtype
* @brief MESSAGE types
*/
enum msgtype {
/*
* when hard interrupt occurs, a msg (with type==HARD_INT) will
* be sent to some tasks
*/
HARD_INT = 1,
/* SYS task */
GET_TICKS, GET_PID, GET_RTC_TIME,
/* FS */
OPEN, CLOSE, READ, WRITE, LSEEK, STAT, UNLINK,
/* FS & TTY */
SUSPEND_PROC, RESUME_PROC,
/* MM */
EXEC, WAIT,
/* FS & MM */
FORK, EXIT,
/* TTY, SYS, FS, MM, etc */
SYSCALL_RET,
/* message type for drivers */
DEV_OPEN = 1001,
DEV_CLOSE,
DEV_READ,
DEV_WRITE,
DEV_IOCTL
};
/* macros for messages */
#define FD u.m3.m3i1
#define PATHNAME u.m3.m3p1
#define FLAGS u.m3.m3i1
#define NAME_LEN u.m3.m3i2
#define BUF_LEN u.m3.m3i3
#define CNT u.m3.m3i2
#define REQUEST u.m3.m3i2
#define PROC_NR u.m3.m3i3
#define DEVICE u.m3.m3i4
#define POSITION u.m3.m3l1
#define BUF u.m3.m3p2
#define OFFSET u.m3.m3i2
#define WHENCE u.m3.m3i3
#define PID u.m3.m3i2
#define RETVAL u.m3.m3i1
#define STATUS u.m3.m3i1
#define DIOCTL_GET_GEO 1
/* Hard Drive */
#define SECTOR_SIZE 512
#define SECTOR_BITS (SECTOR_SIZE * 8)
#define SECTOR_SIZE_SHIFT 9
/* major device numbers (corresponding to kernel/global.c::dd_map[]) */
#define NO_DEV 0
#define DEV_FLOPPY 1
#define DEV_CDROM 2
#define DEV_HD 3
#define DEV_CHAR_TTY 4
#define DEV_SCSI 5
/* make device number from major and minor numbers */
#define MAJOR_SHIFT 8
#define MAKE_DEV(a,b) ((a << MAJOR_SHIFT) | b)
/* separate major and minor numbers from device number */
#define MAJOR(x) ((x >> MAJOR_SHIFT) & 0xFF)
#define MINOR(x) (x & 0xFF)
#define INVALID_INODE 0
#define ROOT_INODE 1
#define MAX_DRIVES 2
#define NR_PART_PER_DRIVE 4
#define NR_SUB_PER_PART 16
#define NR_SUB_PER_DRIVE (NR_SUB_PER_PART * NR_PART_PER_DRIVE)
#define NR_PRIM_PER_DRIVE (NR_PART_PER_DRIVE + 1)
/**
* @def MAX_PRIM
* Defines the max minor number of the primary partitions.
* If there are 2 disks, prim_dev ranges in hd[0-9], this macro will
* equals 9.
*/
#define MAX_PRIM (MAX_DRIVES * NR_PRIM_PER_DRIVE - 1)
#define MAX_SUBPARTITIONS (NR_SUB_PER_DRIVE * MAX_DRIVES)
/* device numbers of hard disk */
#define MINOR_hd1a 0x10
#define MINOR_hd2a (MINOR_hd1a+NR_SUB_PER_PART)
#define ROOT_DEV MAKE_DEV(DEV_HD, MINOR_BOOT)
#define P_PRIMARY 0
#define P_EXTENDED 1
#define ORANGES_PART 0x99 /* Orange'S partition */
#define NO_PART 0x00 /* unused entry */
#define EXT_PART 0x05 /* extended partition */
#define NR_FILES 64
#define NR_FILE_DESC 64 /* FIXME */
#define NR_INODE 64 /* FIXME */
#define NR_SUPER_BLOCK 8
/* INODE::i_mode (octal, lower 12 bits reserved) */
#define I_TYPE_MASK 0170000
#define I_REGULAR 0100000
#define I_BLOCK_SPECIAL 0060000
#define I_DIRECTORY 0040000
#define I_CHAR_SPECIAL 0020000
#define I_NAMED_PIPE 0010000
#define is_special(m) ((((m) & I_TYPE_MASK) == I_BLOCK_SPECIAL) || \
(((m) & I_TYPE_MASK) == I_CHAR_SPECIAL))
#define NR_DEFAULT_FILE_SECTS 2048 /* 2048 * 512 = 1MB */
#endif /* _ORANGES_CONST_H_ */
include/sys/proto.h
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
proto.h
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* kliba.asm */
PUBLIC void out_byte(u16 port, u8 value);
PUBLIC u8 in_byte(u16 port);
PUBLIC void disp_str(char * info);
PUBLIC void disp_color_str(char * info, int color);
PUBLIC void disable_irq(int irq);
PUBLIC void enable_irq(int irq);
PUBLIC void disable_int();
PUBLIC void enable_int();
PUBLIC void port_read(u16 port, void* buf, int n);
PUBLIC void port_write(u16 port, void* buf, int n);
PUBLIC void glitter(int row, int col);
/* string.asm */
PUBLIC char* strcpy(char* dst, const char* src);
/* protect.c */
PUBLIC void init_prot();
PUBLIC u32 seg2linear(u16 seg);
PUBLIC void init_desc(struct descriptor * p_desc,
u32 base, u32 limit, u16 attribute);
/* klib.c */
PUBLIC void get_boot_params(struct boot_params * pbp);
PUBLIC int get_kernel_map(unsigned int * b, unsigned int * l);
PUBLIC void delay(int time);
PUBLIC void disp_int(int input);
PUBLIC char * itoa(char * str, int num);
/* kernel.asm */
PUBLIC void restart();
/* main.c */
PUBLIC void Init();
PUBLIC int get_ticks();
PUBLIC void TestA();
PUBLIC void TestB();
PUBLIC void TestC();
PUBLIC void panic(const char *fmt, ...);
/* i8259.c */
PUBLIC void init_8259A();
PUBLIC void put_irq_handler(int irq, irq_handler handler);
PUBLIC void spurious_irq(int irq);
/* clock.c */
PUBLIC void clock_handler(int irq);
PUBLIC void init_clock();
PUBLIC void milli_delay(int milli_sec);
/* kernel/hd.c */
PUBLIC void task_hd();
PUBLIC void hd_handler(int irq);
/* keyboard.c */
PUBLIC void init_keyboard();
PUBLIC void keyboard_read(TTY* p_tty);
/* tty.c */
PUBLIC void task_tty();
PUBLIC void in_process(TTY* p_tty, u32 key);
PUBLIC void dump_tty_buf(); /* for debug only */
/* systask.c */
PUBLIC void task_sys();
/* fs/main.c */
PUBLIC void task_fs();
PUBLIC int rw_sector(int io_type, int dev, u64 pos,
int bytes, int proc_nr, void * buf);
PUBLIC struct inode * get_inode(int dev, int num);
PUBLIC void put_inode(struct inode * pinode);
PUBLIC void sync_inode(struct inode * p);
PUBLIC struct super_block * get_super_block(int dev);
/* fs/open.c */
PUBLIC int do_open();
PUBLIC int do_close();
/* fs/read_write.c */
PUBLIC int do_rdwt();
/* fs/link.c */
PUBLIC int do_unlink();
/* fs/misc.c */
PUBLIC int do_stat();
PUBLIC int strip_path(char * filename, const char * pathname,
struct inode** ppinode);
PUBLIC int search_file(char * path);
/* fs/disklog.c */
PUBLIC int do_disklog();
PUBLIC int disklog(char * logstr); /* for debug */
PUBLIC void dump_fd_graph(const char * fmt, ...);
/* mm/main.c */
PUBLIC void task_mm();
PUBLIC int alloc_mem(int pid, int memsize);
PUBLIC int free_mem(int pid);
/* mm/forkexit.c */
PUBLIC int do_fork();
PUBLIC void do_exit(int status);
PUBLIC void do_wait();
/* mm/exec.c */
PUBLIC int do_exec();
/* console.c */
PUBLIC void out_char(CONSOLE* p_con, char ch);
PUBLIC void scroll_screen(CONSOLE* p_con, int direction);
PUBLIC void select_console(int nr_console);
PUBLIC void init_screen(TTY* p_tty);
PUBLIC int is_current_console(CONSOLE* p_con);
/* proc.c */
PUBLIC void schedule();
PUBLIC void* va2la(int pid, void* va);
PUBLIC int ldt_seg_linear(struct proc* p, int idx);
PUBLIC void reset_msg(MESSAGE* p);
PUBLIC void dump_msg(const char * title, MESSAGE* m);
PUBLIC void dump_proc(struct proc * p);
PUBLIC int send_recv(int function, int src_dest, MESSAGE* msg);
PUBLIC void inform_int(int task_nr);
/* lib/misc.c */
PUBLIC void spin(char * func_name);
/* 以下是系統調用相關 */
/* 系統調用 - 系統級 */
/* proc.c */
PUBLIC int sys_sendrec(int function, int src_dest, MESSAGE* m, struct proc* p);
PUBLIC int sys_printx(int _unused1, int _unused2, char* s, struct proc * p_proc);
/* syscall.asm */
PUBLIC void sys_call(); /* int_handler */
/* 系統調用 - 用戶級 */
PUBLIC int sendrec(int function, int src_dest, MESSAGE* p_msg);
PUBLIC int printx(char* str);
lib/misc.c
/*************************************************************************//**
*****************************************************************************
* @file misc.c
* @brief
* @author Forrest Y. Yu
* @date 2008
*****************************************************************************
*****************************************************************************/
#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
/*****************************************************************************
* send_recv
*****************************************************************************/
/**
* <Ring 1~3> IPC syscall.
*
* It is an encapsulation of `sendrec',
* invoking `sendrec' directly should be avoided
*
* @param function SEND, RECEIVE or BOTH
* @param src_dest The caller's proc_nr
* @param msg Pointer to the MESSAGE struct
*
* @return always 0.
*****************************************************************************/
PUBLIC int send_recv(int function, int src_dest, MESSAGE* msg)
{
int ret = 0;
if (function == RECEIVE)
memset(msg, 0, sizeof(MESSAGE));
switch (function) {
case BOTH:
ret = sendrec(SEND, src_dest, msg);
if (ret == 0)
ret = sendrec(RECEIVE, src_dest, msg);
break;
case SEND:
case RECEIVE:
ret = sendrec(function, src_dest, msg);
break;
default:
assert((function == BOTH) ||
(function == SEND) || (function == RECEIVE));
break;
}
return ret;
}
/*****************************************************************************
* memcmp
*****************************************************************************/
/**
* Compare memory areas.
*
* @param s1 The 1st area.
* @param s2 The 2nd area.
* @param n The first n bytes will be compared.
*
* @return an integer less than, equal to, or greater than zero if the first
* n bytes of s1 is found, respectively, to be less than, to match,
* or be greater than the first n bytes of s2.
*****************************************************************************/
PUBLIC int memcmp(const void * s1, const void *s2, int n)
{
if ((s1 == 0) || (s2 == 0)) { /* for robustness */
return (s1 - s2);
}
const char * p1 = (const char *)s1;
const char * p2 = (const char *)s2;
int i;
for (i = 0; i < n; i++,p1++,p2++) {
if (*p1 != *p2) {
return (*p1 - *p2);
}
}
return 0;
}
/*****************************************************************************
* strcmp
*****************************************************************************/
/**
* Compare two strings.
*
* @param s1 The 1st string.
* @param s2 The 2nd string.
*
* @return an integer less than, equal to, or greater than zero if s1 (or the
* first n bytes thereof) is found, respectively, to be less than,
* to match, or be greater than s2.
*****************************************************************************/
PUBLIC int strcmp(const char * s1, const char *s2)
{
if ((s1 == 0) || (s2 == 0)) { /* for robustness */
return (s1 - s2);
}
const char * p1 = s1;
const char * p2 = s2;
for (; *p1 && *p2; p1++,p2++) {
if (*p1 != *p2) {
break;
}
}
return (*p1 - *p2);
}
/*****************************************************************************
* strcat
*****************************************************************************/
/**
* Concatenate two strings.
*
* @param s1 The 1st string.
* @param s2 The 2nd string.
*
* @return Ptr to the 1st string.
*****************************************************************************/
PUBLIC char * strcat(char * s1, const char *s2)
{
if ((s1 == 0) || (s2 == 0)) { /* for robustness */
return 0;
}
char * p1 = s1;
for (; *p1; p1++) {}
const char * p2 = s2;
for (; *p2; p1++,p2++) {
*p1 = *p2;
}
*p1 = 0;
return s1;
}
/*****************************************************************************
* spin
*****************************************************************************/
PUBLIC void spin(char * func_name)
{
printl("\nspinning in %s ...\n", func_name);
while (1) {}
}
/*****************************************************************************
* assertion_failure
*************************************************************************//**
* Invoked by assert().
*
* @param exp The failure expression itself.
* @param file __FILE__
* @param base_file __BASE_FILE__
* @param line __LINE__
*****************************************************************************/
PUBLIC void assertion_failure(char *exp, char *file, char *base_file, int line)
{
printl("%c assert(%s) failed: file: %s, base_file: %s, ln%d",
MAG_CH_ASSERT,
exp, file, base_file, line);
/**
* If assertion fails in a TASK, the system will halt before
* printl() returns. If it happens in a USER PROC, printl() will
* return like a common routine and arrive here.
* @see sys_printx()
*
* We use a forever loop to prevent the proc from going on:
*/
spin("assertion_failure()");
/* should never arrive here */
__asm__ __volatile__("ud2");
}
mm/forkexit.c
/*************************************************************************//**
*****************************************************************************
* @file forkexit.c
* @brief
* @author Forrest Y. Yu
* @date Tue May 6 00:37:15 2008
*****************************************************************************
*****************************************************************************/
#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
PRIVATE void cleanup(struct proc * proc);
/*****************************************************************************
* do_fork
*****************************************************************************/
/**
* Perform the fork() syscall.
*
* @return Zero if success, otherwise -1.
*****************************************************************************/
PUBLIC int do_fork()
{
/* find a free slot in proc_table */
struct proc* p = proc_table;
int i;
for (i = 0; i < NR_TASKS + NR_PROCS; i++,p++)
if (p->p_flags == FREE_SLOT)
break;
int child_pid = i;
assert(p == &proc_table[child_pid]);
assert(child_pid >= NR_TASKS + NR_NATIVE_PROCS);
if (i == NR_TASKS + NR_PROCS) /* no free slot */
return -1;
assert(i < NR_TASKS + NR_PROCS);
/* duplicate the process table */
int pid = mm_msg.source;
u16 child_ldt_sel = p->ldt_sel;
*p = proc_table[pid];
p->ldt_sel = child_ldt_sel;
p->p_parent = pid;
sprintf(p->name, "%s_%d", proc_table[pid].name, child_pid);
/* duplicate the process: T, D & S */
struct descriptor * ppd;
/* Text segment */
ppd = &proc_table[pid].ldts[INDEX_LDT_C];
/* base of T-seg, in bytes */
int caller_T_base = reassembly(ppd->base_high, 24,
ppd->base_mid, 16,
ppd->base_low);
/* limit of T-seg, in 1 or 4096 bytes,
depending on the G bit of descriptor */
int caller_T_limit = reassembly(0, 0,
(ppd->limit_high_attr2 & 0xF), 16,
ppd->limit_low);
/* size of T-seg, in bytes */
int caller_T_size = ((caller_T_limit + 1) *
((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
4096 : 1));
/* Data & Stack segments */
ppd = &proc_table[pid].ldts[INDEX_LDT_RW];
/* base of D&S-seg, in bytes */
int caller_D_S_base = reassembly(ppd->base_high, 24,
ppd->base_mid, 16,
ppd->base_low);
/* limit of D&S-seg, in 1 or 4096 bytes,
depending on the G bit of descriptor */
int caller_D_S_limit = reassembly((ppd->limit_high_attr2 & 0xF), 16,
0, 0,
ppd->limit_low);
/* size of D&S-seg, in bytes */
int caller_D_S_size = ((caller_T_limit + 1) *
((ppd->limit_high_attr2 & (DA_LIMIT_4K >> 8)) ?
4096 : 1));
/* we don't separate T, D & S segments, so we have: */
assert((caller_T_base == caller_D_S_base ) &&
(caller_T_limit == caller_D_S_limit) &&
(caller_T_size == caller_D_S_size ));
/* base of child proc, T, D & S segments share the same space,
so we allocate memory just once */
int child_base = alloc_mem(child_pid, caller_T_size);
/* child is a copy of the parent */
phys_copy((void*)child_base, (void*)caller_T_base, caller_T_size);
/* child's LDT */
init_desc(&p->ldts[INDEX_LDT_C],
child_base,
(PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
DA_LIMIT_4K | DA_32 | DA_C | PRIVILEGE_USER << 5);
init_desc(&p->ldts[INDEX_LDT_RW],
child_base,
(PROC_IMAGE_SIZE_DEFAULT - 1) >> LIMIT_4K_SHIFT,
DA_LIMIT_4K | DA_32 | DA_DRW | PRIVILEGE_USER << 5);
/* tell FS, see fs_fork() */
MESSAGE msg2fs;
msg2fs.type = FORK;
msg2fs.PID = child_pid;
send_recv(BOTH, TASK_FS, &msg2fs);
/* child PID will be returned to the parent proc */
mm_msg.PID = child_pid;
/* birth of the child */
MESSAGE m;
m.type = SYSCALL_RET;
m.RETVAL = 0;
m.PID = 0;
send_recv(SEND, child_pid, &m);
return 0;
}
/*****************************************************************************
* do_exit
*****************************************************************************/
/**
* Perform the exit() syscall.
*
* If proc A calls exit(), then MM will do the following in this routine:
* <1> inform FS so that the fd-related things will be cleaned up
* <2> free A's memory
* <3> set A.exit_status, which is for the parent
* <4> depends on parent's status. if parent (say P) is:
* (1) WAITING
* - clean P's WAITING bit, and
* - send P a message to unblock it
* - release A's proc_table[] slot
* (2) not WAITING
* - set A's HANGING bit
* <5> iterate proc_table[], if proc B is found as A's child, then:
* (1) make INIT the new parent of B, and
* (2) if INIT is WAITING and B is HANGING, then:
* - clean INIT's WAITING bit, and
* - send INIT a message to unblock it
* - release B's proc_table[] slot
* else
* if INIT is WAITING but B is not HANGING, then
* - B will call exit()
* if B is HANGING but INIT is not WAITING, then
* - INIT will call wait()
*
* TERMs:
* - HANGING: everything except the proc_table entry has been cleaned up.
* - WAITING: a proc has at least one child, and it is waiting for the
* child(ren) to exit()
* - zombie: say P has a child A, A will become a zombie if
* - A exit(), and
* - P does not wait(), neither does it exit(). that is to say, P just
* keeps running without terminating itself or its child
*
* @param status Exiting status for parent.
*
*****************************************************************************/
PUBLIC void do_exit(int status)
{
int i;
int pid = mm_msg.source; /* PID of caller */
int parent_pid = proc_table[pid].p_parent;
struct proc * p = &proc_table[pid];
/* tell FS, see fs_exit() */
MESSAGE msg2fs;
msg2fs.type = EXIT;
msg2fs.PID = pid;
send_recv(BOTH, TASK_FS, &msg2fs);
free_mem(pid);
p->exit_status = status;
if (proc_table[parent_pid].p_flags & WAITING) { /* parent is waiting */
proc_table[parent_pid].p_flags &= ~WAITING;
cleanup(&proc_table[pid]);
}
else { /* parent is not waiting */
proc_table[pid].p_flags |= HANGING;
}
/* if the proc has any child, make INIT the new parent */
for (i = 0; i < NR_TASKS + NR_PROCS; i++) {
if (proc_table[i].p_parent == pid) { /* is a child */
proc_table[i].p_parent = INIT;
if ((proc_table[INIT].p_flags & WAITING) &&
(proc_table[i].p_flags & HANGING)) {
proc_table[INIT].p_flags &= ~WAITING;
cleanup(&proc_table[i]);
}
}
}
}
/*****************************************************************************
* cleanup
*****************************************************************************/
/**
* Do the last jobs to clean up a proc thoroughly:
* - Send proc's parent a message to unblock it, and
* - release proc's proc_table[] entry
*
* @param proc Process to clean up.
*****************************************************************************/
PRIVATE void cleanup(struct proc * proc)
{
MESSAGE msg2parent;
msg2parent.type = SYSCALL_RET;
msg2parent.PID = proc2pid(proc);
msg2parent.STATUS = proc->exit_status;
send_recv(SEND, proc->p_parent, &msg2parent);
proc->p_flags = FREE_SLOT;
}
/*****************************************************************************
* do_wait
*****************************************************************************/
/**
* Perform the wait() syscall.
*
* If proc P calls wait(), then MM will do the following in this routine:
* <1> iterate proc_table[],
* if proc A is found as P's child and it is HANGING
* - reply to P (cleanup() will send P a messageto unblock it)
* - release A's proc_table[] entry
* - return (MM will go on with the next message loop)
* <2> if no child of P is HANGING
* - set P's WAITING bit
* <3> if P has no child at all
* - reply to P with error
* <4> return (MM will go on with the next message loop)
*
*****************************************************************************/
PUBLIC void do_wait()
{
int pid = mm_msg.source;
int i;
int children = 0;
struct proc* p_proc = proc_table;
for (i = 0; i < NR_TASKS + NR_PROCS; i++,p_proc++) {
if (p_proc->p_parent == pid) {
children++;
if (p_proc->p_flags & HANGING) {
cleanup(p_proc);
return;
}
}
}
if (children) {
/* has children, but no child is HANGING */
proc_table[pid].p_flags |= WAITING;
}
else {
/* no child at all */
MESSAGE msg;
msg.type = SYSCALL_RET;
msg.PID = NO_TASK;
send_recv(SEND, pid, &msg);
}
}
mm/main.c
/*************************************************************************//**
*****************************************************************************
* @file mm/main.c
* @brief Orange'S Memory Management.
* @author Forrest Y. Yu
* @date Tue May 6 00:33:39 2008
*****************************************************************************
*****************************************************************************/
#include "type.h"
#include "config.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
PUBLIC void do_fork_test();
PRIVATE void init_mm();
/*****************************************************************************
* task_mm
*****************************************************************************/
/**
* <Ring 1> The main loop of TASK MM.
*
*****************************************************************************/
PUBLIC void task_mm()
{
init_mm();
while (1) {
send_recv(RECEIVE, ANY, &mm_msg);
int src = mm_msg.source;
int reply = 1;
int msgtype = mm_msg.type;
switch (msgtype) {
case FORK:
mm_msg.RETVAL = do_fork();
break;
case EXIT:
do_exit(mm_msg.STATUS);
reply = 0;
break;
case EXEC:
mm_msg.RETVAL = do_exec();
break;
case WAIT:
do_wait();
reply = 0;
break;
default:
dump_msg("MM::unknown msg", &mm_msg);
assert(0);
break;
}
if (reply) {
mm_msg.type = SYSCALL_RET;
send_recv(SEND, src, &mm_msg);
}
}
}
/*****************************************************************************
* init_mm
*****************************************************************************/
/**
* Do some initialization work.
*
*****************************************************************************/
PRIVATE void init_mm()
{
struct boot_params bp;
get_boot_params(&bp);
memory_size = bp.mem_size;
/* print memory size */
printl("{MM} memsize:%dMB\n", memory_size / (1024 * 1024));
}
/*****************************************************************************
* alloc_mem
*****************************************************************************/
/**
* Allocate a memory block for a proc.
*
* @param pid Which proc the memory is for.
* @param memsize How many bytes is needed.
*
* @return The base of the memory just allocated.
*****************************************************************************/
PUBLIC int alloc_mem(int pid, int memsize)
{
assert(pid >= (NR_TASKS + NR_NATIVE_PROCS));
if (memsize > PROC_IMAGE_SIZE_DEFAULT) {
panic("unsupported memory request: %d. "
"(should be less than %d)",
memsize,
PROC_IMAGE_SIZE_DEFAULT);
}
int base = PROCS_BASE +
(pid - (NR_TASKS + NR_NATIVE_PROCS)) * PROC_IMAGE_SIZE_DEFAULT;
if (base + memsize >= memory_size)
panic("memory allocation failed. pid:%d", pid);
return base;
}
/*****************************************************************************
* free_mem
*****************************************************************************/
/**
* Free a memory block. Because a memory block is corresponding with a PID, so
* we don't need to really `free' anything. In another word, a memory block is
* dedicated to one and only one PID, no matter what proc actually uses this
* PID.
*
* @param pid Whose memory is to be freed.
*
* @return Zero if success.
*****************************************************************************/
PUBLIC int free_mem(int pid)
{
return 0;
}
lib/exec.c
/*************************************************************************//**
*****************************************************************************
* @file lib/exec.c
* @brief
* @author Forrest Y. Yu
* @date Tue May 6 14:26:09 2008
*****************************************************************************
*****************************************************************************/
#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"
/*****************************************************************************
* exec
*****************************************************************************/
/**
* Executes the program pointed by path.
*
* @param path The full path of the file to be executed.
*
* @return Zero if successful, otherwise -1.
*****************************************************************************/
PUBLIC int exec(const char * path)
{
MESSAGE msg;
msg.type = EXEC;
msg.PATHNAME = (void*)path;
msg.NAME_LEN = strlen(path);
msg.BUF = 0;
msg.BUF_LEN = 0;
send_recv(BOTH, TASK_MM, &msg);
assert(msg.type == SYSCALL_RET);
return msg.RETVAL;
}
/*****************************************************************************
* execl
*****************************************************************************/
PUBLIC int execl(const char *path, const char *arg, ...)
{
va_list parg = (va_list)(&arg);
char **p = (char**)parg;
return execv(path, p);
}
/*****************************************************************************
* execv
*****************************************************************************/
PUBLIC int execv(const char *path, char * argv[])
{
char **p = argv;
char arg_stack[PROC_ORIGIN_STACK];
int stack_len = 0;
while(*p++) {
assert(stack_len + 2 * sizeof(char*) < PROC_ORIGIN_STACK);
stack_len += sizeof(char*);
}
*((int*)(&arg_stack[stack_len])) = 0;
stack_len += sizeof(char*);
char ** q = (char**)arg_stack;
for (p = argv; *p != 0; p++) {
*q++ = &arg_stack[stack_len];
assert(stack_len + strlen(*p) + 1 < PROC_ORIGIN_STACK);
strcpy(&arg_stack[stack_len], *p);
stack_len += strlen(*p);
arg_stack[stack_len] = 0;
stack_len++;
}
MESSAGE msg;
msg.type = EXEC;
msg.PATHNAME = (void*)path;
msg.NAME_LEN = strlen(path);
msg.BUF = (void*)arg_stack;
msg.BUF_LEN = stack_len;
send_recv(BOTH, TASK_MM, &msg);
assert(msg.type == SYSCALL_RET);
return msg.RETVAL;
}
mm/exec.c
/*************************************************************************//**
*****************************************************************************
* @file mm/exec.c
* @brief
* @author Forrest Y. Yu
* @date Tue May 6 14:14:02 2008
*****************************************************************************
*****************************************************************************/
#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#include "elf.h"
/*****************************************************************************
* do_exec
*****************************************************************************/
/**
* Perform the exec() system call.
*
* @return Zero if successful, otherwise -1.
*****************************************************************************/
PUBLIC int do_exec()
{
/* get parameters from the message */
int name_len = mm_msg.NAME_LEN; /* length of filename */
int src = mm_msg.source; /* caller proc nr. */
assert(name_len < MAX_PATH);
char pathname[MAX_PATH];
phys_copy((void*)va2la(TASK_MM, pathname),
(void*)va2la(src, mm_msg.PATHNAME),
name_len);
pathname[name_len] = 0; /* terminate the string */
/* get the file size */
struct stat s;
int ret = stat(pathname, &s);
if (ret != 0) {
printl("{MM} MM::do_exec()::stat() returns error. %s", pathname);
return -1;
}
/* read the file */
int fd = open(pathname, O_RDWR);
if (fd == -1)
return -1;
assert(s.st_size < MMBUF_SIZE);
read(fd, mmbuf, s.st_size);
close(fd);
/* overwrite the current proc image with the new one */
Elf32_Ehdr* elf_hdr = (Elf32_Ehdr*)(mmbuf);
int i;
for (i = 0; i < elf_hdr->e_phnum; i++) {
Elf32_Phdr* prog_hdr = (Elf32_Phdr*)(mmbuf + elf_hdr->e_phoff +
(i * elf_hdr->e_phentsize));
if (prog_hdr->p_type == PT_LOAD) {
assert(prog_hdr->p_vaddr + prog_hdr->p_memsz <
PROC_IMAGE_SIZE_DEFAULT);
phys_copy((void*)va2la(src, (void*)prog_hdr->p_vaddr),
(void*)va2la(TASK_MM,
mmbuf + prog_hdr->p_offset),
prog_hdr->p_filesz);
}
}
/* setup the arg stack */
int orig_stack_len = mm_msg.BUF_LEN;
char stackcopy[PROC_ORIGIN_STACK];
phys_copy((void*)va2la(TASK_MM, stackcopy),
(void*)va2la(src, mm_msg.BUF),
orig_stack_len);
u8 * orig_stack = (u8*)(PROC_IMAGE_SIZE_DEFAULT - PROC_ORIGIN_STACK);
int delta = (int)orig_stack - (int)mm_msg.BUF;
int argc = 0;
if (orig_stack_len) { /* has args */
char **q = (char**)stackcopy;
for (; *q != 0; q++,argc++)
*q += delta;
}
phys_copy((void*)va2la(src, orig_stack),
(void*)va2la(TASK_MM, stackcopy),
orig_stack_len);
proc_table[src].regs.ecx = argc; /* argc */
proc_table[src].regs.eax = (u32)orig_stack; /* argv */
/* setup eip & esp */
proc_table[src].regs.eip = elf_hdr->e_entry; /* @see _start.asm */
proc_table[src].regs.esp = PROC_IMAGE_SIZE_DEFAULT - PROC_ORIGIN_STACK;
strcpy(proc_table[src].name, pathname);
return 0;
}
fs/main.c
/*************************************************************************//**
*****************************************************************************
* @file main.c
* @brief
* @author Forrest Y. Yu
* @date 2007
*****************************************************************************
*****************************************************************************/
#include "type.h"
#include "config.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "proto.h"
#include "hd.h"
PRIVATE void init_fs();
PRIVATE void mkfs();
PRIVATE void read_super_block(int dev);
PRIVATE int fs_fork();
PRIVATE int fs_exit();
/*****************************************************************************
* task_fs
*****************************************************************************/
/**
* <Ring 1> The main loop of TASK FS.
*
*****************************************************************************/
PUBLIC void task_fs()
{
printl("{FS} Task FS begins.\n");
init_fs();
while (1) {
send_recv(RECEIVE, ANY, &fs_msg);
int msgtype = fs_msg.type;
int src = fs_msg.source;
pcaller = &proc_table[src];
switch (msgtype) {
case OPEN:
fs_msg.FD = do_open();
break;
case CLOSE:
fs_msg.RETVAL = do_close();
break;
case READ:
case WRITE:
fs_msg.CNT = do_rdwt();
break;
case UNLINK:
fs_msg.RETVAL = do_unlink();
break;
case RESUME_PROC:
src = fs_msg.PROC_NR;
break;
case FORK:
fs_msg.RETVAL = fs_fork();
break;
case EXIT:
fs_msg.RETVAL = fs_exit();
break;
/* case LSEEK: */
/* fs_msg.OFFSET = do_lseek(); */
/* break; */
case STAT:
fs_msg.RETVAL = do_stat();
break;
default:
dump_msg("FS::unknown message:", &fs_msg);
assert(0);
break;
}
#ifdef ENABLE_DISK_LOG
char * msg_name[128];
msg_name[OPEN] = "OPEN";
msg_name[CLOSE] = "CLOSE";
msg_name[READ] = "READ";
msg_name[WRITE] = "WRITE";
msg_name[LSEEK] = "LSEEK";
msg_name[UNLINK] = "UNLINK";
/* msg_name[FORK] = "FORK"; */
/* msg_name[EXIT] = "EXIT"; */
/* msg_name[STAT] = "STAT"; */
switch (msgtype) {
case UNLINK:
dump_fd_graph("%s just finished. (pid:%d)",
msg_name[msgtype], src);
//panic("");
case OPEN:
case CLOSE:
case READ:
case WRITE:
case FORK:
case EXIT:
/* case LSEEK: */
case STAT:
break;
case RESUME_PROC:
break;
default:
assert(0);
}
#endif
/* reply */
if (fs_msg.type != SUSPEND_PROC) {
fs_msg.type = SYSCALL_RET;
send_recv(SEND, src, &fs_msg);
}
}
}
/*****************************************************************************
* init_fs
*****************************************************************************/
/**
* <Ring 1> Do some preparation.
*
*****************************************************************************/
PRIVATE void init_fs()
{
int i;
/* f_desc_table[] */
for (i = 0; i < NR_FILE_DESC; i++)
memset(&f_desc_table[i], 0, sizeof(struct file_desc));
/* inode_table[] */
for (i = 0; i < NR_INODE; i++)
memset(&inode_table[i], 0, sizeof(struct inode));
/* super_block[] */
struct super_block * sb = super_block;
for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
sb->sb_dev = NO_DEV;
/* open the device: hard disk */
MESSAGE driver_msg;
driver_msg.type = DEV_OPEN;
driver_msg.DEVICE = MINOR(ROOT_DEV);
assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);
/* make FS */
mkfs();
/* load super block of ROOT */
read_super_block(ROOT_DEV);
sb = get_super_block(ROOT_DEV);
assert(sb->magic == MAGIC_V1);
root_inode = get_inode(ROOT_DEV, ROOT_INODE);
}
/*****************************************************************************
* mkfs
*****************************************************************************/
/**
* <Ring 1> Make a available Orange'S FS in the disk. It will
* - Write a super block to sector 1.
* - Create three special files: dev_tty0, dev_tty1, dev_tty2
* - Create a file cmd.tar
* - Create the inode map
* - Create the sector map
* - Create the inodes of the files
* - Create `/', the root directory
*****************************************************************************/
PRIVATE void mkfs()
{
MESSAGE driver_msg;
int i, j;
/************************/
/* super block */
/************************/
/* get the geometry of ROOTDEV */
struct part_info geo;
driver_msg.type = DEV_IOCTL;
driver_msg.DEVICE = MINOR(ROOT_DEV);
driver_msg.REQUEST = DIOCTL_GET_GEO;
driver_msg.BUF = &geo;
driver_msg.PROC_NR = TASK_FS;
assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);
printl("{FS} dev size: 0x%x sectors\n", geo.size);
int bits_per_sect = SECTOR_SIZE * 8; /* 8 bits per byte */
/* generate a super block */
struct super_block sb;
sb.magic = MAGIC_V1; /* 0x111 */
sb.nr_inodes = bits_per_sect;
sb.nr_inode_sects = sb.nr_inodes * INODE_SIZE / SECTOR_SIZE;
sb.nr_sects = geo.size; /* partition size in sector */
sb.nr_imap_sects = 1;
sb.nr_smap_sects = sb.nr_sects / bits_per_sect + 1;
sb.n_1st_sect = 1 + 1 + /* boot sector & super block */
sb.nr_imap_sects + sb.nr_smap_sects + sb.nr_inode_sects;
sb.root_inode = ROOT_INODE;
sb.inode_size = INODE_SIZE;
struct inode x;
sb.inode_isize_off= (int)&x.i_size - (int)&x;
sb.inode_start_off= (int)&x.i_start_sect - (int)&x;
sb.dir_ent_size = DIR_ENTRY_SIZE;
struct dir_entry de;
sb.dir_ent_inode_off = (int)&de.inode_nr - (int)&de;
sb.dir_ent_fname_off = (int)&de.name - (int)&de;
memset(fsbuf, 0x90, SECTOR_SIZE);
memcpy(fsbuf, &sb, SUPER_BLOCK_SIZE);
/* write the super block */
WR_SECT(ROOT_DEV, 1);
printl("{FS} devbase:0x%x00, sb:0x%x00, imap:0x%x00, smap:0x%x00\n"
" inodes:0x%x00, 1st_sector:0x%x00\n",
geo.base * 2,
(geo.base + 1) * 2,
(geo.base + 1 + 1) * 2,
(geo.base + 1 + 1 + sb.nr_imap_sects) * 2,
(geo.base + 1 + 1 + sb.nr_imap_sects + sb.nr_smap_sects) * 2,
(geo.base + sb.n_1st_sect) * 2);
/************************/
/* inode map */
/************************/
memset(fsbuf, 0, SECTOR_SIZE);
for (i = 0; i < (NR_CONSOLES + 3); i++)
fsbuf[0] |= 1 << i;
assert(fsbuf[0] == 0x3F);/* 0011 1111 :
* || ||||
* || |||`--- bit 0 : reserved
* || ||`---- bit 1 : the first inode,
* || || which indicates `/'
* || |`----- bit 2 : /dev_tty0
* || `------ bit 3 : /dev_tty1
* |`-------- bit 4 : /dev_tty2
* `--------- bit 5 : /cmd.tar
*/
WR_SECT(ROOT_DEV, 2);
/************************/
/* secter map */
/************************/
memset(fsbuf, 0, SECTOR_SIZE);
int nr_sects = NR_DEFAULT_FILE_SECTS + 1;
/* ~~~~~~~~~~~~~~~~~~~|~ |
* | `--- bit 0 is reserved
* `-------- for `/'
*/
for (i = 0; i < nr_sects / 8; i++)
fsbuf[i] = 0xFF;
for (j = 0; j < nr_sects % 8; j++)
fsbuf[i] |= (1 << j);
WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects);
/* zeromemory the rest sector-map */
memset(fsbuf, 0, SECTOR_SIZE);
for (i = 1; i < sb.nr_smap_sects; i++)
WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + i);
/* cmd.tar */
/* make sure it'll not be overwritten by the disk log */
assert(INSTALL_START_SECT + INSTALL_NR_SECTS <
sb.nr_sects - NR_SECTS_FOR_LOG);
int bit_offset = INSTALL_START_SECT -
sb.n_1st_sect + 1; /* sect M <-> bit (M - sb.n_1stsect + 1) */
int bit_off_in_sect = bit_offset % (SECTOR_SIZE * 8);
int bit_left = INSTALL_NR_SECTS;
int cur_sect = bit_offset / (SECTOR_SIZE * 8);
RD_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
while (bit_left) {
int byte_off = bit_off_in_sect / 8;
/* this line is ineffecient in a loop, but I don't care */
fsbuf[byte_off] |= 1 << (bit_off_in_sect % 8);
bit_left--;
bit_off_in_sect++;
if (bit_off_in_sect == (SECTOR_SIZE * 8)) {
WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
cur_sect++;
RD_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
bit_off_in_sect = 0;
}
}
WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + cur_sect);
/************************/
/* inodes */
/************************/
/* inode of `/' */
memset(fsbuf, 0, SECTOR_SIZE);
struct inode * pi = (struct inode*)fsbuf;
pi->i_mode = I_DIRECTORY;
pi->i_size = DIR_ENTRY_SIZE * 5; /* 5 files:
* `.',
* `dev_tty0', `dev_tty1', `dev_tty2',
* `cmd.tar'
*/
pi->i_start_sect = sb.n_1st_sect;
pi->i_nr_sects = NR_DEFAULT_FILE_SECTS;
/* inode of `/dev_tty0~2' */
for (i = 0; i < NR_CONSOLES; i++) {
pi = (struct inode*)(fsbuf + (INODE_SIZE * (i + 1)));
pi->i_mode = I_CHAR_SPECIAL;
pi->i_size = 0;
pi->i_start_sect = MAKE_DEV(DEV_CHAR_TTY, i);
pi->i_nr_sects = 0;
}
/* inode of `/cmd.tar' */
pi = (struct inode*)(fsbuf + (INODE_SIZE * (NR_CONSOLES + 1)));
pi->i_mode = I_REGULAR;
pi->i_size = INSTALL_NR_SECTS * SECTOR_SIZE;
pi->i_start_sect = INSTALL_START_SECT;
pi->i_nr_sects = INSTALL_NR_SECTS;
WR_SECT(ROOT_DEV, 2 + sb.nr_imap_sects + sb.nr_smap_sects);
/************************/
/* `/' */
/************************/
memset(fsbuf, 0, SECTOR_SIZE);
struct dir_entry * pde = (struct dir_entry *)fsbuf;
pde->inode_nr = 1;
strcpy(pde->name, ".");
/* dir entries of `/dev_tty0~2' */
for (i = 0; i < NR_CONSOLES; i++) {
pde++;
pde->inode_nr = i + 2; /* dev_tty0's inode_nr is 2 */
sprintf(pde->name, "dev_tty%d", i);
}
(++pde)->inode_nr = NR_CONSOLES + 2;
sprintf(pde->name, "cmd.tar", i);
WR_SECT(ROOT_DEV, sb.n_1st_sect);
}
/*****************************************************************************
* rw_sector
*****************************************************************************/
/**
* <Ring 1> R/W a sector via messaging with the corresponding driver.
*
* @param io_type DEV_READ or DEV_WRITE
* @param dev device nr
* @param pos Byte offset from/to where to r/w.
* @param bytes r/w count in bytes.
* @param proc_nr To whom the buffer belongs.
* @param buf r/w buffer.
*
* @return Zero if success.
*****************************************************************************/
PUBLIC int rw_sector(int io_type, int dev, u64 pos, int bytes, int proc_nr,
void* buf)
{
MESSAGE driver_msg;
driver_msg.type = io_type;
driver_msg.DEVICE = MINOR(dev);
driver_msg.POSITION = pos;
driver_msg.BUF = buf;
driver_msg.CNT = bytes;
driver_msg.PROC_NR = proc_nr;
assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);
return 0;
}
/*****************************************************************************
* read_super_block
*****************************************************************************/
/**
* <Ring 1> Read super block from the given device then write it into a free
* super_block[] slot.
*
* @param dev From which device the super block comes.
*****************************************************************************/
PRIVATE void read_super_block(int dev)
{
int i;
MESSAGE driver_msg;
driver_msg.type = DEV_READ;
driver_msg.DEVICE = MINOR(dev);
driver_msg.POSITION = SECTOR_SIZE * 1;
driver_msg.BUF = fsbuf;
driver_msg.CNT = SECTOR_SIZE;
driver_msg.PROC_NR = TASK_FS;
assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);
send_recv(BOTH, dd_map[MAJOR(dev)].driver_nr, &driver_msg);
/* find a free slot in super_block[] */
for (i = 0; i < NR_SUPER_BLOCK; i++)
if (super_block[i].sb_dev == NO_DEV)
break;
if (i == NR_SUPER_BLOCK)
panic("super_block slots used up");
assert(i == 0); /* currently we use only the 1st slot */
struct super_block * psb = (struct super_block *)fsbuf;
super_block[i] = *psb;
super_block[i].sb_dev = dev;
}
/*****************************************************************************
* get_super_block
*****************************************************************************/
/**
* <Ring 1> Get the super block from super_block[].
*
* @param dev Device nr.
*
* @return Super block ptr.
*****************************************************************************/
PUBLIC struct super_block * get_super_block(int dev)
{
struct super_block * sb = super_block;
for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
if (sb->sb_dev == dev)
return sb;
panic("super block of devie %d not found.\n", dev);
return 0;
}
/*****************************************************************************
* get_inode
*****************************************************************************/
/**
* <Ring 1> Get the inode ptr of given inode nr. A cache -- inode_table[] -- is
* maintained to make things faster. If the inode requested is already there,
* just return it. Otherwise the inode will be read from the disk.
*
* @param dev Device nr.
* @param num I-node nr.
*
* @return The inode ptr requested.
*****************************************************************************/
PUBLIC struct inode * get_inode(int dev, int num)
{
if (num == 0)
return 0;
struct inode * p;
struct inode * q = 0;
for (p = &inode_table[0]; p < &inode_table[NR_INODE]; p++) {
if (p->i_cnt) { /* not a free slot */
if ((p->i_dev == dev) && (p->i_num == num)) {
/* this is the inode we want */
p->i_cnt++;
return p;
}
}
else { /* a free slot */
if (!q) /* q hasn't been assigned yet */
q = p; /* q <- the 1st free slot */
}
}
if (!q)
panic("the inode table is full");
q->i_dev = dev;
q->i_num = num;
q->i_cnt = 1;
struct super_block * sb = get_super_block(dev);
int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
((num - 1) / (SECTOR_SIZE / INODE_SIZE));
RD_SECT(dev, blk_nr);
struct inode * pinode =
(struct inode*)((u8*)fsbuf +
((num - 1 ) % (SECTOR_SIZE / INODE_SIZE))
* INODE_SIZE);
q->i_mode = pinode->i_mode;
q->i_size = pinode->i_size;
q->i_start_sect = pinode->i_start_sect;
q->i_nr_sects = pinode->i_nr_sects;
return q;
}
/*****************************************************************************
* put_inode
*****************************************************************************/
/**
* Decrease the reference nr of a slot in inode_table[]. When the nr reaches
* zero, it means the inode is not used any more and can be overwritten by
* a new inode.
*
* @param pinode I-node ptr.
*****************************************************************************/
PUBLIC void put_inode(struct inode * pinode)
{
assert(pinode->i_cnt > 0);
pinode->i_cnt--;
}
/*****************************************************************************
* sync_inode
*****************************************************************************/
/**
* <Ring 1> Write the inode back to the disk. Commonly invoked as soon as the
* inode is changed.
*
* @param p I-node ptr.
*****************************************************************************/
PUBLIC void sync_inode(struct inode * p)
{
struct inode * pinode;
struct super_block * sb = get_super_block(p->i_dev);
int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
((p->i_num - 1) / (SECTOR_SIZE / INODE_SIZE));
RD_SECT(p->i_dev, blk_nr);
pinode = (struct inode*)((u8*)fsbuf +
(((p->i_num - 1) % (SECTOR_SIZE / INODE_SIZE))
* INODE_SIZE));
pinode->i_mode = p->i_mode;
pinode->i_size = p->i_size;
pinode->i_start_sect = p->i_start_sect;
pinode->i_nr_sects = p->i_nr_sects;
WR_SECT(p->i_dev, blk_nr);
}
/*****************************************************************************
* fs_fork
*****************************************************************************/
/**
* Perform the aspects of fork() that relate to files.
*
* @return Zero if success, otherwise a negative integer.
*****************************************************************************/
PRIVATE int fs_fork()
{
int i;
struct proc* child = &proc_table[fs_msg.PID];
for (i = 0; i < NR_FILES; i++) {
if (child->filp[i]) {
child->filp[i]->fd_cnt++;
child->filp[i]->fd_inode->i_cnt++;
}
}
return 0;
}
/*****************************************************************************
* fs_exit
*****************************************************************************/
/**
* Perform the aspects of exit() that relate to files.
*
* @return Zero if success.
*****************************************************************************/
PRIVATE int fs_exit()
{
int i;
struct proc* p = &proc_table[fs_msg.PID];
for (i = 0; i < NR_FILES; i++) {
if (p->filp[i]) {
/* release the inode */
p->filp[i]->fd_inode->i_cnt--;
/* release the file desc slot */
if (--p->filp[i]->fd_cnt == 0)
p->filp[i]->fd_inode = 0;
p->filp[i] = 0;
}
}
return 0;
}
Makefile
######################### # Makefile for Orange'S # ######################### # Entry point of Orange'S # It must have the same value with 'KernelEntryPointPhyAddr' in load.inc! ENTRYPOINT = 0x1000 # Offset of entry point in kernel file # It depends on ENTRYPOINT ENTRYOFFSET = 0x400 # Programs, flags, etc. ASM = nasm DASM = objdump CC = gcc LD = ld ASMBFLAGS = -I boot/include/ ASMKFLAGS = -I include/ -I include/sys/ -f elf CFLAGS = -I include/ -I include/sys/ -m32 -c -fno-builtin -fno-stack-protector LDFLAGS = -s -melf_i386 -Ttext $(ENTRYPOINT) -Map krnl.map DASMFLAGS = -D ARFLAGS = rcs # This Program ORANGESBOOT = boot/boot.bin boot/loader.bin ORANGESKERNEL = kernel.bin LIB = lib/orangescrt.a OBJS = kernel/kernel.o kernel/start.o kernel/main.o\ kernel/clock.o kernel/keyboard.o kernel/tty.o kernel/console.o\ kernel/i8259.o kernel/global.o kernel/protect.o kernel/proc.o\ kernel/systask.o kernel/hd.o\ kernel/kliba.o kernel/klib.o\ lib/syslog.o\ mm/main.o mm/forkexit.o mm/exec.o\ fs/main.o fs/open.o fs/misc.o fs/read_write.o\ fs/link.o\ fs/disklog.o LOBJS = lib/syscall.o\ lib/printf.o lib/vsprintf.o\ lib/string.o lib/misc.o\ lib/open.o lib/read.o lib/write.o lib/close.o lib/unlink.o\ lib/getpid.o lib/stat.o\ lib/fork.o lib/exit.o lib/wait.o lib/exec.o DASMOUTPUT = kernel.bin.asm # All Phony Targets .PHONY : everything final image clean realclean disasm all buildimg # Default starting position nop : @echo "why not \`make image' huh? :)" everything : $(ORANGESBOOT) $(ORANGESKERNEL) all : realclean everything image : realclean everything clean buildimg app vdi clean : rm -f $(OBJS) $(LOBJS) realclean : rm -f $(OBJS) $(LOBJS) $(LIB) $(ORANGESBOOT) $(ORANGESKERNEL) disasm : $(DASM) $(DASMFLAGS) $(ORANGESKERNEL) > $(DASMOUTPUT) # We assume that "a.img" exists in current folder buildimg : mkdir tmp sleep 1 sudo mount -o loop a.img tmp sleep 1 sudo cp -fv boot/loader.bin tmp sudo cp -fv kernel.bin tmp sleep 1 sudo umount tmp sleep 1 rmdir tmp app: cd ./command/; make install vdi: rm 80m.vdi VBoxManage convertfromraw -format VDI 80m.img 80m.vdi VBoxManage internalcommands sethduuid 80m.vdi ccea0f68-01ea-4b9e-8160-e43ef528ba6e debug: rm t.img VBoxManage clonehd -format RAW ccea0f68-01ea-4b9e-8160-e43ef528ba6e t.img xxd -u -a -g 1 -c 16 -s 0xAD9C00 -l 20000 t.img boot/boot.bin : boot/boot.asm boot/include/load.inc boot/include/fat12hdr.inc $(ASM) $(ASMBFLAGS) -o $@ $< boot/loader.bin : boot/loader.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc $(ASM) $(ASMBFLAGS) -o $@ $< $(ORANGESKERNEL) : $(OBJS) $(LIB) $(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $^ $(LIB) : $(LOBJS) $(AR) $(ARFLAGS) $@ $^ kernel/kernel.o : kernel/kernel.asm $(ASM) $(ASMKFLAGS) -o $@ $< lib/syscall.o : lib/syscall.asm $(ASM) $(ASMKFLAGS) -o $@ $< kernel/start.o: kernel/start.c $(CC) $(CFLAGS) -o $@ $< kernel/main.o: kernel/main.c $(CC) $(CFLAGS) -o $@ $< kernel/clock.o: kernel/clock.c $(CC) $(CFLAGS) -o $@ $< kernel/keyboard.o: kernel/keyboard.c $(CC) $(CFLAGS) -o $@ $< kernel/tty.o: kernel/tty.c $(CC) $(CFLAGS) -o $@ $< kernel/console.o: kernel/console.c $(CC) $(CFLAGS) -o $@ $< kernel/i8259.o: kernel/i8259.c $(CC) $(CFLAGS) -o $@ $< kernel/global.o: kernel/global.c $(CC) $(CFLAGS) -o $@ $< kernel/protect.o: kernel/protect.c $(CC) $(CFLAGS) -o $@ $< kernel/proc.o: kernel/proc.c $(CC) $(CFLAGS) -o $@ $< lib/printf.o: lib/printf.c $(CC) $(CFLAGS) -o $@ $< lib/vsprintf.o: lib/vsprintf.c $(CC) $(CFLAGS) -o $@ $< kernel/systask.o: kernel/systask.c $(CC) $(CFLAGS) -o $@ $< kernel/hd.o: kernel/hd.c $(CC) $(CFLAGS) -o $@ $< kernel/klib.o: kernel/klib.c $(CC) $(CFLAGS) -o $@ $< lib/misc.o: lib/misc.c $(CC) $(CFLAGS) -o $@ $< kernel/kliba.o : kernel/kliba.asm $(ASM) $(ASMKFLAGS) -o $@ $< lib/string.o : lib/string.asm $(ASM) $(ASMKFLAGS) -o $@ $< lib/open.o: lib/open.c $(CC) $(CFLAGS) -o $@ $< lib/read.o: lib/read.c $(CC) $(CFLAGS) -o $@ $< lib/write.o: lib/write.c $(CC) $(CFLAGS) -o $@ $< lib/close.o: lib/close.c $(CC) $(CFLAGS) -o $@ $< lib/unlink.o: lib/unlink.c $(CC) $(CFLAGS) -o $@ $< lib/getpid.o: lib/getpid.c $(CC) $(CFLAGS) -o $@ $< lib/syslog.o: lib/syslog.c $(CC) $(CFLAGS) -o $@ $< lib/fork.o: lib/fork.c $(CC) $(CFLAGS) -o $@ $< lib/exit.o: lib/exit.c $(CC) $(CFLAGS) -o $@ $< lib/wait.o: lib/wait.c $(CC) $(CFLAGS) -o $@ $< lib/exec.o: lib/exec.c $(CC) $(CFLAGS) -o $@ $< lib/stat.o: lib/stat.c $(CC) $(CFLAGS) -o $@ $< mm/main.o: mm/main.c $(CC) $(CFLAGS) -o $@ $< mm/forkexit.o: mm/forkexit.c $(CC) $(CFLAGS) -o $@ $< mm/exec.o: mm/exec.c $(CC) $(CFLAGS) -o $@ $< fs/main.o: fs/main.c $(CC) $(CFLAGS) -o $@ $< fs/open.o: fs/open.c $(CC) $(CFLAGS) -o $@ $< fs/read_write.o: fs/read_write.c $(CC) $(CFLAGS) -o $@ $< fs/link.o: fs/link.c $(CC) $(CFLAGS) -o $@ $< fs/disklog.o: fs/disklog.c $(CC) $(CFLAGS) -o $@ $<
程式碼說明
- 從訊息體中獲取各種參數。由於使用者和 MM 處在不同的位址空間,所以對於檔案名這樣的一段記憶體,需要透過獲取其物理位置並進行物理位址複製。
- 透過一個新的系統使用 stat() 獲取被執行檔的大小
- 將被執行檔全部讀入 MM 自己的緩衝區(MM 的緩衝區有 1MB,我們姑且假設這個空間足夠了。等有一天真的不夠了,會觸發一個 assert,到時我們再做打算)。
- 根據 ELF 檔的程式頭(ProgramHeader)資訊,將被執行檔的各個段放置到合適的位置。
- 建立參數堆疊-這個堆疊在 execv() 已經準備好了,但由於記憶體空間發生了變化,所以裡面所有的指標都需要重新定位,這個過程並不難,透過一個 delta 變數即可完成
- 為被執行程式的 eax 和 ecx 賦值-還記得 _start 中我們將 eax 和 ecx 壓入堆疊嗎?壓的就是 argv 和 argc。它們是在這裡被賦值的。
- 為程式的 eip 賦值,這是程式的入口位址,即 _start 處。
- 為程式的 esp 賦值。這熠燿閃出剛才我們準備好的堆疊的位置。
- 最後是將處理序的名字改成被執行程式的名字。
![]() |
| 執行結果 |
注意事項
- 請留意 ROOT_BASE(boot/include/load.inc)的位址:必須對應開始磁區。如結果的圖,我預計將作業系統放在 HD 16 (hd2a),其開始磁區為 0x56C0
- 請留意 INSTALL_START_SECT(include/sys/config.h)的程式放置位置:請盡量放磁區最後。如圖,HD 16 的大小為 0xA000,我選擇放在 0x8900。
- 請留意 fs/main.c, line 282-283:INSTALL_START_SECT 雖然放後面一點,但還是有其極限:INSTALL_START_SECT - INSTALL_NR_SECTS < sb.nr_sects - NR_SECTS_FOR_LOG,亦即 INSTALL_START_SECT + 0x800 < 0xA000 - 0x800,故其極限應為 0x8FFF。
編譯步驟
$ make image
目錄結構
. ├── 80m.img ├── 80m.vdi ├── a.img ├── boot │ ├── boot.asm │ ├── include │ │ ├── fat12hdr.inc │ │ ├── load.inc │ │ └── pm.inc │ └── loader.asm ├── command │ ├── echo.c │ ├── Makefile │ ├── pwd.c │ ├── start.asm ├── fs │ ├── disklog.c │ ├── link.c │ ├── main.c │ ├── misc.c │ ├── open.c │ └── read_write.c ├── include │ ├── stdio.h │ ├── string.h │ ├── sys │ │ ├── config.h │ │ ├── console.h │ │ ├── const.h │ │ ├── fs.h │ │ ├── global.h │ │ ├── hd.h │ │ ├── keyboard.h │ │ ├── keymap.h │ │ ├── proc.h │ │ ├── protect.h │ │ ├── proto.h │ │ ├── sconst.inc │ │ └── tty.h │ └── type.h ├── kernel │ ├── clock.c │ ├── console.c │ ├── global.c │ ├── hd.c │ ├── i8259.c │ ├── kernel.asm │ ├── keyboard.c │ ├── kliba.asm │ ├── klib.c │ ├── main.c │ ├── proc.c │ ├── protect.c │ ├── start.c │ ├── systask.c │ └── tty.c ├── krnl.map ├── lib │ ├── close.c │ ├── exec.c │ ├── exit.c │ ├── fork.c │ ├── getpid.c │ ├── misc.c │ ├── open.c │ ├── printf.c │ ├── read.c │ ├── stat.c │ ├── string.asm │ ├── syscall.asm │ ├── syslog.c │ ├── unlink.c │ ├── vsprintf.c │ ├── wait.c │ └── write.c ├── Makefile └── mm ├── exec.c ├── forkexit.c └── main.c

留言
張貼留言