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
留言
張貼留言