Last active
December 23, 2015 12:18
-
-
Save choupi/6633938 to your computer and use it in GitHub Desktop.
init/main.c: process 0 -> process 1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int fork(void) | |
{ | |
long __res; | |
__asm__ volatile ("int $0x80" | |
: "=a" (__res) | |
: "0" (__NR_ fork)) ; | |
if (__res >= 0) | |
return (int) __res; | |
errno = -__res; | |
return -1; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define move_to_user_mode() \ | |
__asm__ ("movl %%esp,%%eax\n\t" \ | |
"pushl $0x17\n\t" \ | |
"pushl %%eax\n\t" \ | |
"pushfl\n\t" \ | |
"pushl $0x0f\n\t" \ | |
"pushl $1f\n\t" \ | |
"iret\n" \ | |
"1:\tmovl $0x17,%%eax\n\t" \ | |
"movw %%ax,%%ds\n\t" \ | |
"movw %%ax,%%es\n\t" \ | |
"movw %%ax,%%fs\n\t" \ | |
"movw %%ax,%%gs" \ | |
:::"ax") | |
#define sti() __asm__ ("sti"::) | |
#define cli() __asm__ ("cli"::) | |
#define nop() __asm__ ("nop"::) | |
#define iret() __asm__ ("iret"::) | |
#define _set_gate(gate_addr,type,dpl,addr) \ | |
__asm__ ("movw %%dx,%%ax\n\t" \ | |
"movw %0,%%dx\n\t" \ | |
"movl %%eax,%1\n\t" \ | |
"movl %%edx,%2" \ | |
: \ | |
: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ | |
"o" (*((char *) (gate_addr))), \ | |
"o" (*(4+(char *) (gate_addr))), \ | |
"d" ((char *) (addr)),"a" (0x00080000)) | |
#define set_intr_gate(n,addr) \ | |
_set_gate(&idt[n],14,0,addr) | |
#define set_trap_gate(n,addr) \ | |
_set_gate(&idt[n],15,0,addr) | |
#define set_system_gate(n,addr) \ | |
_set_gate(&idt[n],15,3,addr) | |
#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\ | |
*(gate_addr) = ((base) & 0xff000000) | \ | |
(((base) & 0x00ff0000)>>16) | \ | |
((limit) & 0xf0000) | \ | |
((dpl)<<13) | \ | |
(0x00408000) | \ | |
((type)<<8); \ | |
*((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \ | |
((limit) & 0x0ffff); } | |
#define _set_tssldt_desc(n,addr,type) \ | |
__asm__ ("movw $104,%1\n\t" \ | |
"movw %%ax,%2\n\t" \ | |
"rorl $16,%%eax\n\t" \ | |
"movb %%al,%3\n\t" \ | |
"movb $" type ",%4\n\t" \ | |
"movb $0x00,%5\n\t" \ | |
"movb %%ah,%6\n\t" \ | |
"rorl $16,%%eax" \ | |
::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \ | |
"m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \ | |
) | |
#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x89") | |
#define set_ldt_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr,"0x82") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _UNISTD_H | |
#define _UNISTD_H | |
/* ok, this may be a joke, but I'm working on it */ | |
#define _POSIX_VERSION 198808L | |
#define _POSIX_CHOWN_RESTRICTED /* only root can do a chown (I think..) */ | |
#define _POSIX_NO_TRUNC /* no pathname truncation (but see in kernel) */ | |
#define _POSIX_VDISABLE '\0' /* character to disable things like ^C */ | |
/*#define _POSIX_SAVED_IDS */ /* we'll get to this yet */ | |
/*#define _POSIX_JOB_CONTROL */ /* we aren't there quite yet. Soon hopefully */ | |
#define STDIN_FILENO 0 | |
#define STDOUT_FILENO 1 | |
#define STDERR_FILENO 2 | |
#ifndef NULL | |
#define NULL ((void *)0) | |
#endif | |
/* access */ | |
#define F_OK 0 | |
#define X_OK 1 | |
#define W_OK 2 | |
#define R_OK 4 | |
/* lseek */ | |
#define SEEK_SET 0 | |
#define SEEK_CUR 1 | |
#define SEEK_END 2 | |
/* _SC stands for System Configuration. We don't use them much */ | |
#define _SC_ARG_MAX 1 | |
#define _SC_CHILD_MAX 2 | |
#define _SC_CLOCKS_PER_SEC 3 | |
#define _SC_NGROUPS_MAX 4 | |
#define _SC_OPEN_MAX 5 | |
#define _SC_JOB_CONTROL 6 | |
#define _SC_SAVED_IDS 7 | |
#define _SC_VERSION 8 | |
/* more (possibly) configurable things - now pathnames */ | |
#define _PC_LINK_MAX 1 | |
#define _PC_MAX_CANON 2 | |
#define _PC_MAX_INPUT 3 | |
#define _PC_NAME_MAX 4 | |
#define _PC_PATH_MAX 5 | |
#define _PC_PIPE_BUF 6 | |
#define _PC_NO_TRUNC 7 | |
#define _PC_VDISABLE 8 | |
#define _PC_CHOWN_RESTRICTED 9 | |
#include <sys/stat.h> | |
#include <sys/times.h> | |
#include <sys/utsname.h> | |
#include <utime.h> | |
#ifdef __LIBRARY__ | |
#define __NR_setup 0 /* used only by init, to get system going */ | |
#define __NR_exit 1 | |
#define __NR_fork 2 | |
#define __NR_read 3 | |
#define __NR_write 4 | |
#define __NR_open 5 | |
#define __NR_close 6 | |
#define __NR_waitpid 7 | |
#define __NR_creat 8 | |
#define __NR_link 9 | |
#define __NR_unlink 10 | |
#define __NR_execve 11 | |
#define __NR_chdir 12 | |
#define __NR_time 13 | |
#define __NR_mknod 14 | |
#define __NR_chmod 15 | |
#define __NR_chown 16 | |
#define __NR_break 17 | |
#define __NR_stat 18 | |
#define __NR_lseek 19 | |
#define __NR_getpid 20 | |
#define __NR_mount 21 | |
#define __NR_umount 22 | |
#define __NR_setuid 23 | |
#define __NR_getuid 24 | |
#define __NR_stime 25 | |
#define __NR_ptrace 26 | |
#define __NR_alarm 27 | |
#define __NR_fstat 28 | |
#define __NR_pause 29 | |
#define __NR_utime 30 | |
#define __NR_stty 31 | |
#define __NR_gtty 32 | |
#define __NR_access 33 | |
#define __NR_nice 34 | |
#define __NR_ftime 35 | |
#define __NR_sync 36 | |
#define __NR_kill 37 | |
#define __NR_rename 38 | |
#define __NR_mkdir 39 | |
#define __NR_rmdir 40 | |
#define __NR_dup 41 | |
#define __NR_pipe 42 | |
#define __NR_times 43 | |
#define __NR_prof 44 | |
#define __NR_brk 45 | |
#define __NR_setgid 46 | |
#define __NR_getgid 47 | |
#define __NR_signal 48 | |
#define __NR_geteuid 49 | |
#define __NR_getegid 50 | |
#define __NR_acct 51 | |
#define __NR_phys 52 | |
#define __NR_lock 53 | |
#define __NR_ioctl 54 | |
#define __NR_fcntl 55 | |
#define __NR_mpx 56 | |
#define __NR_setpgid 57 | |
#define __NR_ulimit 58 | |
#define __NR_uname 59 | |
#define __NR_umask 60 | |
#define __NR_chroot 61 | |
#define __NR_ustat 62 | |
#define __NR_dup2 63 | |
#define __NR_getppid 64 | |
#define __NR_getpgrp 65 | |
#define __NR_setsid 66 | |
#define __NR_sigaction 67 | |
#define __NR_sgetmask 68 | |
#define __NR_ssetmask 69 | |
#define __NR_setreuid 70 | |
#define __NR_setregid 71 | |
#define _syscall0(type,name) \ | |
type name(void) \ | |
{ \ | |
long __res; \ | |
__asm__ volatile ("int $0x80" \ | |
: "=a" (__res) \ | |
: "0" (__NR_##name)); \ | |
if (__res >= 0) \ | |
return (type) __res; \ | |
errno = -__res; \ | |
return -1; \ | |
} | |
#define _syscall1(type,name,atype,a) \ | |
type name(atype a) \ | |
{ \ | |
long __res; \ | |
__asm__ volatile ("int $0x80" \ | |
: "=a" (__res) \ | |
: "0" (__NR_##name),"b" ((long)(a))); \ | |
if (__res >= 0) \ | |
return (type) __res; \ | |
errno = -__res; \ | |
return -1; \ | |
} | |
#define _syscall2(type,name,atype,a,btype,b) \ | |
type name(atype a,btype b) \ | |
{ \ | |
long __res; \ | |
__asm__ volatile ("int $0x80" \ | |
: "=a" (__res) \ | |
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b))); \ | |
if (__res >= 0) \ | |
return (type) __res; \ | |
errno = -__res; \ | |
return -1; \ | |
} | |
#define _syscall3(type,name,atype,a,btype,b,ctype,c) \ | |
type name(atype a,btype b,ctype c) \ | |
{ \ | |
long __res; \ | |
__asm__ volatile ("int $0x80" \ | |
: "=a" (__res) \ | |
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \ | |
if (__res>=0) \ | |
return (type) __res; \ | |
errno=-__res; \ | |
return -1; \ | |
} | |
#endif /* __LIBRARY__ */ | |
extern int errno; | |
int access(const char * filename, mode_t mode); | |
int acct(const char * filename); | |
int alarm(int sec); | |
int brk(void * end_data_segment); | |
void * sbrk(ptrdiff_t increment); | |
int chdir(const char * filename); | |
int chmod(const char * filename, mode_t mode); | |
int chown(const char * filename, uid_t owner, gid_t group); | |
int chroot(const char * filename); | |
int close(int fildes); | |
int creat(const char * filename, mode_t mode); | |
int dup(int fildes); | |
int execve(const char * filename, char ** argv, char ** envp); | |
int execv(const char * pathname, char ** argv); | |
int execvp(const char * file, char ** argv); | |
int execl(const char * pathname, char * arg0, ...); | |
int execlp(const char * file, char * arg0, ...); | |
int execle(const char * pathname, char * arg0, ...); | |
volatile void exit(int status); | |
volatile void _exit(int status); | |
int fcntl(int fildes, int cmd, ...); | |
int fork(void); | |
int getpid(void); | |
int getuid(void); | |
int geteuid(void); | |
int getgid(void); | |
int getegid(void); | |
int ioctl(int fildes, int cmd, ...); | |
int kill(pid_t pid, int signal); | |
int link(const char * filename1, const char * filename2); | |
int lseek(int fildes, off_t offset, int origin); | |
int mknod(const char * filename, mode_t mode, dev_t dev); | |
int mount(const char * specialfile, const char * dir, int rwflag); | |
int nice(int val); | |
int open(const char * filename, int flag, ...); | |
int pause(void); | |
int pipe(int * fildes); | |
int read(int fildes, char * buf, off_t count); | |
int setpgrp(void); | |
int setpgid(pid_t pid,pid_t pgid); | |
int setuid(uid_t uid); | |
int setgid(gid_t gid); | |
void (*signal(int sig, void (*fn)(int)))(int); | |
int stat(const char * filename, struct stat * stat_buf); | |
int fstat(int fildes, struct stat * stat_buf); | |
int stime(time_t * tptr); | |
int sync(void); | |
time_t time(time_t * tloc); | |
time_t times(struct tms * tbuf); | |
int ulimit(int cmd, long limit); | |
mode_t umask(mode_t mask); | |
int umount(const char * specialfile); | |
int uname(struct utsname * name); | |
int unlink(const char * filename); | |
int ustat(dev_t dev, struct ustat * ubuf); | |
int utime(const char * filename, struct utimbuf * times); | |
pid_t waitpid(pid_t pid,int * wait_stat,int options); | |
pid_t wait(int * wait_stat); | |
int write(int fildes, const char * buf, off_t count); | |
int dup2(int oldfd, int newfd); | |
int getppid(void); | |
pid_t getpgrp(void); | |
pid_t setsid(void); | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* linux/init/main.c | |
* | |
* (C) 1991 Linus Torvalds | |
*/ | |
#define __LIBRARY__ | |
#include <unistd.h> | |
#include <time.h> | |
/* | |
* we need this inline - forking from kernel space will result | |
* in NO COPY ON WRITE (!!!), until an execve is executed. This | |
* is no problem, but for the stack. This is handled by not letting | |
* main() use the stack at all after fork(). Thus, no function | |
* calls - which means inline code for fork too, as otherwise we | |
* would use the stack upon exit from 'fork()'. | |
* | |
* Actually only pause and fork are needed inline, so that there | |
* won't be any messing with the stack from main(), but we define | |
* some others too. | |
*/ | |
static inline _syscall0(int,fork) | |
static inline _syscall0(int,pause) | |
static inline _syscall1(int,setup,void *,BIOS) | |
static inline _syscall0(int,sync) | |
#include <linux/tty.h> | |
#include <linux/sched.h> | |
#include <linux/head.h> | |
#include <asm/system.h> | |
#include <asm/io.h> | |
#include <stddef.h> | |
#include <stdarg.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/types.h> | |
#include <linux/fs.h> | |
static char printbuf[1024]; | |
extern int vsprintf(); | |
extern void init(void); | |
extern void blk_dev_init(void); | |
extern void chr_dev_init(void); | |
extern void hd_init(void); | |
extern void floppy_init(void); | |
extern void mem_init(long start, long end); | |
extern long rd_init(long mem_start, int length); | |
extern long kernel_mktime(struct tm * tm); | |
extern long startup_time; | |
/* | |
* This is set up by the setup-routine at boot-time | |
*/ | |
#define EXT_MEM_K (*(unsigned short *)0x90002) | |
#define DRIVE_INFO (*(struct drive_info *)0x90080) | |
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) | |
/* | |
* Yeah, yeah, it's ugly, but I cannot find how to do this correctly | |
* and this seems to work. I anybody has more info on the real-time | |
* clock I'd be interested. Most of this was trial and error, and some | |
* bios-listing reading. Urghh. | |
*/ | |
#define CMOS_READ(addr) ({ \ | |
outb_p(0x80|addr,0x70); \ | |
inb_p(0x71); \ | |
}) | |
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) | |
static void time_init(void) | |
{ | |
struct tm time; | |
do { | |
time.tm_sec = CMOS_READ(0); | |
time.tm_min = CMOS_READ(2); | |
time.tm_hour = CMOS_READ(4); | |
time.tm_mday = CMOS_READ(7); | |
time.tm_mon = CMOS_READ(8); | |
time.tm_year = CMOS_READ(9); | |
} while (time.tm_sec != CMOS_READ(0)); | |
BCD_TO_BIN(time.tm_sec); | |
BCD_TO_BIN(time.tm_min); | |
BCD_TO_BIN(time.tm_hour); | |
BCD_TO_BIN(time.tm_mday); | |
BCD_TO_BIN(time.tm_mon); | |
BCD_TO_BIN(time.tm_year); | |
time.tm_mon--; | |
startup_time = kernel_mktime(&time); | |
} | |
static long memory_end = 0; | |
static long buffer_memory_end = 0; | |
static long main_memory_start = 0; | |
struct drive_info { char dummy[32]; } drive_info; | |
void main(void) /* This really IS void, no error here. */ | |
{ /* The startup routine assumes (well, ...) this */ | |
/* | |
* Interrupts are still disabled. Do necessary setups, then | |
* enable them | |
*/ | |
ROOT_DEV = ORIG_ROOT_DEV; | |
drive_info = DRIVE_INFO; | |
memory_end = (1<<20) + (EXT_MEM_K<<10); | |
memory_end &= 0xfffff000; | |
if (memory_end > 16*1024*1024) | |
memory_end = 16*1024*1024; | |
if (memory_end > 12*1024*1024) | |
buffer_memory_end = 4*1024*1024; | |
else if (memory_end > 6*1024*1024) | |
buffer_memory_end = 2*1024*1024; | |
else | |
buffer_memory_end = 1*1024*1024; | |
main_memory_start = buffer_memory_end; | |
#ifdef RAMDISK | |
main_memory_start += rd_init(main_memory_start, RAMDISK*1024); | |
#endif | |
mem_init(main_memory_start,memory_end); | |
trap_init(); | |
blk_dev_init(); | |
chr_dev_init(); | |
tty_init(); | |
time_init(); | |
sched_init(); | |
buffer_init(buffer_memory_end); | |
hd_init(); | |
floppy_init(); | |
sti(); | |
move_to_user_mode(); | |
if (!fork()) { /* we count on this going ok */ | |
init(); | |
} | |
/* | |
* NOTE!! For any other task 'pause()' would mean we have to get a | |
* signal to awaken, but task0 is the sole exception (see 'schedule()') | |
* as task 0 gets activated at every idle moment (when no other tasks | |
* can run). For task0 'pause()' just means we go check if some other | |
* task can run, and if not we return here. | |
*/ | |
for(;;) pause(); | |
} | |
static int printf(const char *fmt, ...) | |
{ | |
va_list args; | |
int i; | |
va_start(args, fmt); | |
write(1,printbuf,i=vsprintf(printbuf, fmt, args)); | |
va_end(args); | |
return i; | |
} | |
static char * argv_rc[] = { "/bin/sh", NULL }; | |
static char * envp_rc[] = { "HOME=/", NULL }; | |
static char * argv[] = { "-/bin/sh",NULL }; | |
static char * envp[] = { "HOME=/usr/root", NULL }; | |
void init(void) | |
{ | |
int pid,i; | |
setup((void *) &drive_info); | |
(void) open("/dev/tty0",O_RDWR,0); | |
(void) dup(0); | |
(void) dup(0); | |
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS, | |
NR_BUFFERS*BLOCK_SIZE); | |
printf("Free mem: %d bytes\n\r",memory_end-main_memory_start); | |
if (!(pid=fork())) { | |
close(0); | |
if (open("/etc/rc",O_RDONLY,0)) | |
_exit(1); | |
execve("/bin/sh",argv_rc,envp_rc); | |
_exit(2); | |
} | |
if (pid>0) | |
while (pid != wait(&i)) | |
/* nothing */; | |
while (1) { | |
if ((pid=fork())<0) { | |
printf("Fork failed in init\r\n"); | |
continue; | |
} | |
if (!pid) { | |
close(0);close(1);close(2); | |
setsid(); | |
(void) open("/dev/tty0",O_RDWR,0); | |
(void) dup(0); | |
(void) dup(0); | |
_exit(execve("/bin/sh",argv,envp)); | |
} | |
while (1) | |
if (pid == wait(&i)) | |
break; | |
printf("\n\rchild %d died with code %04x\n\r",pid,i); | |
sync(); | |
} | |
_exit(0); /* NOTE! _exit, not exit() */ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* linux/kernel/fork.c | |
* | |
* (C) 1991 Linus Torvalds | |
*/ | |
/* | |
* 'fork.c' contains the help-routines for the 'fork' system call | |
* (see also system_call.s), and some misc functions ('verify_area'). | |
* Fork is rather simple, once you get the hang of it, but the memory | |
* management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' | |
*/ | |
#include <errno.h> | |
#include <linux/sched.h> | |
#include <linux/kernel.h> | |
#include <asm/segment.h> | |
#include <asm/system.h> | |
extern void write_verify(unsigned long address); | |
long last_pid=0; | |
void verify_area(void * addr,int size) | |
{ | |
unsigned long start; | |
start = (unsigned long) addr; | |
size += start & 0xfff; | |
start &= 0xfffff000; | |
start += get_base(current->ldt[2]); | |
while (size>0) { | |
size -= 4096; | |
write_verify(start); | |
start += 4096; | |
} | |
} | |
int copy_mem(int nr,struct task_struct * p) | |
{ | |
unsigned long old_data_base,new_data_base,data_limit; | |
unsigned long old_code_base,new_code_base,code_limit; | |
code_limit=get_limit(0x0f); | |
data_limit=get_limit(0x17); | |
old_code_base = get_base(current->ldt[1]); | |
old_data_base = get_base(current->ldt[2]); | |
if (old_data_base != old_code_base) | |
panic("We don't support separate I&D"); | |
if (data_limit < code_limit) | |
panic("Bad data_limit"); | |
new_data_base = new_code_base = nr * 0x4000000; | |
p->start_code = new_code_base; | |
set_base(p->ldt[1],new_code_base); | |
set_base(p->ldt[2],new_data_base); | |
if (copy_page_tables(old_data_base,new_data_base,data_limit)) { | |
free_page_tables(new_data_base,data_limit); | |
return -ENOMEM; | |
} | |
return 0; | |
} | |
/* | |
* Ok, this is the main fork-routine. It copies the system process | |
* information (task[nr]) and sets up the necessary registers. It | |
* also copies the data segment in it's entirety. | |
*/ | |
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none, | |
long ebx,long ecx,long edx, | |
long fs,long es,long ds, | |
long eip,long cs,long eflags,long esp,long ss) | |
{ | |
struct task_struct *p; | |
int i; | |
struct file *f; | |
p = (struct task_struct *) get_free_page(); | |
if (!p) | |
return -EAGAIN; | |
task[nr] = p; | |
*p = *current; /* NOTE! this doesn't copy the supervisor stack */ | |
p->state = TASK_UNINTERRUPTIBLE; | |
p->pid = last_pid; | |
p->father = current->pid; | |
p->counter = p->priority; | |
p->signal = 0; | |
p->alarm = 0; | |
p->leader = 0; /* process leadership doesn't inherit */ | |
p->utime = p->stime = 0; | |
p->cutime = p->cstime = 0; | |
p->start_time = jiffies; | |
p->tss.back_link = 0; | |
p->tss.esp0 = PAGE_SIZE + (long) p; | |
p->tss.ss0 = 0x10; | |
p->tss.eip = eip; | |
p->tss.eflags = eflags; | |
p->tss.eax = 0; | |
p->tss.ecx = ecx; | |
p->tss.edx = edx; | |
p->tss.ebx = ebx; | |
p->tss.esp = esp; | |
p->tss.ebp = ebp; | |
p->tss.esi = esi; | |
p->tss.edi = edi; | |
p->tss.es = es & 0xffff; | |
p->tss.cs = cs & 0xffff; | |
p->tss.ss = ss & 0xffff; | |
p->tss.ds = ds & 0xffff; | |
p->tss.fs = fs & 0xffff; | |
p->tss.gs = gs & 0xffff; | |
p->tss.ldt = _LDT(nr); | |
p->tss.trace_bitmap = 0x80000000; | |
if (last_task_used_math == current) | |
__asm__("clts ; fnsave %0"::"m" (p->tss.i387)); | |
if (copy_mem(nr,p)) { | |
task[nr] = NULL; | |
free_page((long) p); | |
return -EAGAIN; | |
} | |
for (i=0; i<NR_OPEN;i++) | |
if (f=p->filp[i]) | |
f->f_count++; | |
if (current->pwd) | |
current->pwd->i_count++; | |
if (current->root) | |
current->root->i_count++; | |
if (current->executable) | |
current->executable->i_count++; | |
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); | |
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt)); | |
p->state = TASK_RUNNING; /* do this last, just in case */ | |
return last_pid; | |
} | |
int find_empty_process(void) | |
{ | |
int i; | |
repeat: | |
if ((++last_pid)<0) last_pid=1; | |
for(i=0 ; i<NR_TASKS ; i++) | |
if (task[i] && task[i]->pid == last_pid) goto repeat; | |
for(i=1 ; i<NR_TASKS ; i++) | |
if (!task[i]) | |
return i; | |
return -EAGAIN; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* linux/kernel/sched.c | |
* | |
* (C) 1991 Linus Torvalds | |
*/ | |
/* | |
* 'sched.c' is the main kernel file. It contains scheduling primitives | |
* (sleep_on, wakeup, schedule etc) as well as a number of simple system | |
* call functions (type getpid(), which just extracts a field from | |
* current-task | |
*/ | |
#include <linux/sched.h> | |
#include <linux/kernel.h> | |
#include <linux/sys.h> | |
#include <linux/fdreg.h> | |
#include <asm/system.h> | |
#include <asm/io.h> | |
#include <asm/segment.h> | |
#include <signal.h> | |
#define _S(nr) (1<<((nr)-1)) | |
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) | |
void show_task(int nr,struct task_struct * p) | |
{ | |
int i,j = 4096-sizeof(struct task_struct); | |
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state); | |
i=0; | |
while (i<j && !((char *)(p+1))[i]) | |
i++; | |
printk("%d (of %d) chars free in kernel stack\n\r",i,j); | |
} | |
void show_stat(void) | |
{ | |
int i; | |
for (i=0;i<NR_TASKS;i++) | |
if (task[i]) | |
show_task(i,task[i]); | |
} | |
#define LATCH (1193180/HZ) | |
extern void mem_use(void); | |
extern int timer_interrupt(void); | |
extern int system_call(void); | |
union task_union { | |
struct task_struct task; | |
char stack[PAGE_SIZE]; | |
}; | |
static union task_union init_task = {INIT_TASK,}; | |
long volatile jiffies=0; | |
long startup_time=0; | |
struct task_struct *current = &(init_task.task); | |
struct task_struct *last_task_used_math = NULL; | |
struct task_struct * task[NR_TASKS] = {&(init_task.task), }; | |
long user_stack [ PAGE_SIZE>>2 ] ; | |
struct { | |
long * a; | |
short b; | |
} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 }; | |
/* | |
* 'math_state_restore()' saves the current math information in the | |
* old math state array, and gets the new ones from the current task | |
*/ | |
void math_state_restore() | |
{ | |
if (last_task_used_math == current) | |
return; | |
__asm__("fwait"); | |
if (last_task_used_math) { | |
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); | |
} | |
last_task_used_math=current; | |
if (current->used_math) { | |
__asm__("frstor %0"::"m" (current->tss.i387)); | |
} else { | |
__asm__("fninit"::); | |
current->used_math=1; | |
} | |
} | |
/* | |
* 'schedule()' is the scheduler function. This is GOOD CODE! There | |
* probably won't be any reason to change this, as it should work well | |
* in all circumstances (ie gives IO-bound processes good response etc). | |
* The one thing you might take a look at is the signal-handler code here. | |
* | |
* NOTE!! Task 0 is the 'idle' task, which gets called when no other | |
* tasks can run. It can not be killed, and it cannot sleep. The 'state' | |
* information in task[0] is never used. | |
*/ | |
void schedule(void) | |
{ | |
int i,next,c; | |
struct task_struct ** p; | |
/* check alarm, wake up any interruptible tasks that have got a signal */ | |
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) | |
if (*p) { | |
if ((*p)->alarm && (*p)->alarm < jiffies) { | |
(*p)->signal |= (1<<(SIGALRM-1)); | |
(*p)->alarm = 0; | |
} | |
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) && | |
(*p)->state==TASK_INTERRUPTIBLE) | |
(*p)->state=TASK_RUNNING; | |
} | |
/* this is the scheduler proper: */ | |
while (1) { | |
c = -1; | |
next = 0; | |
i = NR_TASKS; | |
p = &task[NR_TASKS]; | |
while (--i) { | |
if (!*--p) | |
continue; | |
if ((*p)->state == TASK_RUNNING && (*p)->counter > c) | |
c = (*p)->counter, next = i; | |
} | |
if (c) break; | |
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) | |
if (*p) | |
(*p)->counter = ((*p)->counter >> 1) + | |
(*p)->priority; | |
} | |
switch_to(next); | |
} | |
int sys_pause(void) | |
{ | |
current->state = TASK_INTERRUPTIBLE; | |
schedule(); | |
return 0; | |
} | |
void sleep_on(struct task_struct **p) | |
{ | |
struct task_struct *tmp; | |
if (!p) | |
return; | |
if (current == &(init_task.task)) | |
panic("task[0] trying to sleep"); | |
tmp = *p; | |
*p = current; | |
current->state = TASK_UNINTERRUPTIBLE; | |
schedule(); | |
if (tmp) | |
tmp->state=0; | |
} | |
void interruptible_sleep_on(struct task_struct **p) | |
{ | |
struct task_struct *tmp; | |
if (!p) | |
return; | |
if (current == &(init_task.task)) | |
panic("task[0] trying to sleep"); | |
tmp=*p; | |
*p=current; | |
repeat: current->state = TASK_INTERRUPTIBLE; | |
schedule(); | |
if (*p && *p != current) { | |
(**p).state=0; | |
goto repeat; | |
} | |
*p=NULL; | |
if (tmp) | |
tmp->state=0; | |
} | |
void wake_up(struct task_struct **p) | |
{ | |
if (p && *p) { | |
(**p).state=0; | |
*p=NULL; | |
} | |
} | |
/* | |
* OK, here are some floppy things that shouldn't be in the kernel | |
* proper. They are here because the floppy needs a timer, and this | |
* was the easiest way of doing it. | |
*/ | |
static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL}; | |
static int mon_timer[4]={0,0,0,0}; | |
static int moff_timer[4]={0,0,0,0}; | |
unsigned char current_DOR = 0x0C; | |
int ticks_to_floppy_on(unsigned int nr) | |
{ | |
extern unsigned char selected; | |
unsigned char mask = 0x10 << nr; | |
if (nr>3) | |
panic("floppy_on: nr>3"); | |
moff_timer[nr]=10000; /* 100 s = very big :-) */ | |
cli(); /* use floppy_off to turn it off */ | |
mask |= current_DOR; | |
if (!selected) { | |
mask &= 0xFC; | |
mask |= nr; | |
} | |
if (mask != current_DOR) { | |
outb(mask,FD_DOR); | |
if ((mask ^ current_DOR) & 0xf0) | |
mon_timer[nr] = HZ/2; | |
else if (mon_timer[nr] < 2) | |
mon_timer[nr] = 2; | |
current_DOR = mask; | |
} | |
sti(); | |
return mon_timer[nr]; | |
} | |
void floppy_on(unsigned int nr) | |
{ | |
cli(); | |
while (ticks_to_floppy_on(nr)) | |
sleep_on(nr+wait_motor); | |
sti(); | |
} | |
void floppy_off(unsigned int nr) | |
{ | |
moff_timer[nr]=3*HZ; | |
} | |
void do_floppy_timer(void) | |
{ | |
int i; | |
unsigned char mask = 0x10; | |
for (i=0 ; i<4 ; i++,mask <<= 1) { | |
if (!(mask & current_DOR)) | |
continue; | |
if (mon_timer[i]) { | |
if (!--mon_timer[i]) | |
wake_up(i+wait_motor); | |
} else if (!moff_timer[i]) { | |
current_DOR &= ~mask; | |
outb(current_DOR,FD_DOR); | |
} else | |
moff_timer[i]--; | |
} | |
} | |
#define TIME_REQUESTS 64 | |
static struct timer_list { | |
long jiffies; | |
void (*fn)(); | |
struct timer_list * next; | |
} timer_list[TIME_REQUESTS], * next_timer = NULL; | |
void add_timer(long jiffies, void (*fn)(void)) | |
{ | |
struct timer_list * p; | |
if (!fn) | |
return; | |
cli(); | |
if (jiffies <= 0) | |
(fn)(); | |
else { | |
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++) | |
if (!p->fn) | |
break; | |
if (p >= timer_list + TIME_REQUESTS) | |
panic("No more time requests free"); | |
p->fn = fn; | |
p->jiffies = jiffies; | |
p->next = next_timer; | |
next_timer = p; | |
while (p->next && p->next->jiffies < p->jiffies) { | |
p->jiffies -= p->next->jiffies; | |
fn = p->fn; | |
p->fn = p->next->fn; | |
p->next->fn = fn; | |
jiffies = p->jiffies; | |
p->jiffies = p->next->jiffies; | |
p->next->jiffies = jiffies; | |
p = p->next; | |
} | |
} | |
sti(); | |
} | |
void do_timer(long cpl) | |
{ | |
extern int beepcount; | |
extern void sysbeepstop(void); | |
if (beepcount) | |
if (!--beepcount) | |
sysbeepstop(); | |
if (cpl) | |
current->utime++; | |
else | |
current->stime++; | |
if (next_timer) { | |
next_timer->jiffies--; | |
while (next_timer && next_timer->jiffies <= 0) { | |
void (*fn)(void); | |
fn = next_timer->fn; | |
next_timer->fn = NULL; | |
next_timer = next_timer->next; | |
(fn)(); | |
} | |
} | |
if (current_DOR & 0xf0) | |
do_floppy_timer(); | |
if ((--current->counter)>0) return; | |
current->counter=0; | |
if (!cpl) return; | |
schedule(); | |
} | |
int sys_alarm(long seconds) | |
{ | |
int old = current->alarm; | |
if (old) | |
old = (old - jiffies) / HZ; | |
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0; | |
return (old); | |
} | |
int sys_getpid(void) | |
{ | |
return current->pid; | |
} | |
int sys_getppid(void) | |
{ | |
return current->father; | |
} | |
int sys_getuid(void) | |
{ | |
return current->uid; | |
} | |
int sys_geteuid(void) | |
{ | |
return current->euid; | |
} | |
int sys_getgid(void) | |
{ | |
return current->gid; | |
} | |
int sys_getegid(void) | |
{ | |
return current->egid; | |
} | |
int sys_nice(long increment) | |
{ | |
if (current->priority-increment>0) | |
current->priority -= increment; | |
return 0; | |
} | |
void sched_init(void) | |
{ | |
int i; | |
struct desc_struct * p; | |
if (sizeof(struct sigaction) != 16) | |
panic("Struct sigaction MUST be 16 bytes"); | |
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); | |
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); | |
p = gdt+2+FIRST_TSS_ENTRY; | |
for(i=1;i<NR_TASKS;i++) { | |
task[i] = NULL; | |
p->a=p->b=0; | |
p++; | |
p->a=p->b=0; | |
p++; | |
} | |
/* Clear NT, so that we won't have troubles with that later on */ | |
__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); | |
ltr(0); | |
lldt(0); | |
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */ | |
outb_p(LATCH & 0xff , 0x40); /* LSB */ | |
outb(LATCH >> 8 , 0x40); /* MSB */ | |
set_intr_gate(0x20,&timer_interrupt); | |
outb(inb_p(0x21)&~0x01,0x21); | |
set_system_gate(0x80,&system_call); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* linux/kernel/system_call.s | |
* | |
* (C) 1991 Linus Torvalds | |
*/ | |
/* | |
* system_call.s contains the system-call low-level handling routines. | |
* This also contains the timer-interrupt handler, as some of the code is | |
* the same. The hd- and flopppy-interrupts are also here. | |
* | |
* NOTE: This code handles signal-recognition, which happens every time | |
* after a timer-interrupt and after each system call. Ordinary interrupts | |
* don't handle signal-recognition, as that would clutter them up totally | |
* unnecessarily. | |
* | |
* Stack layout in 'ret_from_system_call': | |
* | |
* 0(%esp) - %eax | |
* 4(%esp) - %ebx | |
* 8(%esp) - %ecx | |
* C(%esp) - %edx | |
* 10(%esp) - %fs | |
* 14(%esp) - %es | |
* 18(%esp) - %ds | |
* 1C(%esp) - %eip | |
* 20(%esp) - %cs | |
* 24(%esp) - %eflags | |
* 28(%esp) - %oldesp | |
* 2C(%esp) - %oldss | |
*/ | |
SIG_CHLD = 17 | |
EAX = 0x00 | |
EBX = 0x04 | |
ECX = 0x08 | |
EDX = 0x0C | |
FS = 0x10 | |
ES = 0x14 | |
DS = 0x18 | |
EIP = 0x1C | |
CS = 0x20 | |
EFLAGS = 0x24 | |
OLDESP = 0x28 | |
OLDSS = 0x2C | |
state = 0 # these are offsets into the task-struct. | |
counter = 4 | |
priority = 8 | |
signal = 12 | |
sigaction = 16 # MUST be 16 (=len of sigaction) | |
blocked = (33*16) | |
# offsets within sigaction | |
sa_handler = 0 | |
sa_mask = 4 | |
sa_flags = 8 | |
sa_restorer = 12 | |
nr_system_calls = 72 | |
/* | |
* Ok, I get parallel printer interrupts while using the floppy for some | |
* strange reason. Urgel. Now I just ignore them. | |
*/ | |
.globl _system_call,_sys_fork,_timer_interrupt,_sys_execve | |
.globl _hd_interrupt,_floppy_interrupt,_parallel_interrupt | |
.globl _device_not_available, _coprocessor_error | |
.align 2 | |
bad_sys_call: | |
movl $-1,%eax | |
iret | |
.align 2 | |
reschedule: | |
pushl $ret_from_sys_call | |
jmp _schedule | |
.align 2 | |
_system_call: | |
cmpl $nr_system_calls-1,%eax | |
ja bad_sys_call | |
push %ds | |
push %es | |
push %fs | |
pushl %edx | |
pushl %ecx # push %ebx,%ecx,%edx as parameters | |
pushl %ebx # to the system call | |
movl $0x10,%edx # set up ds,es to kernel space | |
mov %dx,%ds | |
mov %dx,%es | |
movl $0x17,%edx # fs points to local data space | |
mov %dx,%fs | |
call _sys_call_table(,%eax,4) | |
pushl %eax | |
movl _current,%eax | |
cmpl $0,state(%eax) # state | |
jne reschedule | |
cmpl $0,counter(%eax) # counter | |
je reschedule | |
ret_from_sys_call: | |
movl _current,%eax # task[0] cannot have signals | |
cmpl _task,%eax | |
je 3f | |
cmpw $0x0f,CS(%esp) # was old code segment supervisor ? | |
jne 3f | |
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? | |
jne 3f | |
movl signal(%eax),%ebx | |
movl blocked(%eax),%ecx | |
notl %ecx | |
andl %ebx,%ecx | |
bsfl %ecx,%ecx | |
je 3f | |
btrl %ecx,%ebx | |
movl %ebx,signal(%eax) | |
incl %ecx | |
pushl %ecx | |
call _do_signal | |
popl %eax | |
3: popl %eax | |
popl %ebx | |
popl %ecx | |
popl %edx | |
pop %fs | |
pop %es | |
pop %ds | |
iret | |
.align 2 | |
_coprocessor_error: | |
push %ds | |
push %es | |
push %fs | |
pushl %edx | |
pushl %ecx | |
pushl %ebx | |
pushl %eax | |
movl $0x10,%eax | |
mov %ax,%ds | |
mov %ax,%es | |
movl $0x17,%eax | |
mov %ax,%fs | |
pushl $ret_from_sys_call | |
jmp _math_error | |
.align 2 | |
_device_not_available: | |
push %ds | |
push %es | |
push %fs | |
pushl %edx | |
pushl %ecx | |
pushl %ebx | |
pushl %eax | |
movl $0x10,%eax | |
mov %ax,%ds | |
mov %ax,%es | |
movl $0x17,%eax | |
mov %ax,%fs | |
pushl $ret_from_sys_call | |
clts # clear TS so that we can use math | |
movl %cr0,%eax | |
testl $0x4,%eax # EM (math emulation bit) | |
je _math_state_restore | |
pushl %ebp | |
pushl %esi | |
pushl %edi | |
call _math_emulate | |
popl %edi | |
popl %esi | |
popl %ebp | |
ret | |
.align 2 | |
_timer_interrupt: | |
push %ds # save ds,es and put kernel data space | |
push %es # into them. %fs is used by _system_call | |
push %fs | |
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't | |
pushl %ecx # save those across function calls. %ebx | |
pushl %ebx # is saved as we use that in ret_sys_call | |
pushl %eax | |
movl $0x10,%eax | |
mov %ax,%ds | |
mov %ax,%es | |
movl $0x17,%eax | |
mov %ax,%fs | |
incl _jiffies | |
movb $0x20,%al # EOI to interrupt controller #1 | |
outb %al,$0x20 | |
movl CS(%esp),%eax | |
andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor) | |
pushl %eax | |
call _do_timer # 'do_timer(long CPL)' does everything from | |
addl $4,%esp # task switching to accounting ... | |
jmp ret_from_sys_call | |
.align 2 | |
_sys_execve: | |
lea EIP(%esp),%eax | |
pushl %eax | |
call _do_execve | |
addl $4,%esp | |
ret | |
.align 2 | |
_sys_fork: | |
call _find_empty_process | |
testl %eax,%eax | |
js 1f | |
push %gs | |
pushl %esi | |
pushl %edi | |
pushl %ebp | |
pushl %eax | |
call _copy_process | |
addl $20,%esp | |
1: ret | |
_hd_interrupt: | |
pushl %eax | |
pushl %ecx | |
pushl %edx | |
push %ds | |
push %es | |
push %fs | |
movl $0x10,%eax | |
mov %ax,%ds | |
mov %ax,%es | |
movl $0x17,%eax | |
mov %ax,%fs | |
movb $0x20,%al | |
outb %al,$0xA0 # EOI to interrupt controller #1 | |
jmp 1f # give port chance to breathe | |
1: jmp 1f | |
1: xorl %edx,%edx | |
xchgl _do_hd,%edx | |
testl %edx,%edx | |
jne 1f | |
movl $_unexpected_hd_interrupt,%edx | |
1: outb %al,$0x20 | |
call *%edx # "interesting" way of handling intr. | |
pop %fs | |
pop %es | |
pop %ds | |
popl %edx | |
popl %ecx | |
popl %eax | |
iret | |
_floppy_interrupt: | |
pushl %eax | |
pushl %ecx | |
pushl %edx | |
push %ds | |
push %es | |
push %fs | |
movl $0x10,%eax | |
mov %ax,%ds | |
mov %ax,%es | |
movl $0x17,%eax | |
mov %ax,%fs | |
movb $0x20,%al | |
outb %al,$0x20 # EOI to interrupt controller #1 | |
xorl %eax,%eax | |
xchgl _do_floppy,%eax | |
testl %eax,%eax | |
jne 1f | |
movl $_unexpected_floppy_interrupt,%eax | |
1: call *%eax # "interesting" way of handling intr. | |
pop %fs | |
pop %es | |
pop %ds | |
popl %edx | |
popl %ecx | |
popl %eax | |
iret | |
_parallel_interrupt: | |
pushl %eax | |
movb $0x20,%al | |
outb %al,$0x20 | |
popl %eax | |
iret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* linux/kernel/traps.c | |
* | |
* (C) 1991 Linus Torvalds | |
*/ | |
/* | |
* 'Traps.c' handles hardware traps and faults after we have saved some | |
* state in 'asm.s'. Currently mostly a debugging-aid, will be extended | |
* to mainly kill the offending process (probably by giving it a signal, | |
* but possibly by killing it outright if necessary). | |
*/ | |
#include <string.h> | |
#include <linux/head.h> | |
#include <linux/sched.h> | |
#include <linux/kernel.h> | |
#include <asm/system.h> | |
#include <asm/segment.h> | |
#include <asm/io.h> | |
#define get_seg_byte(seg,addr) ({ \ | |
register char __res; \ | |
__asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ | |
:"=a" (__res):"0" (seg),"m" (*(addr))); \ | |
__res;}) | |
#define get_seg_long(seg,addr) ({ \ | |
register unsigned long __res; \ | |
__asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ | |
:"=a" (__res):"0" (seg),"m" (*(addr))); \ | |
__res;}) | |
#define _fs() ({ \ | |
register unsigned short __res; \ | |
__asm__("mov %%fs,%%ax":"=a" (__res):); \ | |
__res;}) | |
int do_exit(long code); | |
void page_exception(void); | |
void divide_error(void); | |
void debug(void); | |
void nmi(void); | |
void int3(void); | |
void overflow(void); | |
void bounds(void); | |
void invalid_op(void); | |
void device_not_available(void); | |
void double_fault(void); | |
void coprocessor_segment_overrun(void); | |
void invalid_TSS(void); | |
void segment_not_present(void); | |
void stack_segment(void); | |
void general_protection(void); | |
void page_fault(void); | |
void coprocessor_error(void); | |
void reserved(void); | |
void parallel_interrupt(void); | |
void irq13(void); | |
static void die(char * str,long esp_ptr,long nr) | |
{ | |
long * esp = (long *) esp_ptr; | |
int i; | |
printk("%s: %04x\n\r",str,nr&0xffff); | |
printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n", | |
esp[1],esp[0],esp[2],esp[4],esp[3]); | |
printk("fs: %04x\n",_fs()); | |
printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17)); | |
if (esp[4] == 0x17) { | |
printk("Stack: "); | |
for (i=0;i<4;i++) | |
printk("%p ",get_seg_long(0x17,i+(long *)esp[3])); | |
printk("\n"); | |
} | |
str(i); | |
printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i); | |
for(i=0;i<10;i++) | |
printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0]))); | |
printk("\n\r"); | |
do_exit(11); /* play segment exception */ | |
} | |
void do_double_fault(long esp, long error_code) | |
{ | |
die("double fault",esp,error_code); | |
} | |
void do_general_protection(long esp, long error_code) | |
{ | |
die("general protection",esp,error_code); | |
} | |
void do_divide_error(long esp, long error_code) | |
{ | |
die("divide error",esp,error_code); | |
} | |
void do_int3(long * esp, long error_code, | |
long fs,long es,long ds, | |
long ebp,long esi,long edi, | |
long edx,long ecx,long ebx,long eax) | |
{ | |
int tr; | |
__asm__("str %%ax":"=a" (tr):"0" (0)); | |
printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r", | |
eax,ebx,ecx,edx); | |
printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r", | |
esi,edi,ebp,(long) esp); | |
printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r", | |
ds,es,fs,tr); | |
printk("EIP: %8x CS: %4x EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]); | |
} | |
void do_nmi(long esp, long error_code) | |
{ | |
die("nmi",esp,error_code); | |
} | |
void do_debug(long esp, long error_code) | |
{ | |
die("debug",esp,error_code); | |
} | |
void do_overflow(long esp, long error_code) | |
{ | |
die("overflow",esp,error_code); | |
} | |
void do_bounds(long esp, long error_code) | |
{ | |
die("bounds",esp,error_code); | |
} | |
void do_invalid_op(long esp, long error_code) | |
{ | |
die("invalid operand",esp,error_code); | |
} | |
void do_device_not_available(long esp, long error_code) | |
{ | |
die("device not available",esp,error_code); | |
} | |
void do_coprocessor_segment_overrun(long esp, long error_code) | |
{ | |
die("coprocessor segment overrun",esp,error_code); | |
} | |
void do_invalid_TSS(long esp,long error_code) | |
{ | |
die("invalid TSS",esp,error_code); | |
} | |
void do_segment_not_present(long esp,long error_code) | |
{ | |
die("segment not present",esp,error_code); | |
} | |
void do_stack_segment(long esp,long error_code) | |
{ | |
die("stack segment",esp,error_code); | |
} | |
void do_coprocessor_error(long esp, long error_code) | |
{ | |
if (last_task_used_math != current) | |
return; | |
die("coprocessor error",esp,error_code); | |
} | |
void do_reserved(long esp, long error_code) | |
{ | |
die("reserved (15,17-47) error",esp,error_code); | |
} | |
void trap_init(void) | |
{ | |
int i; | |
set_trap_gate(0,÷_error); | |
set_trap_gate(1,&debug); | |
set_trap_gate(2,&nmi); | |
set_system_gate(3,&int3); /* int3-5 can be called from all */ | |
set_system_gate(4,&overflow); | |
set_system_gate(5,&bounds); | |
set_trap_gate(6,&invalid_op); | |
set_trap_gate(7,&device_not_available); | |
set_trap_gate(8,&double_fault); | |
set_trap_gate(9,&coprocessor_segment_overrun); | |
set_trap_gate(10,&invalid_TSS); | |
set_trap_gate(11,&segment_not_present); | |
set_trap_gate(12,&stack_segment); | |
set_trap_gate(13,&general_protection); | |
set_trap_gate(14,&page_fault); | |
set_trap_gate(15,&reserved); | |
set_trap_gate(16,&coprocessor_error); | |
for (i=17;i<48;i++) | |
set_trap_gate(i,&reserved); | |
set_trap_gate(45,&irq13); | |
outb_p(inb_p(0x21)&0xfb,0x21); | |
outb(inb_p(0xA1)&0xdf,0xA1); | |
set_trap_gate(39,¶llel_interrupt); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* linux/mm/memory.c | |
* | |
* (C) 1991 Linus Torvalds | |
*/ | |
/* | |
* demand-loading started 01.12.91 - seems it is high on the list of | |
* things wanted, and it should be easy to implement. - Linus | |
*/ | |
/* | |
* Ok, demand-loading was easy, shared pages a little bit tricker. Shared | |
* pages started 02.12.91, seems to work. - Linus. | |
* | |
* Tested sharing by executing about 30 /bin/sh: under the old kernel it | |
* would have taken more than the 6M I have free, but it worked well as | |
* far as I could see. | |
* | |
* Also corrected some "invalidate()"s - I wasn't doing enough of them. | |
*/ | |
#include <signal.h> | |
#include <asm/system.h> | |
#include <linux/sched.h> | |
#include <linux/head.h> | |
#include <linux/kernel.h> | |
volatile void do_exit(long code); | |
static inline volatile void oom(void) | |
{ | |
printk("out of memory\n\r"); | |
do_exit(SIGSEGV); | |
} | |
#define invalidate() \ | |
__asm__("movl %%eax,%%cr3"::"a" (0)) | |
/* these are not to be changed without changing head.s etc */ | |
#define LOW_MEM 0x100000 | |
#define PAGING_MEMORY (15*1024*1024) | |
#define PAGING_PAGES (PAGING_MEMORY>>12) | |
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12) | |
#define USED 100 | |
#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \ | |
current->start_code + current->end_code) | |
static long HIGH_MEMORY = 0; | |
#define copy_page(from,to) \ | |
__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si") | |
static unsigned char mem_map [ PAGING_PAGES ] = {0,}; | |
/* | |
* Get physical address of first (actually last :-) free page, and mark it | |
* used. If no free pages left, return 0. | |
*/ | |
unsigned long get_free_page(void) | |
{ | |
register unsigned long __res asm("ax"); | |
__asm__("std ; repne ; scasb\n\t" | |
"jne 1f\n\t" | |
"movb $1,1(%%edi)\n\t" | |
"sall $12,%%ecx\n\t" | |
"addl %2,%%ecx\n\t" | |
"movl %%ecx,%%edx\n\t" | |
"movl $1024,%%ecx\n\t" | |
"leal 4092(%%edx),%%edi\n\t" | |
"rep ; stosl\n\t" | |
"movl %%edx,%%eax\n" | |
"1:" | |
:"=a" (__res) | |
:"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), | |
"D" (mem_map+PAGING_PAGES-1) | |
:"di","cx","dx"); | |
return __res; | |
} | |
/* | |
* Free a page of memory at physical address 'addr'. Used by | |
* 'free_page_tables()' | |
*/ | |
void free_page(unsigned long addr) | |
{ | |
if (addr < LOW_MEM) return; | |
if (addr >= HIGH_MEMORY) | |
panic("trying to free nonexistent page"); | |
addr -= LOW_MEM; | |
addr >>= 12; | |
if (mem_map[addr]--) return; | |
mem_map[addr]=0; | |
panic("trying to free free page"); | |
} | |
/* | |
* This function frees a continuos block of page tables, as needed | |
* by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks. | |
*/ | |
int free_page_tables(unsigned long from,unsigned long size) | |
{ | |
unsigned long *pg_table; | |
unsigned long * dir, nr; | |
if (from & 0x3fffff) | |
panic("free_page_tables called with wrong alignment"); | |
if (!from) | |
panic("Trying to free up swapper memory space"); | |
size = (size + 0x3fffff) >> 22; | |
dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ | |
for ( ; size-->0 ; dir++) { | |
if (!(1 & *dir)) | |
continue; | |
pg_table = (unsigned long *) (0xfffff000 & *dir); | |
for (nr=0 ; nr<1024 ; nr++) { | |
if (1 & *pg_table) | |
free_page(0xfffff000 & *pg_table); | |
*pg_table = 0; | |
pg_table++; | |
} | |
free_page(0xfffff000 & *dir); | |
*dir = 0; | |
} | |
invalidate(); | |
return 0; | |
} | |
/* | |
* Well, here is one of the most complicated functions in mm. It | |
* copies a range of linerar addresses by copying only the pages. | |
* Let's hope this is bug-free, 'cause this one I don't want to debug :-) | |
* | |
* Note! We don't copy just any chunks of memory - addresses have to | |
* be divisible by 4Mb (one page-directory entry), as this makes the | |
* function easier. It's used only by fork anyway. | |
* | |
* NOTE 2!! When from==0 we are copying kernel space for the first | |
* fork(). Then we DONT want to copy a full page-directory entry, as | |
* that would lead to some serious memory waste - we just copy the | |
* first 160 pages - 640kB. Even that is more than we need, but it | |
* doesn't take any more memory - we don't copy-on-write in the low | |
* 1 Mb-range, so the pages can be shared with the kernel. Thus the | |
* special case for nr=xxxx. | |
*/ | |
int copy_page_tables(unsigned long from,unsigned long to,long size) | |
{ | |
unsigned long * from_page_table; | |
unsigned long * to_page_table; | |
unsigned long this_page; | |
unsigned long * from_dir, * to_dir; | |
unsigned long nr; | |
if ((from&0x3fffff) || (to&0x3fffff)) | |
panic("copy_page_tables called with wrong alignment"); | |
from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ | |
to_dir = (unsigned long *) ((to>>20) & 0xffc); | |
size = ((unsigned) (size+0x3fffff)) >> 22; | |
for( ; size-->0 ; from_dir++,to_dir++) { | |
if (1 & *to_dir) | |
panic("copy_page_tables: already exist"); | |
if (!(1 & *from_dir)) | |
continue; | |
from_page_table = (unsigned long *) (0xfffff000 & *from_dir); | |
if (!(to_page_table = (unsigned long *) get_free_page())) | |
return -1; /* Out of memory, see freeing */ | |
*to_dir = ((unsigned long) to_page_table) | 7; | |
nr = (from==0)?0xA0:1024; | |
for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { | |
this_page = *from_page_table; | |
if (!(1 & this_page)) | |
continue; | |
this_page &= ~2; | |
*to_page_table = this_page; | |
if (this_page > LOW_MEM) { | |
*from_page_table = this_page; | |
this_page -= LOW_MEM; | |
this_page >>= 12; | |
mem_map[this_page]++; | |
} | |
} | |
} | |
invalidate(); | |
return 0; | |
} | |
/* | |
* This function puts a page in memory at the wanted address. | |
* It returns the physical address of the page gotten, 0 if | |
* out of memory (either when trying to access page-table or | |
* page.) | |
*/ | |
unsigned long put_page(unsigned long page,unsigned long address) | |
{ | |
unsigned long tmp, *page_table; | |
/* NOTE !!! This uses the fact that _pg_dir=0 */ | |
if (page < LOW_MEM || page >= HIGH_MEMORY) | |
printk("Trying to put page %p at %p\n",page,address); | |
if (mem_map[(page-LOW_MEM)>>12] != 1) | |
printk("mem_map disagrees with %p at %p\n",page,address); | |
page_table = (unsigned long *) ((address>>20) & 0xffc); | |
if ((*page_table)&1) | |
page_table = (unsigned long *) (0xfffff000 & *page_table); | |
else { | |
if (!(tmp=get_free_page())) | |
return 0; | |
*page_table = tmp|7; | |
page_table = (unsigned long *) tmp; | |
} | |
page_table[(address>>12) & 0x3ff] = page | 7; | |
/* no need for invalidate */ | |
return page; | |
} | |
void un_wp_page(unsigned long * table_entry) | |
{ | |
unsigned long old_page,new_page; | |
old_page = 0xfffff000 & *table_entry; | |
if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) { | |
*table_entry |= 2; | |
invalidate(); | |
return; | |
} | |
if (!(new_page=get_free_page())) | |
oom(); | |
if (old_page >= LOW_MEM) | |
mem_map[MAP_NR(old_page)]--; | |
*table_entry = new_page | 7; | |
invalidate(); | |
copy_page(old_page,new_page); | |
} | |
/* | |
* This routine handles present pages, when users try to write | |
* to a shared page. It is done by copying the page to a new address | |
* and decrementing the shared-page counter for the old page. | |
* | |
* If it's in code space we exit with a segment error. | |
*/ | |
void do_wp_page(unsigned long error_code,unsigned long address) | |
{ | |
#if 0 | |
/* we cannot do this yet: the estdio library writes to code space */ | |
/* stupid, stupid. I really want the libc.a from GNU */ | |
if (CODE_SPACE(address)) | |
do_exit(SIGSEGV); | |
#endif | |
un_wp_page((unsigned long *) | |
(((address>>10) & 0xffc) + (0xfffff000 & | |
*((unsigned long *) ((address>>20) &0xffc))))); | |
} | |
void write_verify(unsigned long address) | |
{ | |
unsigned long page; | |
if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1)) | |
return; | |
page &= 0xfffff000; | |
page += ((address>>10) & 0xffc); | |
if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */ | |
un_wp_page((unsigned long *) page); | |
return; | |
} | |
void get_empty_page(unsigned long address) | |
{ | |
unsigned long tmp; | |
if (!(tmp=get_free_page()) || !put_page(tmp,address)) { | |
free_page(tmp); /* 0 is ok - ignored */ | |
oom(); | |
} | |
} | |
/* | |
* try_to_share() checks the page at address "address" in the task "p", | |
* to see if it exists, and if it is clean. If so, share it with the current | |
* task. | |
* | |
* NOTE! This assumes we have checked that p != current, and that they | |
* share the same executable. | |
*/ | |
static int try_to_share(unsigned long address, struct task_struct * p) | |
{ | |
unsigned long from; | |
unsigned long to; | |
unsigned long from_page; | |
unsigned long to_page; | |
unsigned long phys_addr; | |
from_page = to_page = ((address>>20) & 0xffc); | |
from_page += ((p->start_code>>20) & 0xffc); | |
to_page += ((current->start_code>>20) & 0xffc); | |
/* is there a page-directory at from? */ | |
from = *(unsigned long *) from_page; | |
if (!(from & 1)) | |
return 0; | |
from &= 0xfffff000; | |
from_page = from + ((address>>10) & 0xffc); | |
phys_addr = *(unsigned long *) from_page; | |
/* is the page clean and present? */ | |
if ((phys_addr & 0x41) != 0x01) | |
return 0; | |
phys_addr &= 0xfffff000; | |
if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM) | |
return 0; | |
to = *(unsigned long *) to_page; | |
if (!(to & 1)) | |
if (to = get_free_page()) | |
*(unsigned long *) to_page = to | 7; | |
else | |
oom(); | |
to &= 0xfffff000; | |
to_page = to + ((address>>10) & 0xffc); | |
if (1 & *(unsigned long *) to_page) | |
panic("try_to_share: to_page already exists"); | |
/* share them: write-protect */ | |
*(unsigned long *) from_page &= ~2; | |
*(unsigned long *) to_page = *(unsigned long *) from_page; | |
invalidate(); | |
phys_addr -= LOW_MEM; | |
phys_addr >>= 12; | |
mem_map[phys_addr]++; | |
return 1; | |
} | |
/* | |
* share_page() tries to find a process that could share a page with | |
* the current one. Address is the address of the wanted page relative | |
* to the current data space. | |
* | |
* We first check if it is at all feasible by checking executable->i_count. | |
* It should be >1 if there are other tasks sharing this inode. | |
*/ | |
static int share_page(unsigned long address) | |
{ | |
struct task_struct ** p; | |
if (!current->executable) | |
return 0; | |
if (current->executable->i_count < 2) | |
return 0; | |
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { | |
if (!*p) | |
continue; | |
if (current == *p) | |
continue; | |
if ((*p)->executable != current->executable) | |
continue; | |
if (try_to_share(address,*p)) | |
return 1; | |
} | |
return 0; | |
} | |
void do_no_page(unsigned long error_code,unsigned long address) | |
{ | |
int nr[4]; | |
unsigned long tmp; | |
unsigned long page; | |
int block,i; | |
address &= 0xfffff000; | |
tmp = address - current->start_code; | |
if (!current->executable || tmp >= current->end_data) { | |
get_empty_page(address); | |
return; | |
} | |
if (share_page(tmp)) | |
return; | |
if (!(page = get_free_page())) | |
oom(); | |
/* remember that 1 block is used for header */ | |
block = 1 + tmp/BLOCK_SIZE; | |
for (i=0 ; i<4 ; block++,i++) | |
nr[i] = bmap(current->executable,block); | |
bread_page(page,current->executable->i_dev,nr); | |
i = tmp + 4096 - current->end_data; | |
tmp = page + 4096; | |
while (i-- > 0) { | |
tmp--; | |
*(char *)tmp = 0; | |
} | |
if (put_page(page,address)) | |
return; | |
free_page(page); | |
oom(); | |
} | |
void mem_init(long start_mem, long end_mem) | |
{ | |
int i; | |
HIGH_MEMORY = end_mem; | |
for (i=0 ; i<PAGING_PAGES ; i++) | |
mem_map[i] = USED; | |
i = MAP_NR(start_mem); | |
end_mem -= start_mem; | |
end_mem >>= 12; | |
while (end_mem-->0) | |
mem_map[i++]=0; | |
} | |
void calc_mem(void) | |
{ | |
int i,j,k,free=0; | |
long * pg_tbl; | |
for(i=0 ; i<PAGING_PAGES ; i++) | |
if (!mem_map[i]) free++; | |
printk("%d pages free (of %d)\n\r",free,PAGING_PAGES); | |
for(i=2 ; i<1024 ; i++) { | |
if (1&pg_dir[i]) { | |
pg_tbl=(long *) (0xfffff000 & pg_dir[i]); | |
for(j=k=0 ; j<1024 ; j++) | |
if (pg_tbl[j]&1) | |
k++; | |
printk("Pg-dir[%d] uses %d pages\n",i,k); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment