From fdae69b4bb90c1623f7625c10106880f61f2a2a6 Mon Sep 17 00:00:00 2001 From: YanXu <2100704027@qq.com> Date: Fri, 9 Jul 2021 07:21:25 +0800 Subject: [PATCH] 1 --- 4/linux/Image | Bin 0 -> 135716 bytes 4/linux/Image.bkp | Bin 0 -> 131556 bytes 4/linux/Makefile | 125 +++ 4/linux/System.map.2 | 1087 +++++++++++++++++++++++++++ 4/linux/boot/bootsect | Bin 0 -> 544 bytes 4/linux/boot/bootsect.s | 260 +++++++ 4/linux/boot/head.s | 240 ++++++ 4/linux/boot/setup | Bin 0 -> 344 bytes 4/linux/boot/setup.s | 231 ++++++ 4/linux/execve2.patch | 41 + 4/linux/fs/Makefile | 101 +++ 4/linux/fs/bitmap.c | 168 +++++ 4/linux/fs/block_dev.c | 73 ++ 4/linux/fs/buffer.c | 384 ++++++++++ 4/linux/fs/char_dev.c | 104 +++ 4/linux/fs/exec.c | 578 ++++++++++++++ 4/linux/fs/fcntl.c | 75 ++ 4/linux/fs/file_dev.c | 90 +++ 4/linux/fs/file_table.c | 9 + 4/linux/fs/inode.c | 340 +++++++++ 4/linux/fs/ioctl.c | 46 ++ 4/linux/fs/namei.c | 783 +++++++++++++++++++ 4/linux/fs/open.c | 208 +++++ 4/linux/fs/pipe.c | 111 +++ 4/linux/fs/read_write.c | 103 +++ 4/linux/fs/select.c | 10 + 4/linux/fs/stat.c | 66 ++ 4/linux/fs/super.c | 282 +++++++ 4/linux/fs/truncate.c | 65 ++ 4/linux/include/.DS_Store | Bin 0 -> 6148 bytes 4/linux/include/a.out.h | 220 ++++++ 4/linux/include/asm/io.h | 24 + 4/linux/include/asm/memory.h | 15 + 4/linux/include/asm/segment.h | 65 ++ 4/linux/include/asm/system.h | 66 ++ 4/linux/include/assert.h | 34 + 4/linux/include/const.h | 15 + 4/linux/include/ctype.h | 34 + 4/linux/include/errno.h | 60 ++ 4/linux/include/fcntl.h | 56 ++ 4/linux/include/linux/config.h | 48 ++ 4/linux/include/linux/fdreg.h | 71 ++ 4/linux/include/linux/fs.h | 202 +++++ 4/linux/include/linux/hdreg.h | 65 ++ 4/linux/include/linux/head.h | 20 + 4/linux/include/linux/kernel.h | 22 + 4/linux/include/linux/mm.h | 10 + 4/linux/include/linux/sched.h | 239 ++++++ 4/linux/include/linux/sys.h | 122 +++ 4/linux/include/linux/tty.h | 77 ++ 4/linux/include/new.h | 30 + 4/linux/include/signal.h | 69 ++ 4/linux/include/stdarg.h | 28 + 4/linux/include/stddef.h | 19 + 4/linux/include/stdio.h | 256 +++++++ 4/linux/include/stdlib.h | 43 ++ 4/linux/include/string.h | 405 ++++++++++ 4/linux/include/sys/stat.h | 58 ++ 4/linux/include/sys/times.h | 15 + 4/linux/include/sys/types.h | 46 ++ 4/linux/include/sys/utsname.h | 16 + 4/linux/include/sys/wait.h | 23 + 4/linux/include/termios.h | 228 ++++++ 4/linux/include/time.h | 42 ++ 4/linux/include/unistd.h | 278 +++++++ 4/linux/include/utime.h | 13 + 4/linux/init/main.c | 216 ++++++ 4/linux/kernel/Makefile | 83 ++ 4/linux/kernel/asm.s | 146 ++++ 4/linux/kernel/blk_drv/Makefile | 58 ++ 4/linux/kernel/blk_drv/blk.h | 140 ++++ 4/linux/kernel/blk_drv/floppy.c | 463 ++++++++++++ 4/linux/kernel/blk_drv/hd.c | 351 +++++++++ 4/linux/kernel/blk_drv/ll_rw_blk.c | 165 ++++ 4/linux/kernel/blk_drv/ramdisk.c | 126 ++++ 4/linux/kernel/chr_drv/Makefile | 68 ++ 4/linux/kernel/chr_drv/console.c | 710 +++++++++++++++++ 4/linux/kernel/chr_drv/keyboard.2.s | 466 ++++++++++++ 4/linux/kernel/chr_drv/keyboard.S | 588 +++++++++++++++ 4/linux/kernel/chr_drv/rs_io.s | 147 ++++ 4/linux/kernel/chr_drv/serial.c | 59 ++ 4/linux/kernel/chr_drv/tty_io.c | 349 +++++++++ 4/linux/kernel/chr_drv/tty_ioctl.c | 204 +++++ 4/linux/kernel/exit.c | 197 +++++ 4/linux/kernel/fork.c | 148 ++++ 4/linux/kernel/math/Makefile | 43 ++ 4/linux/kernel/math/math_emulate.c | 42 ++ 4/linux/kernel/mktime.c | 58 ++ 4/linux/kernel/panic.c | 24 + 4/linux/kernel/printk.c | 41 + 4/linux/kernel/sched.c | 412 ++++++++++ 4/linux/kernel/signal.c | 161 ++++ 4/linux/kernel/sys.c | 292 +++++++ 4/linux/kernel/system_call.s | 298 ++++++++ 4/linux/kernel/traps.c | 208 +++++ 4/linux/kernel/vsprintf.c | 233 ++++++ 4/linux/lib/Makefile | 73 ++ 4/linux/lib/_exit.c | 13 + 4/linux/lib/close.c | 10 + 4/linux/lib/ctype.c | 35 + 4/linux/lib/dup.c | 10 + 4/linux/lib/errno.c | 7 + 4/linux/lib/execve.c | 10 + 4/linux/lib/malloc.c | 232 ++++++ 4/linux/lib/open.c | 25 + 4/linux/lib/setsid.c | 10 + 4/linux/lib/string.c | 14 + 4/linux/lib/wait.c | 16 + 4/linux/lib/write.c | 10 + 4/linux/mm/Makefile | 38 + 4/linux/mm/memory.c | 469 ++++++++++++ 4/linux/mm/memory.c.orig | 466 ++++++++++++ 4/linux/mm/page.s | 40 + 4/linux/new.patch | 11 + 4/linux/tools/build | Bin 0 -> 15144 bytes 4/linux/tools/build.c | 171 +++++ 4/linux/tools/system | Bin 0 -> 295153 bytes 117 files changed, 16770 insertions(+) create mode 100644 4/linux/Image create mode 100644 4/linux/Image.bkp create mode 100644 4/linux/Makefile create mode 100644 4/linux/System.map.2 create mode 100644 4/linux/boot/bootsect create mode 100644 4/linux/boot/bootsect.s create mode 100644 4/linux/boot/head.s create mode 100644 4/linux/boot/setup create mode 100644 4/linux/boot/setup.s create mode 100644 4/linux/execve2.patch create mode 100644 4/linux/fs/Makefile create mode 100644 4/linux/fs/bitmap.c create mode 100644 4/linux/fs/block_dev.c create mode 100644 4/linux/fs/buffer.c create mode 100644 4/linux/fs/char_dev.c create mode 100644 4/linux/fs/exec.c create mode 100644 4/linux/fs/fcntl.c create mode 100644 4/linux/fs/file_dev.c create mode 100644 4/linux/fs/file_table.c create mode 100644 4/linux/fs/inode.c create mode 100644 4/linux/fs/ioctl.c create mode 100644 4/linux/fs/namei.c create mode 100644 4/linux/fs/open.c create mode 100644 4/linux/fs/pipe.c create mode 100644 4/linux/fs/read_write.c create mode 100644 4/linux/fs/select.c create mode 100644 4/linux/fs/stat.c create mode 100644 4/linux/fs/super.c create mode 100644 4/linux/fs/truncate.c create mode 100644 4/linux/include/.DS_Store create mode 100644 4/linux/include/a.out.h create mode 100644 4/linux/include/asm/io.h create mode 100644 4/linux/include/asm/memory.h create mode 100644 4/linux/include/asm/segment.h create mode 100644 4/linux/include/asm/system.h create mode 100644 4/linux/include/assert.h create mode 100644 4/linux/include/const.h create mode 100644 4/linux/include/ctype.h create mode 100644 4/linux/include/errno.h create mode 100644 4/linux/include/fcntl.h create mode 100644 4/linux/include/linux/config.h create mode 100644 4/linux/include/linux/fdreg.h create mode 100644 4/linux/include/linux/fs.h create mode 100644 4/linux/include/linux/hdreg.h create mode 100644 4/linux/include/linux/head.h create mode 100644 4/linux/include/linux/kernel.h create mode 100644 4/linux/include/linux/mm.h create mode 100644 4/linux/include/linux/sched.h create mode 100644 4/linux/include/linux/sys.h create mode 100644 4/linux/include/linux/tty.h create mode 100644 4/linux/include/new.h create mode 100644 4/linux/include/signal.h create mode 100644 4/linux/include/stdarg.h create mode 100644 4/linux/include/stddef.h create mode 100644 4/linux/include/stdio.h create mode 100644 4/linux/include/stdlib.h create mode 100644 4/linux/include/string.h create mode 100644 4/linux/include/sys/stat.h create mode 100644 4/linux/include/sys/times.h create mode 100644 4/linux/include/sys/types.h create mode 100644 4/linux/include/sys/utsname.h create mode 100644 4/linux/include/sys/wait.h create mode 100644 4/linux/include/termios.h create mode 100644 4/linux/include/time.h create mode 100644 4/linux/include/unistd.h create mode 100644 4/linux/include/utime.h create mode 100644 4/linux/init/main.c create mode 100644 4/linux/kernel/Makefile create mode 100644 4/linux/kernel/asm.s create mode 100644 4/linux/kernel/blk_drv/Makefile create mode 100644 4/linux/kernel/blk_drv/blk.h create mode 100644 4/linux/kernel/blk_drv/floppy.c create mode 100644 4/linux/kernel/blk_drv/hd.c create mode 100644 4/linux/kernel/blk_drv/ll_rw_blk.c create mode 100644 4/linux/kernel/blk_drv/ramdisk.c create mode 100644 4/linux/kernel/chr_drv/Makefile create mode 100644 4/linux/kernel/chr_drv/console.c create mode 100644 4/linux/kernel/chr_drv/keyboard.2.s create mode 100644 4/linux/kernel/chr_drv/keyboard.S create mode 100644 4/linux/kernel/chr_drv/rs_io.s create mode 100644 4/linux/kernel/chr_drv/serial.c create mode 100644 4/linux/kernel/chr_drv/tty_io.c create mode 100644 4/linux/kernel/chr_drv/tty_ioctl.c create mode 100644 4/linux/kernel/exit.c create mode 100644 4/linux/kernel/fork.c create mode 100644 4/linux/kernel/math/Makefile create mode 100644 4/linux/kernel/math/math_emulate.c create mode 100644 4/linux/kernel/mktime.c create mode 100644 4/linux/kernel/panic.c create mode 100644 4/linux/kernel/printk.c create mode 100644 4/linux/kernel/sched.c create mode 100644 4/linux/kernel/signal.c create mode 100644 4/linux/kernel/sys.c create mode 100644 4/linux/kernel/system_call.s create mode 100644 4/linux/kernel/traps.c create mode 100644 4/linux/kernel/vsprintf.c create mode 100644 4/linux/lib/Makefile create mode 100644 4/linux/lib/_exit.c create mode 100644 4/linux/lib/close.c create mode 100644 4/linux/lib/ctype.c create mode 100644 4/linux/lib/dup.c create mode 100644 4/linux/lib/errno.c create mode 100644 4/linux/lib/execve.c create mode 100644 4/linux/lib/malloc.c create mode 100644 4/linux/lib/open.c create mode 100644 4/linux/lib/setsid.c create mode 100644 4/linux/lib/string.c create mode 100644 4/linux/lib/wait.c create mode 100644 4/linux/lib/write.c create mode 100644 4/linux/mm/Makefile create mode 100644 4/linux/mm/memory.c create mode 100644 4/linux/mm/memory.c.orig create mode 100644 4/linux/mm/page.s create mode 100644 4/linux/new.patch create mode 100644 4/linux/tools/build create mode 100644 4/linux/tools/build.c create mode 100644 4/linux/tools/system diff --git a/4/linux/Image b/4/linux/Image new file mode 100644 index 0000000000000000000000000000000000000000..ca9b74e5ba29f893dd41d9d2ff0ffec38a69e1c9 GIT binary patch literal 135716 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 I7L9)Y2hMl7?3)uwHvX&V)lDk`>=qNUbq7PJ>b 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 Cri=yv literal 0 HcmV?d00001 diff --git a/4/linux/Makefile b/4/linux/Makefile new file mode 100644 index 0000000..ecbcf30 --- /dev/null +++ b/4/linux/Makefile @@ -0,0 +1,125 @@ +# +# if you want the ram-disk device, define this to be the +# size in blocks. +# +RAMDISK = #-DRAMDISK=512 + +AS86 =as86 -0 -a +LD86 =ld86 -0 + +AS =as +LD =ld +LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32 -Map=System.map.2 -N +CC =gcc -march=i386 $(RAMDISK) +CFLAGS =-w -g -fstrength-reduce -fomit-frame-pointer -fno-stack-protector -mcld +CPP =cpp -nostdinc -Iinclude + +# +# ROOT_DEV specifies the default root-device when making the image. +# This can be either FLOPPY, /dev/xxxx or empty, in which case the +# default of /dev/hd6 is used by 'build'. +# +ROOT_DEV= + +ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o +DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a +MATH =kernel/math/math.a +LIBS =lib/lib.a + +.c.s: + $(CC) $(CFLAGS) \ + -nostdinc -Iinclude -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -nostdinc -Iinclude -c -o $*.o $< + +all: Image + +Image: boot/bootsect boot/setup tools/system tools/build + cp -f tools/system system.tmp + strip system.tmp + tools/build boot/bootsect boot/setup system.tmp $(ROOT_DEV) > Image + rm -f system.tmp + sync + +disk: Image + dd bs=8192 if=Image of=/dev/PS0 + +tools/build: tools/build.c + $(CC) $(CFLAGS) \ + -o tools/build tools/build.c + +boot/head.o: boot/head.s + +tools/system: boot/head.o init/main.o \ + $(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS) + $(LD) $(LDFLAGS) boot/head.o init/main.o \ + $(ARCHIVES) \ + $(DRIVERS) \ + $(MATH) \ + $(LIBS) \ + -o tools/system + nm tools/system | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw]\)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort >System.map + +kernel/math/math.a: FORCE + (cd kernel/math; make) + +kernel/blk_drv/blk_drv.a: FORCE + (cd kernel/blk_drv; make) + +kernel/chr_drv/chr_drv.a: FORCE + (cd kernel/chr_drv; make) + +kernel/kernel.o: FORCE + (cd kernel; make) + +mm/mm.o: FORCE + (cd mm; make) + +fs/fs.o: FORCE + (cd fs; make) + +lib/lib.a: FORCE + (cd lib; make) + +FORCE: + +boot/setup: boot/setup.s + $(AS86) -o boot/setup.o boot/setup.s + $(LD86) -s -o boot/setup boot/setup.o + +boot/bootsect: boot/bootsect.s + $(AS86) -o boot/bootsect.o boot/bootsect.s + $(LD86) -s -o boot/bootsect boot/bootsect.o + +tmp.s: boot/bootsect.s tools/system + (echo -n "SYSSIZE = (";ls -l tools/system | grep system \ + | cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s + cat boot/bootsect.s >> tmp.s + +clean: + rm -f Image System.map tmp_make core System.map.2 + rm -f boot/bootsect boot/setup + rm -f init/*.o tools/system tools/build boot/*.o + (cd mm;make clean) + (cd fs;make clean) + (cd kernel;make clean) + (cd lib;make clean) + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + (cd fs; make dep) + (cd kernel; make dep) + (cd mm; make dep) + +### Dependencies: +init/main.o : init/main.c include/unistd.h include/sys/stat.h \ + include/sys/types.h include/sys/times.h include/sys/utsname.h \ + include/utime.h include/time.h include/linux/tty.h include/termios.h \ + include/linux/sched.h include/linux/head.h include/linux/fs.h \ + include/linux/mm.h include/signal.h include/asm/system.h include/asm/io.h \ + include/stddef.h include/stdarg.h include/fcntl.h diff --git a/4/linux/System.map.2 b/4/linux/System.map.2 new file mode 100644 index 0000000..0e6282f --- /dev/null +++ b/4/linux/System.map.2 @@ -0,0 +1,1087 @@ +Archive member included to satisfy reference by file (symbol) + +kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + fs/fs.o (ll_rw_block) +kernel/blk_drv/blk_drv.a(floppy.o) + kernel/kernel.o (do_floppy) +kernel/blk_drv/blk_drv.a(hd.o) + kernel/kernel.o (do_hd) +kernel/blk_drv/blk_drv.a(ramdisk.o) + kernel/blk_drv/blk_drv.a(hd.o) (rd_load) +kernel/chr_drv/chr_drv.a(tty_io.o) + kernel/kernel.o (tty_table) +kernel/chr_drv/chr_drv.a(console.o) + kernel/chr_drv/chr_drv.a(tty_io.o) (con_write) +kernel/chr_drv/chr_drv.a(keyboard.2.o) + kernel/chr_drv/chr_drv.a(console.o) (keyboard_interrupt) +kernel/chr_drv/chr_drv.a(serial.o) + kernel/chr_drv/chr_drv.a(tty_io.o) (rs_init) +kernel/chr_drv/chr_drv.a(rs_io.o) + kernel/chr_drv/chr_drv.a(serial.o) (rs1_interrupt) +kernel/chr_drv/chr_drv.a(tty_ioctl.o) + fs/fs.o (tty_ioctl) +kernel/math/math.a(math_emulate.o) + kernel/kernel.o (math_emulate) +lib/lib.a(ctype.o) kernel/chr_drv/chr_drv.a(tty_io.o) (_ctmp) +lib/lib.a(_exit.o) init/main.o (_exit) +lib/lib.a(open.o) init/main.o (open) +lib/lib.a(close.o) init/main.o (close) +lib/lib.a(errno.o) init/main.o (errno) +lib/lib.a(write.o) init/main.o (write) +lib/lib.a(dup.o) init/main.o (dup) +lib/lib.a(setsid.o) init/main.o (setsid) +lib/lib.a(execve.o) init/main.o (execve) +lib/lib.a(wait.o) init/main.o (wait) +lib/lib.a(string.o) fs/fs.o (strcpy) + +分配公共符号 +公共符号 大小 文件 + +errno 0x4 lib/lib.a(errno.o) +hash_table 0x4cc fs/fs.o +___strtok 0x4 lib/lib.a(string.o) +rd_start 0x4 kernel/blk_drv/blk_drv.a(ramdisk.o) +request 0x480 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) +user_stack 0x1000 kernel/kernel.o +_ctmp 0x1 lib/lib.a(ctype.o) +super_block 0x360 fs/fs.o +drive_info 0x20 init/main.o +file_table 0x400 fs/fs.o + +Discarded input sections + + .note.GNU-stack + 0x0000000000000000 0x0 init/main.o + .note.GNU-stack + 0x0000000000000000 0x0 kernel/kernel.o + .note.GNU-stack + 0x0000000000000000 0x0 mm/mm.o + .note.GNU-stack + 0x0000000000000000 0x0 fs/fs.o + .note.GNU-stack + 0x0000000000000000 0x0 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/blk_drv/blk_drv.a(floppy.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/blk_drv/blk_drv.a(hd.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/blk_drv/blk_drv.a(ramdisk.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/chr_drv/chr_drv.a(tty_io.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/chr_drv/chr_drv.a(console.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/chr_drv/chr_drv.a(serial.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .note.GNU-stack + 0x0000000000000000 0x0 kernel/math/math.a(math_emulate.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(ctype.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(_exit.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(open.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(close.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(errno.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(write.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(dup.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(setsid.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(execve.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(wait.o) + .note.GNU-stack + 0x0000000000000000 0x0 lib/lib.a(string.o) + +内存配置 + +名称 来源 长度 属性 +*default* 0x0000000000000000 0xffffffffffffffff + +Linker script and memory map + +段 .text 的地址设置为 0x0 +LOAD boot/head.o +LOAD init/main.o +LOAD kernel/kernel.o +LOAD mm/mm.o +LOAD fs/fs.o +LOAD kernel/blk_drv/blk_drv.a +LOAD kernel/chr_drv/chr_drv.a +LOAD kernel/math/math.a +LOAD lib/lib.a + 0x0000000008048000 PROVIDE (__executable_start, 0x8048000) + 0x0000000008048074 . = (0x8048000 + SIZEOF_HEADERS) + +.interp + *(.interp) + +.note.gnu.build-id + *(.note.gnu.build-id) + +.hash + *(.hash) + +.gnu.hash + *(.gnu.hash) + +.dynsym + *(.dynsym) + +.dynstr + *(.dynstr) + +.gnu.version + *(.gnu.version) + +.gnu.version_d + *(.gnu.version_d) + +.gnu.version_r + *(.gnu.version_r) + +.rel.init + *(.rel.init) + +.rel.text 0x0000000008048074 0x0 + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + .rel.text 0x0000000000000000 0x0 boot/head.o + +.rel.fini + *(.rel.fini) + +.rel.rodata + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + +.rel.data.rel.ro + *(.rel.data.rel.ro .rel.data.rel.ro.* .rel.gnu.linkonce.d.rel.ro.*) + +.rel.data 0x0000000008048074 0x0 + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + .rel.data 0x0000000000000000 0x0 boot/head.o + +.rel.tdata + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + +.rel.tbss + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + +.rel.ctors + *(.rel.ctors) + +.rel.dtors + *(.rel.dtors) + +.rel.got + *(.rel.got) + +.rel.bss + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + +.rel.ifunc + *(.rel.ifunc) + +.rel.plt 0x0000000008048074 0x0 + *(.rel.plt) + 0x0000000008048074 PROVIDE (__rel_iplt_start, .) + *(.rel.iplt) + .rel.iplt 0x0000000000000000 0x0 boot/head.o + 0x0000000008048074 PROVIDE (__rel_iplt_end, .) + +.init + *(SORT(.init)) + +.plt 0x0000000008048080 0x0 + *(.plt) + *(.iplt) + .iplt 0x0000000000000000 0x0 boot/head.o + +.text 0x0000000000000000 0x18bcf + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + .text 0x0000000000000000 0x64c0 boot/head.o + 0x0000000000000000 startup_32 + 0x0000000000000000 pg_dir + 0x0000000000005000 tmp_floppy_area + 0x00000000000054c0 idt + 0x0000000000005cc0 gdt + .text 0x00000000000064c0 0x744 init/main.o + 0x00000000000064c0 fork + 0x00000000000064ef pause + 0x000000000000651e setup + 0x0000000000006555 sync + 0x0000000000006799 main + 0x000000000000695b init + 0x0000000000006bd3 print_nr + .text 0x0000000000006c04 0x399e kernel/kernel.o + 0x0000000000006c04 show_task + 0x0000000000006c89 show_stat + 0x0000000000006ccf math_state_restore + 0x0000000000006d2d schedule + 0x0000000000006edf sys_pause + 0x0000000000006ef5 sleep_on + 0x0000000000006f54 interruptible_sleep_on + 0x0000000000006fe7 wake_up + 0x000000000000700f ticks_to_floppy_on + 0x00000000000070da floppy_on + 0x0000000000007109 floppy_off + 0x0000000000007119 do_floppy_timer + 0x00000000000071ed add_timer + 0x0000000000007311 do_timer + 0x0000000000007400 sys_alarm + 0x000000000000747f sys_getpid + 0x000000000000748b sys_getppid + 0x0000000000007497 sys_getuid + 0x00000000000074a6 sys_geteuid + 0x00000000000074b6 sys_getgid + 0x00000000000074c6 sys_getegid + 0x00000000000074d5 sys_nice + 0x0000000000007500 sched_init + 0x00000000000076a4 system_call + 0x0000000000007728 coprocessor_error + 0x000000000000774a device_not_available + 0x0000000000007784 timer_interrupt + 0x00000000000077bc sys_execve + 0x00000000000077cc sys_execve2 + 0x00000000000077dc sys_fork + 0x00000000000077f4 hd_interrupt + 0x0000000000007830 floppy_interrupt + 0x0000000000007866 parallel_interrupt + 0x0000000000007a81 do_double_fault + 0x0000000000007aa4 do_general_protection + 0x0000000000007ac7 do_divide_error + 0x0000000000007aea do_int3 + 0x0000000000007bb2 do_nmi + 0x0000000000007bd5 do_debug + 0x0000000000007bf8 do_overflow + 0x0000000000007c1b do_bounds + 0x0000000000007c3e do_invalid_op + 0x0000000000007c61 do_device_not_available + 0x0000000000007c84 do_coprocessor_segment_overrun + 0x0000000000007ca7 do_invalid_TSS + 0x0000000000007cca do_segment_not_present + 0x0000000000007ced do_stack_segment + 0x0000000000007d10 do_coprocessor_error + 0x0000000000007d44 do_reserved + 0x0000000000007d67 trap_init + 0x000000000000804b divide_error + 0x0000000000008080 debug + 0x0000000000008087 nmi + 0x000000000000808e int3 + 0x0000000000008095 overflow + 0x000000000000809c bounds + 0x00000000000080a3 invalid_op + 0x00000000000080aa coprocessor_segment_overrun + 0x00000000000080b1 reserved + 0x00000000000080b8 irq13 + 0x00000000000080cd double_fault + 0x0000000000008104 invalid_TSS + 0x000000000000810b segment_not_present + 0x0000000000008112 stack_segment + 0x0000000000008119 general_protection + 0x0000000000008152 verify_area + 0x00000000000081b9 copy_mem + 0x0000000000008313 copy_process + 0x00000000000087c0 find_empty_process + 0x0000000000008854 panic + 0x000000000000888f printk + 0x0000000000008bac vsprintf + 0x0000000000009072 sys_ftime + 0x0000000000009078 sys_break + 0x000000000000907e sys_ptrace + 0x0000000000009084 sys_stty + 0x000000000000908a sys_gtty + 0x0000000000009090 sys_rename + 0x0000000000009096 sys_prof + 0x000000000000909c sys_setregid + 0x0000000000009155 sys_setgid + 0x00000000000091ec sys_acct + 0x00000000000091f2 sys_phys + 0x00000000000091f8 sys_lock + 0x00000000000091fe sys_mpx + 0x0000000000009204 sys_ulimit + 0x000000000000920a sys_time + 0x0000000000009266 sys_setreuid + 0x0000000000009348 sys_setuid + 0x00000000000093dd sys_stime + 0x0000000000009433 sys_times + 0x00000000000094d3 sys_brk + 0x0000000000009515 sys_setpgid + 0x00000000000095ee sys_getpgrp + 0x00000000000095fa sys_setsid + 0x0000000000009674 sys_getgroups + 0x000000000000967a sys_setgroups + 0x0000000000009680 sys_uname + 0x00000000000096e9 sys_sethostname + 0x00000000000096ef sys_getrlimit + 0x00000000000096f5 sys_setrlimit + 0x00000000000096fb sys_getrusage + 0x0000000000009701 sys_gettimeofday + 0x0000000000009707 sys_settimeofday + 0x000000000000970d sys_umask + 0x000000000000977e release + 0x00000000000098bb sys_kill + 0x0000000000009af5 do_exit + 0x0000000000009d29 sys_exit + 0x0000000000009d42 sys_waitpid + 0x0000000000009f9a sys_sgetmask + 0x0000000000009fa6 sys_ssetmask + 0x0000000000009fd2 sys_sigpending + 0x0000000000009fd8 sys_sigsuspend + 0x000000000000a071 sys_signal + 0x000000000000a0f5 sys_sleep + 0x000000000000a12e sys_pipe2 + 0x000000000000a14f sys_getcwd + 0x000000000000a155 sys_mmap + 0x000000000000a15b sys_munmap + 0x000000000000a161 sys_clone + 0x000000000000a167 sys_sigaction + 0x000000000000a271 do_signal + 0x000000000000a45d kernel_mktime + .text 0x000000000000a5a2 0xc63 mm/mm.o + 0x000000000000a5c1 get_free_page + 0x000000000000a5fd free_page + 0x000000000000a66c free_page_tables + 0x000000000000a76b copy_page_tables + 0x000000000000a8ec put_page + 0x000000000000a9d8 un_wp_page + 0x000000000000aa90 do_wp_page + 0x000000000000aac2 write_verify + 0x000000000000ab1b get_empty_page + 0x000000000000ada1 do_no_page + 0x000000000000af03 mem_init + 0x000000000000af77 calc_mem + 0x000000000000b06c do_no_page1 + 0x000000000000b1ce page_fault + .text 0x000000000000b205 0x81ff fs/fs.o + 0x000000000000b21b sys_ustat + 0x000000000000b221 sys_utime + 0x000000000000b2ce sys_access + 0x000000000000b3aa sys_chdir + 0x000000000000b41e sys_chroot + 0x000000000000b492 sys_chmod + 0x000000000000b52c sys_chown + 0x000000000000b5a7 sys_open + 0x000000000000b84a sys_creat + 0x000000000000b86d sys_close + 0x000000000000b93c sys_lseek + 0x000000000000ba53 sys_read + 0x000000000000bc46 sys_write + 0x000000000000be3f invalidate_inodes + 0x000000000000beae sync_inodes + 0x000000000000c2f1 bmap + 0x000000000000c314 create_block + 0x000000000000c337 iput + 0x000000000000c4a6 get_empty_inode + 0x000000000000c5fb get_pipe_inode + 0x000000000000c670 iget + 0x000000000000ca6b sys_sync + 0x000000000000cace sync_dev + 0x000000000000cbc7 invalidate_buffers + 0x000000000000cc39 check_disk_change + 0x000000000000cf07 get_hash_table + 0x000000000000cf89 getblk + 0x000000000000d127 brelse + 0x000000000000d170 bread + 0x000000000000d1f5 bread_page + 0x000000000000d317 breada + 0x000000000000d40c buffer_init + 0x000000000000d5ad get_super + 0x000000000000d61c put_super + 0x000000000000da32 sys_umount + 0x000000000000db93 sys_mount + 0x000000000000dd04 mount_root + 0x000000000000df38 block_write + 0x000000000000e08a block_read + 0x000000000000e3f5 rw_char + 0x000000000000e485 file_read + 0x000000000000e630 file_write + 0x000000000000e919 sys_stat + 0x000000000000e963 sys_lstat + 0x000000000000e97e sys_fstat + 0x000000000000e9db sys_readlink + 0x000000000000ea8a sys_uselib + 0x000000000000ef59 do_execve + 0x000000000000f9db do_execve2 + 0x00000000000104c7 memSet + 0x000000000001050a memCpy + 0x0000000000010565 sys_getdents + 0x00000000000106d3 read_pipe + 0x0000000000010829 write_pipe + 0x00000000000109a8 sys_pipe + 0x0000000000011387 namei + 0x000000000001149d open_namei + 0x00000000000117f0 sys_mknod + 0x0000000000011a20 sys_mkdir + 0x0000000000011faf sys_rmdir + 0x0000000000012307 sys_unlink + 0x00000000000125a8 sys_symlink + 0x00000000000125ae sys_link + 0x0000000000012808 free_block + 0x000000000001297a new_block + 0x0000000000012b1e free_inode + 0x0000000000012c85 new_inode + 0x0000000000012f39 sys_dup2 + 0x0000000000012f60 sys_dup + 0x0000000000012f7b sys_fcntl + 0x00000000000130ba sys_ioctl + 0x00000000000132c9 truncate + 0x00000000000133fe sys_select + .text 0x0000000000013404 0x46a kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + 0x00000000000137c7 ll_rw_block + 0x0000000000013820 blk_dev_init + .text 0x000000000001386e 0xb9d kernel/blk_drv/blk_drv.a(floppy.o) + 0x0000000000013954 floppy_deselect + 0x000000000001398b floppy_change + 0x0000000000013d48 setup_rw_floppy + 0x0000000000013fe1 unexpected_floppy_interrupt + 0x00000000000143b9 floppy_init + .text 0x000000000001440b 0xc8f kernel/blk_drv/blk_drv.a(hd.o) + 0x00000000000144df sys_setup + 0x0000000000014b8e unexpected_hd_interrupt + 0x0000000000015020 hd_init + .text 0x000000000001509a 0x50c kernel/blk_drv/blk_drv.a(ramdisk.o) + 0x00000000000152a4 rd_init + 0x00000000000152fa rd_load + .text 0x00000000000155a6 0xcbb kernel/chr_drv/chr_drv.a(tty_io.o) + 0x00000000000155cc tty_init + 0x00000000000155dd tty_intr + 0x00000000000156ea wait_for_keypress + 0x00000000000156fd copy_to_cooked + 0x0000000000015cb8 tty_read + 0x0000000000016026 tty_write + 0x0000000000016238 do_tty_interrupt + 0x0000000000016260 chr_dev_init + .text 0x0000000000016261 0x1283 kernel/chr_drv/chr_drv.a(console.o) + 0x00000000000167fe csi_m + 0x0000000000016b98 con_write + 0x0000000000017226 con_init + 0x000000000001745d sysbeepstop + .text 0x00000000000174e4 0x7e2 kernel/chr_drv/chr_drv.a(keyboard.2.o) + 0x00000000000174e7 keyboard_interrupt + .text 0x0000000000017cc6 0x145 kernel/chr_drv/chr_drv.a(serial.o) + 0x0000000000017d3b rs_init + 0x0000000000017dbe rs_write + *fill* 0x0000000000017e0b 0x1 + .text 0x0000000000017e0c 0xf7 kernel/chr_drv/chr_drv.a(rs_io.o) + 0x0000000000017e0c rs1_interrupt + 0x0000000000017e14 rs2_interrupt + .text 0x0000000000017f03 0x60f kernel/chr_drv/chr_drv.a(tty_ioctl.o) + 0x0000000000018214 tty_ioctl + .text 0x0000000000018512 0x111 kernel/math/math.a(math_emulate.o) + 0x0000000000018520 math_emulate + 0x0000000000018603 math_error + .text 0x0000000000018623 0x0 lib/lib.a(ctype.o) + .text 0x0000000000018623 0x10 lib/lib.a(_exit.o) + 0x0000000000018623 _exit + .text 0x0000000000018633 0x49 lib/lib.a(open.o) + 0x0000000000018633 open + .text 0x000000000001867c 0x37 lib/lib.a(close.o) + 0x000000000001867c close + .text 0x00000000000186b3 0x0 lib/lib.a(errno.o) + .text 0x00000000000186b3 0x3d lib/lib.a(write.o) + 0x00000000000186b3 write + .text 0x00000000000186f0 0x37 lib/lib.a(dup.o) + 0x00000000000186f0 dup + .text 0x0000000000018727 0x2f lib/lib.a(setsid.o) + 0x0000000000018727 setsid + .text 0x0000000000018756 0x3d lib/lib.a(execve.o) + 0x0000000000018756 execve + .text 0x0000000000018793 0x60 lib/lib.a(wait.o) + 0x0000000000018793 waitpid + 0x00000000000187d0 wait + .text 0x00000000000187f3 0x3dc lib/lib.a(string.o) + 0x00000000000187f3 strcpy + 0x000000000001880f strncpy + 0x0000000000018834 strcat + 0x000000000001885f strncat + 0x0000000000018894 strcmp + 0x00000000000188bb strncmp + 0x00000000000188e9 strchr + 0x0000000000018916 strrchr + 0x0000000000018945 strspn + 0x0000000000018982 strcspn + 0x00000000000189bf strpbrk + 0x00000000000189f6 strstr + 0x0000000000018a2f strlen + 0x0000000000018a52 strtok + 0x0000000000018ad5 memcpy + 0x0000000000018af5 memmove + 0x0000000000018b49 memcmp + 0x0000000000018b73 memchr + 0x0000000000018bab memset + *(.gnu.warning) + +.fini + *(SORT(.fini)) + 0x0000000000018bcf PROVIDE (__etext, .) + 0x0000000000018bcf PROVIDE (_etext, .) + 0x0000000000018bcf PROVIDE (etext, .) + +.rodata 0x0000000000018bd0 0x13da + *(.rodata .rodata.* .gnu.linkonce.r.*) + .rodata 0x0000000000018bd0 0xc9 init/main.o + *fill* 0x0000000000018c99 0x3 + .rodata 0x0000000000018c9c 0x438 kernel/kernel.o + .rodata 0x00000000000190d4 0x183 mm/mm.o + *fill* 0x0000000000019257 0x1 + .rodata 0x0000000000019258 0x62c fs/fs.o + .rodata 0x0000000000019884 0x7a kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + *fill* 0x00000000000198fe 0x2 + .rodata 0x0000000000019900 0x133 kernel/blk_drv/blk_drv.a(floppy.o) + *fill* 0x0000000000019a33 0x1 + .rodata 0x0000000000019a34 0x19b kernel/blk_drv/blk_drv.a(hd.o) + *fill* 0x0000000000019bcf 0x1 + .rodata 0x0000000000019bd0 0x188 kernel/blk_drv/blk_drv.a(ramdisk.o) + .rodata 0x0000000000019d58 0x17d kernel/chr_drv/chr_drv.a(console.o) + *fill* 0x0000000000019ed5 0x3 + .rodata 0x0000000000019ed8 0x80 kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .rodata 0x0000000000019f58 0x52 kernel/math/math.a(math_emulate.o) + +.rodata1 + *(.rodata1) + +.eh_frame_hdr + *(.eh_frame_hdr) + +.eh_frame 0x0000000000019fac 0x2ba4 + *(.eh_frame) + .eh_frame 0x0000000000019fac 0x108 init/main.o + .eh_frame 0x000000000001a0b4 0xb84 kernel/kernel.o + 0xc74 (size before relaxing) + .eh_frame 0x000000000001ac38 0x1b0 mm/mm.o + 0x1c8 (size before relaxing) + .eh_frame 0x000000000001ade8 0xe5c fs/fs.o + 0xff4 (size before relaxing) + .eh_frame 0x000000000001bc44 0x98 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + 0xb0 (size before relaxing) + .eh_frame 0x000000000001bcdc 0x23c kernel/blk_drv/blk_drv.a(floppy.o) + 0x254 (size before relaxing) + .eh_frame 0x000000000001bf18 0x1e8 kernel/blk_drv/blk_drv.a(hd.o) + 0x200 (size before relaxing) + .eh_frame 0x000000000001c100 0xac kernel/blk_drv/blk_drv.a(ramdisk.o) + 0xc4 (size before relaxing) + .eh_frame 0x000000000001c1ac 0x12c kernel/chr_drv/chr_drv.a(tty_io.o) + 0x144 (size before relaxing) + .eh_frame 0x000000000001c2d8 0x2c0 kernel/chr_drv/chr_drv.a(console.o) + 0x2d8 (size before relaxing) + .eh_frame 0x000000000001c598 0x54 kernel/chr_drv/chr_drv.a(serial.o) + 0x6c (size before relaxing) + .eh_frame 0x000000000001c5ec 0x148 kernel/chr_drv/chr_drv.a(tty_ioctl.o) + 0x160 (size before relaxing) + .eh_frame 0x000000000001c734 0x5c kernel/math/math.a(math_emulate.o) + 0x74 (size before relaxing) + .eh_frame 0x000000000001c790 0x1c lib/lib.a(_exit.o) + 0x34 (size before relaxing) + .eh_frame 0x000000000001c7ac 0x24 lib/lib.a(open.o) + 0x3c (size before relaxing) + .eh_frame 0x000000000001c7d0 0x20 lib/lib.a(close.o) + 0x38 (size before relaxing) + .eh_frame 0x000000000001c7f0 0x20 lib/lib.a(write.o) + 0x38 (size before relaxing) + .eh_frame 0x000000000001c810 0x20 lib/lib.a(dup.o) + 0x38 (size before relaxing) + .eh_frame 0x000000000001c830 0x18 lib/lib.a(setsid.o) + 0x30 (size before relaxing) + .eh_frame 0x000000000001c848 0x20 lib/lib.a(execve.o) + 0x38 (size before relaxing) + .eh_frame 0x000000000001c868 0x38 lib/lib.a(wait.o) + 0x50 (size before relaxing) + .eh_frame 0x000000000001c8a0 0x2b0 lib/lib.a(string.o) + 0x2c8 (size before relaxing) + +.gcc_except_table + *(.gcc_except_table .gcc_except_table.*) + +.exception_ranges + *(.exception_ranges .exception_ranges*) + 0x000000000001cb50 . = . + +.eh_frame + *(.eh_frame) + +.gcc_except_table + *(.gcc_except_table .gcc_except_table.*) + +.exception_ranges + *(.exception_ranges .exception_ranges*) + +.tdata + *(.tdata .tdata.* .gnu.linkonce.td.*) + +.tbss + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + +.preinit_array 0x000000000001cb50 0x0 + 0x000000000001cb50 PROVIDE (__preinit_array_start, .) + *(.preinit_array) + 0x000000000001cb50 PROVIDE (__preinit_array_end, .) + +.init_array 0x000000000001cb50 0x0 + 0x000000000001cb50 PROVIDE (__init_array_start, .) + *(SORT(.init_array.*) SORT(.ctors.*)) + *(.init_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .ctors) + 0x000000000001cb50 PROVIDE (__init_array_end, .) + +.fini_array 0x000000000001cb50 0x0 + 0x000000000001cb50 PROVIDE (__fini_array_start, .) + *(SORT(.fini_array.*) SORT(.dtors.*)) + *(.fini_array EXCLUDE_FILE(*crtend?.o *crtend.o *crtbegin?.o *crtbegin.o) .dtors) + 0x000000000001cb50 PROVIDE (__fini_array_end, .) + +.ctors + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + +.dtors + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + +.jcr + *(.jcr) + +.data.rel.ro + *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) + *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) + +.dynamic + *(.dynamic) + +.got + *(.got) + *(.igot) + +.got.plt 0x000000000001cb50 0x0 + *(.got.plt) + *(.igot.plt) + .igot.plt 0x0000000000000000 0x0 boot/head.o + +.data 0x000000000001cb60 0x3b21 + *(.data .data.* .gnu.linkonce.d.*) + .data 0x000000000001cb60 0x0 boot/head.o + .data 0x000000000001cb60 0x28 init/main.o + *fill* 0x000000000001cb88 0x18 + .data 0x000000000001cba0 0x1330 kernel/kernel.o + 0x000000000001cba0 sys_call_table + 0x000000000001cd1c NR_syscalls + 0x000000000001dd20 current + 0x000000000001dd40 task + 0x000000000001de40 stack_start + 0x000000000001de48 current_DOR + .data 0x000000000001ded0 0x0 mm/mm.o + *fill* 0x000000000001ded0 0x10 + .data 0x000000000001dee0 0x60 fs/fs.o + 0x000000000001dee4 start_buffer + .data 0x000000000001df40 0x0 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + .data 0x000000000001df40 0xcd kernel/blk_drv/blk_drv.a(floppy.o) + *fill* 0x000000000001e00d 0x3 + .data 0x000000000001e010 0x4 kernel/blk_drv/blk_drv.a(hd.o) + .data 0x000000000001e014 0x0 kernel/blk_drv/blk_drv.a(ramdisk.o) + *fill* 0x000000000001e014 0xc + .data 0x000000000001e020 0x2538 kernel/chr_drv/chr_drv.a(tty_io.o) + 0x000000000001e020 tty_table + 0x0000000000020540 table_list + .data 0x0000000000020558 0x1 kernel/chr_drv/chr_drv.a(console.o) + .data 0x0000000000020559 0x0 kernel/chr_drv/chr_drv.a(keyboard.2.o) + .data 0x0000000000020559 0x0 kernel/chr_drv/chr_drv.a(serial.o) + .data 0x0000000000020559 0x0 kernel/chr_drv/chr_drv.a(rs_io.o) + *fill* 0x0000000000020559 0x7 + .data 0x0000000000020560 0x20 kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .data 0x0000000000020580 0x0 kernel/math/math.a(math_emulate.o) + .data 0x0000000000020580 0x101 lib/lib.a(ctype.o) + 0x0000000000020580 _ctype + .data 0x0000000000020681 0x0 lib/lib.a(_exit.o) + .data 0x0000000000020681 0x0 lib/lib.a(open.o) + .data 0x0000000000020681 0x0 lib/lib.a(close.o) + .data 0x0000000000020681 0x0 lib/lib.a(errno.o) + .data 0x0000000000020681 0x0 lib/lib.a(write.o) + .data 0x0000000000020681 0x0 lib/lib.a(dup.o) + .data 0x0000000000020681 0x0 lib/lib.a(setsid.o) + .data 0x0000000000020681 0x0 lib/lib.a(execve.o) + .data 0x0000000000020681 0x0 lib/lib.a(wait.o) + .data 0x0000000000020681 0x0 lib/lib.a(string.o) + +.data1 + *(.data1) + 0x0000000000020681 _edata = . + 0x0000000000020681 PROVIDE (edata, .) + 0x0000000000020681 . = . + 0x0000000000020681 __bss_start = . + +.bss 0x00000000000206a0 0x44b0 + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + .bss 0x00000000000206a0 0x0 boot/head.o + .bss 0x00000000000206a0 0x40c init/main.o + *fill* 0x0000000000020aac 0x14 + .bss 0x0000000000020ac0 0x760 kernel/kernel.o + 0x0000000000020ac0 jiffies + 0x0000000000020ac4 startup_time + 0x0000000000020ac8 last_task_used_math + 0x0000000000020e04 last_pid + .bss 0x0000000000021220 0xf20 mm/mm.o + .bss 0x0000000000022140 0x710 fs/fs.o + 0x0000000000022140 inode_table + 0x0000000000022840 nr_buffers + 0x000000000002284c ROOT_DEV + *fill* 0x0000000000022850 0x10 + .bss 0x0000000000022860 0x58 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + 0x0000000000022860 wait_for_request + 0x0000000000022880 blk_dev + .bss 0x00000000000228b8 0x25 kernel/blk_drv/blk_drv.a(floppy.o) + 0x00000000000228b8 do_floppy + 0x00000000000228bc selected + 0x00000000000228c0 wait_on_floppy_select + *fill* 0x00000000000228dd 0x3 + .bss 0x00000000000228e0 0xb0 kernel/blk_drv/blk_drv.a(hd.o) + 0x00000000000228e0 do_hd + 0x0000000000022900 hd_info + .bss 0x0000000000022990 0x4 kernel/blk_drv/blk_drv.a(ramdisk.o) + 0x0000000000022990 rd_length + .bss 0x0000000000022994 0x4 kernel/chr_drv/chr_drv.a(tty_io.o) + *fill* 0x0000000000022998 0x8 + .bss 0x00000000000229a0 0xac kernel/chr_drv/chr_drv.a(console.o) + 0x00000000000229a0 beepcount + .bss 0x0000000000022a4c 0x0 kernel/chr_drv/chr_drv.a(keyboard.2.o) + .bss 0x0000000000022a4c 0x0 kernel/chr_drv/chr_drv.a(serial.o) + .bss 0x0000000000022a4c 0x0 kernel/chr_drv/chr_drv.a(rs_io.o) + .bss 0x0000000000022a4c 0x0 kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .bss 0x0000000000022a4c 0x0 kernel/math/math.a(math_emulate.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(ctype.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(_exit.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(open.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(close.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(errno.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(write.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(dup.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(setsid.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(execve.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(wait.o) + .bss 0x0000000000022a4c 0x0 lib/lib.a(string.o) + *(COMMON) + *fill* 0x0000000000022a4c 0x14 + COMMON 0x0000000000022a60 0x20 init/main.o + 0x0000000000022a60 drive_info + COMMON 0x0000000000022a80 0x1000 kernel/kernel.o + 0x0000000000022a80 user_stack + COMMON 0x0000000000023a80 0xc40 fs/fs.o + 0x0000000000023a80 hash_table + 0x0000000000023f60 super_block + 0x00000000000242c0 file_table + COMMON 0x00000000000246c0 0x480 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + 0x00000000000246c0 request + COMMON 0x0000000000024b40 0x4 kernel/blk_drv/blk_drv.a(ramdisk.o) + 0x0000000000024b40 rd_start + COMMON 0x0000000000024b44 0x1 lib/lib.a(ctype.o) + 0x0000000000024b44 _ctmp + *fill* 0x0000000000024b45 0x3 + COMMON 0x0000000000024b48 0x4 lib/lib.a(errno.o) + 0x0000000000024b48 errno + COMMON 0x0000000000024b4c 0x4 lib/lib.a(string.o) + 0x0000000000024b4c ___strtok + 0x0000000000024b50 . = ALIGN ((. != 0x0)?0x4:0x1) + 0x0000000000024b50 . = ALIGN (0x4) + 0x0000000000024b50 . = SEGMENT_START ("ldata-segment", .) + 0x0000000000024b50 . = ALIGN (0x4) + 0x0000000000024b50 _end = . + 0x0000000000024b50 PROVIDE (end, .) + +.stab + *(.stab) + +.stabstr + *(.stabstr) + +.stab.excl + *(.stab.excl) + +.stab.exclstr + *(.stab.exclstr) + +.stab.index + *(.stab.index) + +.stab.indexstr + *(.stab.indexstr) + +.comment 0x0000000000000000 0x29 + *(.comment) + .comment 0x0000000000000000 0x29 init/main.o + 0x2a (size before relaxing) + .comment 0x0000000000000000 0x1a4 kernel/kernel.o + .comment 0x0000000000000000 0x2a mm/mm.o + .comment 0x0000000000000000 0x2f4 fs/fs.o + .comment 0x0000000000000000 0x2a kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + .comment 0x0000000000000000 0x2a kernel/blk_drv/blk_drv.a(floppy.o) + .comment 0x0000000000000000 0x2a kernel/blk_drv/blk_drv.a(hd.o) + .comment 0x0000000000000000 0x2a kernel/blk_drv/blk_drv.a(ramdisk.o) + .comment 0x0000000000000000 0x2a kernel/chr_drv/chr_drv.a(tty_io.o) + .comment 0x0000000000000000 0x2a kernel/chr_drv/chr_drv.a(console.o) + .comment 0x0000000000000000 0x2a kernel/chr_drv/chr_drv.a(serial.o) + .comment 0x0000000000000000 0x2a kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .comment 0x0000000000000000 0x2a kernel/math/math.a(math_emulate.o) + .comment 0x0000000000000000 0x2a lib/lib.a(ctype.o) + .comment 0x0000000000000000 0x2a lib/lib.a(_exit.o) + .comment 0x0000000000000000 0x2a lib/lib.a(open.o) + .comment 0x0000000000000000 0x2a lib/lib.a(close.o) + .comment 0x0000000000000000 0x2a lib/lib.a(errno.o) + .comment 0x0000000000000000 0x2a lib/lib.a(write.o) + .comment 0x0000000000000000 0x2a lib/lib.a(dup.o) + .comment 0x0000000000000000 0x2a lib/lib.a(setsid.o) + .comment 0x0000000000000000 0x2a lib/lib.a(execve.o) + .comment 0x0000000000000000 0x2a lib/lib.a(wait.o) + .comment 0x0000000000000000 0x2a lib/lib.a(string.o) + +.debug + *(.debug) + +.line + *(.line) + +.debug_srcinfo + *(.debug_srcinfo) + +.debug_sfnames + *(.debug_sfnames) + +.debug_aranges 0x0000000000000000 0x628 + *(.debug_aranges) + .debug_aranges + 0x0000000000000000 0x20 init/main.o + .debug_aranges + 0x0000000000000020 0x140 kernel/kernel.o + .debug_aranges + 0x0000000000000160 0x20 mm/mm.o + .debug_aranges + 0x0000000000000180 0x238 fs/fs.o + .debug_aranges + 0x00000000000003b8 0x20 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + .debug_aranges + 0x00000000000003d8 0x20 kernel/blk_drv/blk_drv.a(floppy.o) + .debug_aranges + 0x00000000000003f8 0x20 kernel/blk_drv/blk_drv.a(hd.o) + .debug_aranges + 0x0000000000000418 0x20 kernel/blk_drv/blk_drv.a(ramdisk.o) + .debug_aranges + 0x0000000000000438 0x20 kernel/chr_drv/chr_drv.a(tty_io.o) + .debug_aranges + 0x0000000000000458 0x20 kernel/chr_drv/chr_drv.a(console.o) + .debug_aranges + 0x0000000000000478 0x20 kernel/chr_drv/chr_drv.a(serial.o) + .debug_aranges + 0x0000000000000498 0x20 kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .debug_aranges + 0x00000000000004b8 0x20 kernel/math/math.a(math_emulate.o) + .debug_aranges + 0x00000000000004d8 0x18 lib/lib.a(ctype.o) + .debug_aranges + 0x00000000000004f0 0x20 lib/lib.a(_exit.o) + .debug_aranges + 0x0000000000000510 0x20 lib/lib.a(open.o) + .debug_aranges + 0x0000000000000530 0x20 lib/lib.a(close.o) + .debug_aranges + 0x0000000000000550 0x18 lib/lib.a(errno.o) + .debug_aranges + 0x0000000000000568 0x20 lib/lib.a(write.o) + .debug_aranges + 0x0000000000000588 0x20 lib/lib.a(dup.o) + .debug_aranges + 0x00000000000005a8 0x20 lib/lib.a(setsid.o) + .debug_aranges + 0x00000000000005c8 0x20 lib/lib.a(execve.o) + .debug_aranges + 0x00000000000005e8 0x20 lib/lib.a(wait.o) + .debug_aranges + 0x0000000000000608 0x20 lib/lib.a(string.o) + +.debug_pubnames + *(.debug_pubnames) + +.debug_info 0x0000000000000000 0x17990 + *(.debug_info .gnu.linkonce.wi.*) + .debug_info 0x0000000000000000 0xb73 init/main.o + .debug_info 0x0000000000000b73 0x4e90 kernel/kernel.o + .debug_info 0x0000000000005a03 0xb99 mm/mm.o + .debug_info 0x000000000000659c 0xa695 fs/fs.o + .debug_info 0x0000000000010c31 0x932 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + .debug_info 0x0000000000011563 0xc9f kernel/blk_drv/blk_drv.a(floppy.o) + .debug_info 0x0000000000012202 0xd5f kernel/blk_drv/blk_drv.a(hd.o) + .debug_info 0x0000000000012f61 0xad5 kernel/blk_drv/blk_drv.a(ramdisk.o) + .debug_info 0x0000000000013a36 0xacd kernel/chr_drv/chr_drv.a(tty_io.o) + .debug_info 0x0000000000014503 0xdb0 kernel/chr_drv/chr_drv.a(console.o) + .debug_info 0x00000000000152b3 0x85c kernel/chr_drv/chr_drv.a(serial.o) + .debug_info 0x0000000000015b0f 0xb47 kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .debug_info 0x0000000000016656 0x7b5 kernel/math/math.a(math_emulate.o) + .debug_info 0x0000000000016e0b 0x66 lib/lib.a(ctype.o) + .debug_info 0x0000000000016e71 0x77 lib/lib.a(_exit.o) + .debug_info 0x0000000000016ee8 0xd0 lib/lib.a(open.o) + .debug_info 0x0000000000016fb8 0x97 lib/lib.a(close.o) + .debug_info 0x000000000001704f 0x36 lib/lib.a(errno.o) + .debug_info 0x0000000000017085 0xc9 lib/lib.a(write.o) + .debug_info 0x000000000001714e 0x97 lib/lib.a(dup.o) + .debug_info 0x00000000000171e5 0x95 lib/lib.a(setsid.o) + .debug_info 0x000000000001727a 0xcb lib/lib.a(execve.o) + .debug_info 0x0000000000017345 0xed lib/lib.a(wait.o) + .debug_info 0x0000000000017432 0x55e lib/lib.a(string.o) + +.debug_abbrev 0x0000000000000000 0x44d6 + *(.debug_abbrev) + .debug_abbrev 0x0000000000000000 0x237 init/main.o + .debug_abbrev 0x0000000000000237 0xf9e kernel/kernel.o + .debug_abbrev 0x00000000000011d5 0x20b mm/mm.o + .debug_abbrev 0x00000000000013e0 0x1a6f fs/fs.o + .debug_abbrev 0x0000000000002e4f 0x169 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + .debug_abbrev 0x0000000000002fb8 0x27d kernel/blk_drv/blk_drv.a(floppy.o) + .debug_abbrev 0x0000000000003235 0x23c kernel/blk_drv/blk_drv.a(hd.o) + .debug_abbrev 0x0000000000003471 0x1a8 kernel/blk_drv/blk_drv.a(ramdisk.o) + .debug_abbrev 0x0000000000003619 0x279 kernel/chr_drv/chr_drv.a(tty_io.o) + .debug_abbrev 0x0000000000003892 0x26b kernel/chr_drv/chr_drv.a(console.o) + .debug_abbrev 0x0000000000003afd 0x175 kernel/chr_drv/chr_drv.a(serial.o) + .debug_abbrev 0x0000000000003c72 0x18d kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .debug_abbrev 0x0000000000003dff 0x163 kernel/math/math.a(math_emulate.o) + .debug_abbrev 0x0000000000003f62 0x3e lib/lib.a(ctype.o) + .debug_abbrev 0x0000000000003fa0 0x51 lib/lib.a(_exit.o) + .debug_abbrev 0x0000000000003ff1 0x97 lib/lib.a(open.o) + .debug_abbrev 0x0000000000004088 0x75 lib/lib.a(close.o) + .debug_abbrev 0x00000000000040fd 0x2c lib/lib.a(errno.o) + .debug_abbrev 0x0000000000004129 0xa1 lib/lib.a(write.o) + .debug_abbrev 0x00000000000041ca 0x75 lib/lib.a(dup.o) + .debug_abbrev 0x000000000000423f 0x73 lib/lib.a(setsid.o) + .debug_abbrev 0x00000000000042b2 0x85 lib/lib.a(execve.o) + .debug_abbrev 0x0000000000004337 0xb6 lib/lib.a(wait.o) + .debug_abbrev 0x00000000000043ed 0xe9 lib/lib.a(string.o) + +.debug_line 0x0000000000000000 0x4831 + *(.debug_line .debug_line.* .debug_line_end) + .debug_line 0x0000000000000000 0x1e5 init/main.o + .debug_line 0x00000000000001e5 0xe9e kernel/kernel.o + .debug_line 0x0000000000001083 0x2b2 mm/mm.o + .debug_line 0x0000000000001335 0x1e54 fs/fs.o + .debug_line 0x0000000000003189 0x197 kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + .debug_line 0x0000000000003320 0x24c kernel/blk_drv/blk_drv.a(floppy.o) + .debug_line 0x000000000000356c 0x2bf kernel/blk_drv/blk_drv.a(hd.o) + .debug_line 0x000000000000382b 0x160 kernel/blk_drv/blk_drv.a(ramdisk.o) + .debug_line 0x000000000000398b 0x33b kernel/chr_drv/chr_drv.a(tty_io.o) + .debug_line 0x0000000000003cc6 0x3ab kernel/chr_drv/chr_drv.a(console.o) + .debug_line 0x0000000000004071 0xd9 kernel/chr_drv/chr_drv.a(serial.o) + .debug_line 0x000000000000414a 0x1db kernel/chr_drv/chr_drv.a(tty_ioctl.o) + .debug_line 0x0000000000004325 0xe1 kernel/math/math.a(math_emulate.o) + .debug_line 0x0000000000004406 0x28 lib/lib.a(ctype.o) + .debug_line 0x000000000000442e 0x39 lib/lib.a(_exit.o) + .debug_line 0x0000000000004467 0x62 lib/lib.a(open.o) + .debug_line 0x00000000000044c9 0x5c lib/lib.a(close.o) + .debug_line 0x0000000000004525 0x28 lib/lib.a(errno.o) + .debug_line 0x000000000000454d 0x76 lib/lib.a(write.o) + .debug_line 0x00000000000045c3 0x5a lib/lib.a(dup.o) + .debug_line 0x000000000000461d 0x77 lib/lib.a(setsid.o) + .debug_line 0x0000000000004694 0x5d lib/lib.a(execve.o) + .debug_line 0x00000000000046f1 0x7a lib/lib.a(wait.o) + .debug_line 0x000000000000476b 0xc6 lib/lib.a(string.o) + +.debug_frame + *(.debug_frame) + +.debug_str 0x0000000000000000 0x2266 + *(.debug_str) + .debug_str 0x0000000000000000 0x3bd init/main.o + 0x460 (size before relaxing) + .debug_str 0x00000000000003bd 0x97e kernel/kernel.o + 0x239e (size before relaxing) + .debug_str 0x0000000000000d3b 0x18d mm/mm.o + 0x4e8 (size before relaxing) + .debug_str 0x0000000000000ec8 0x9c0 fs/fs.o + 0x49fd (size before relaxing) + .debug_str 0x0000000000001888 0xec kernel/blk_drv/blk_drv.a(ll_rw_blk.o) + 0x48d (size before relaxing) + .debug_str 0x0000000000001974 0x1cf kernel/blk_drv/blk_drv.a(floppy.o) + 0x66f (size before relaxing) + .debug_str 0x0000000000001b43 0x15e kernel/blk_drv/blk_drv.a(hd.o) + 0x5e4 (size before relaxing) + .debug_str 0x0000000000001ca1 0x7b kernel/blk_drv/blk_drv.a(ramdisk.o) + 0x58a (size before relaxing) + .debug_str 0x0000000000001d1c 0xf7 kernel/chr_drv/chr_drv.a(tty_io.o) + 0x4df (size before relaxing) + .debug_str 0x0000000000001e13 0x1b9 kernel/chr_drv/chr_drv.a(console.o) + 0x5b3 (size before relaxing) + .debug_str 0x0000000000001fcc 0x1a kernel/chr_drv/chr_drv.a(serial.o) + 0x3f8 (size before relaxing) + .debug_str 0x0000000000001fe6 0x8b kernel/chr_drv/chr_drv.a(tty_ioctl.o) + 0x4a3 (size before relaxing) + .debug_str 0x0000000000002071 0x70 kernel/math/math.a(math_emulate.o) + 0x392 (size before relaxing) + .debug_str 0x00000000000020e1 0x8d lib/lib.a(ctype.o) + 0xb6 (size before relaxing) + .debug_str 0x000000000000216e 0x31 lib/lib.a(_exit.o) + 0xde (size before relaxing) + .debug_str 0x0000000000000000 0x31 lib/lib.a(open.o) + 0xee (size before relaxing) + .debug_str 0x000000000000219f 0x8 lib/lib.a(close.o) + 0xe0 (size before relaxing) + .debug_str 0x00000000000021a7 0x8 lib/lib.a(errno.o) + 0x93 (size before relaxing) + .debug_str 0x0000000000000000 0x8 lib/lib.a(write.o) + 0xec (size before relaxing) + .debug_str 0x00000000000021af 0x6 lib/lib.a(dup.o) + 0xd8 (size before relaxing) + .debug_str 0x00000000000021b5 0x9 lib/lib.a(setsid.o) + 0xe8 (size before relaxing) + .debug_str 0x00000000000021be 0x9 lib/lib.a(execve.o) + 0xf1 (size before relaxing) + .debug_str 0x00000000000021c7 0x11 lib/lib.a(wait.o) + 0xfe (size before relaxing) + .debug_str 0x00000000000021d8 0x8e lib/lib.a(string.o) + 0x147 (size before relaxing) + +.debug_loc + *(.debug_loc) + +.debug_macinfo + *(.debug_macinfo) + +.debug_weaknames + *(.debug_weaknames) + +.debug_funcnames + *(.debug_funcnames) + +.debug_typenames + *(.debug_typenames) + +.debug_varnames + *(.debug_varnames) + +.debug_pubtypes + *(.debug_pubtypes) + +.debug_ranges 0x0000000000000000 0x18 + *(.debug_ranges) + .debug_ranges 0x0000000000000000 0x18 init/main.o + +.debug_macro + *(.debug_macro) + +.gnu.attributes + *(.gnu.attributes) + +/DISCARD/ + *(.note.GNU-stack) + *(.gnu_debuglink) + *(.gnu.lto_*) +OUTPUT(tools/system elf32-i386) diff --git a/4/linux/boot/bootsect b/4/linux/boot/bootsect new file mode 100644 index 0000000000000000000000000000000000000000..4fbb8f823afe23687f1887d48deeb19fdcf83049 GIT binary patch literal 544 zcmZQ%7GP0e00JfufdrZ$>>UT#`)=%Dn9z4%Cj+DAH_iW_m%fre~uHUSwNXN9*i=ymehGJ;Ln!ff#WtiVnQ zhTZH8d+ix_Fbba)*ufysci@E)!;7N~dJF$B+A=VditBaD0d;UNHUH)>;o2bsq=ibD zUjNfu{D<)s1B1c@RhzFW-(_8 zyWs)N8wX!-zAym_G6SXbtl4ZCONDkYGHA2SVkl+#&o+l)@hk?z1B+)cfHa*h-qdq| z733tt+pm`g1P2Cni_Kx^=9|Ib*m1R6Y!*YuwM`6-j~E!Y0DW7;8WkBGaTw@dps&vg zgE+6x1nv67yn*3e)L{k&UM`>f#FWgubcN!|;*!){1wB1IUM^lPq<|j0Og84wRRH+h BgWdoD literal 0 HcmV?d00001 diff --git a/4/linux/boot/bootsect.s b/4/linux/boot/bootsect.s new file mode 100644 index 0000000..711f103 --- /dev/null +++ b/4/linux/boot/bootsect.s @@ -0,0 +1,260 @@ +! +! SYS_SIZE is the number of clicks (16 bytes) to be loaded. +! 0x3000 is 0x30000 bytes = 196kB, more than enough for current +! versions of linux +! +SYSSIZE = 0x3000 +! +! bootsect.s (C) 1991 Linus Torvalds +! +! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves +! iself out of the way to address 0x90000, and jumps there. +! +! It then loads 'setup' directly after itself (0x90200), and the system +! at 0x10000, using BIOS interrupts. +! +! NOTE! currently system is at most 8*65536 bytes long. This should be no +! problem, even in the future. I want to keep it simple. This 512 kB +! kernel size should be enough, especially as this doesn't contain the +! buffer cache as in minix +! +! The loader has been made as simple as possible, and continuos +! read errors will result in a unbreakable loop. Reboot by hand. It +! loads pretty fast by getting whole sectors at a time whenever possible. + +.globl begtext, begdata, begbss, endtext, enddata, endbss +.text +begtext: +.data +begdata: +.bss +begbss: +.text + +SETUPLEN = 4 ! nr of setup-sectors +BOOTSEG = 0x07c0 ! original address of boot-sector +INITSEG = 0x9000 ! we move boot here - out of the way +SETUPSEG = 0x9020 ! setup starts here +SYSSEG = 0x1000 ! system loaded at 0x10000 (65536). +ENDSEG = SYSSEG + SYSSIZE ! where to stop loading + +! ROOT_DEV: 0x000 - same type of floppy as boot. +! 0x301 - first partition on first drive etc +ROOT_DEV = 0x306 + +entry start +start: + mov ax,#BOOTSEG + mov ds,ax + mov ax,#INITSEG + mov es,ax + mov cx,#256 + sub si,si + sub di,di + rep + movw + jmpi go,INITSEG +go: mov ax,cs + mov ds,ax + mov es,ax +! put stack at 0x9ff00. + mov ss,ax + mov sp,#0xFF00 ! arbitrary value >>512 + +! load the setup-sectors directly after the bootblock. +! Note that 'es' is already set up. + +load_setup: + mov dx,#0x0000 ! drive 0, head 0 + mov cx,#0x0002 ! sector 2, track 0 + mov bx,#0x0200 ! address = 512, in INITSEG + mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors + int 0x13 ! read it + jnc ok_load_setup ! ok - continue + mov dx,#0x0000 + mov ax,#0x0000 ! reset the diskette + int 0x13 + j load_setup + +ok_load_setup: + +! Get disk drive parameters, specifically nr of sectors/track + + mov dl,#0x00 + mov ax,#0x0800 ! AH=8 is get drive parameters + int 0x13 + mov ch,#0x00 + seg cs + mov sectors,cx + mov ax,#INITSEG + mov es,ax + +! Print some inane message + + mov ah,#0x03 ! read cursor pos + xor bh,bh + int 0x10 + + mov cx,#24 + mov bx,#0x0007 ! page 0, attribute 7 (normal) + mov bp,#msg1 + mov ax,#0x1301 ! write string, move cursor + int 0x10 + +! ok, we've written the message, now +! we want to load the system (at 0x10000) + + mov ax,#SYSSEG + mov es,ax ! segment of 0x010000 + call read_it + call kill_motor + +! After that we check which root-device to use. If the device is +! defined (!= 0), nothing is done and the given device is used. +! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending +! on the number of sectors that the BIOS reports currently. + + seg cs + mov ax,root_dev + cmp ax,#0 + jne root_defined + seg cs + mov bx,sectors + mov ax,#0x0208 ! /dev/ps0 - 1.2Mb + cmp bx,#15 + je root_defined + mov ax,#0x021c ! /dev/PS0 - 1.44Mb + cmp bx,#18 + je root_defined +undef_root: + jmp undef_root +root_defined: + seg cs + mov root_dev,ax + +! after that (everyting loaded), we jump to +! the setup-routine loaded directly after +! the bootblock: + + jmpi 0,SETUPSEG + +! This routine loads the system at address 0x10000, making sure +! no 64kB boundaries are crossed. We try to load it as fast as +! possible, loading whole tracks whenever we can. +! +! in: es - starting address segment (normally 0x1000) +! +sread: .word 1+SETUPLEN ! sectors read of current track +head: .word 0 ! current head +track: .word 0 ! current track + +read_it: + mov ax,es + test ax,#0x0fff +die: jne die ! es must be at 64kB boundary + xor bx,bx ! bx is starting address within segment +rp_read: + mov ax,es + cmp ax,#ENDSEG ! have we loaded all yet? + jb ok1_read + ret +ok1_read: + seg cs + mov ax,sectors + sub ax,sread + mov cx,ax + shl cx,#9 + add cx,bx + jnc ok2_read + je ok2_read + xor ax,ax + sub ax,bx + shr ax,#9 +ok2_read: + call read_track + mov cx,ax + add ax,sread + seg cs + cmp ax,sectors + jne ok3_read + mov ax,#1 + sub ax,head + jne ok4_read + inc track +ok4_read: + mov head,ax + xor ax,ax +ok3_read: + mov sread,ax + shl cx,#9 + add bx,cx + jnc rp_read + mov ax,es + add ax,#0x1000 + mov es,ax + xor bx,bx + jmp rp_read + +read_track: + push ax + push bx + push cx + push dx + mov dx,track + mov cx,sread + inc cx + mov ch,dl + mov dx,head + mov dh,dl + mov dl,#0 + and dx,#0x0100 + mov ah,#2 + int 0x13 + jc bad_rt + pop dx + pop cx + pop bx + pop ax + ret +bad_rt: mov ax,#0 + mov dx,#0 + int 0x13 + pop dx + pop cx + pop bx + pop ax + jmp read_track + +/* + * This procedure turns off the floppy drive motor, so + * that we enter the kernel in a known state, and + * don't have to worry about it later. + */ +kill_motor: + push dx + mov dx,#0x3f2 + mov al,#0 + outb + pop dx + ret + +sectors: + .word 0 + +msg1: + .byte 13,10 + .ascii "Loading system ..." + .byte 13,10,13,10 + +.org 508 +root_dev: + .word ROOT_DEV +boot_flag: + .word 0xAA55 + +.text +endtext: +.data +enddata: +.bss +endbss: diff --git a/4/linux/boot/head.s b/4/linux/boot/head.s new file mode 100644 index 0000000..651e95c --- /dev/null +++ b/4/linux/boot/head.s @@ -0,0 +1,240 @@ +/* + * linux/boot/head.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * head.s contains the 32-bit startup code. + * + * NOTE!!! Startup happens at absolute address 0x00000000, which is also where + * the page directory will exist. The startup code will be overwritten by + * the page directory. + */ +.text +.globl idt,gdt,pg_dir,tmp_floppy_area +pg_dir: +.globl startup_32 +startup_32: + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + mov %ax,%gs + lss stack_start,%esp + call setup_idt + call setup_gdt + movl $0x10,%eax # reload all the segment registers + mov %ax,%ds # after changing gdt. CS was already + mov %ax,%es # reloaded in 'setup_gdt' + mov %ax,%fs + mov %ax,%gs + lss stack_start,%esp + xorl %eax,%eax +1: incl %eax # check that A20 really IS enabled + movl %eax,0x000000 # loop forever if it isn't + cmpl %eax,0x100000 + je 1b +/* + * NOTE! 486 should set bit 16, to check for write-protect in supervisor + * mode. Then it would be unnecessary with the "verify_area()"-calls. + * 486 users probably want to set the NE (#5) bit also, so as to use + * int 16 for math errors. + */ + movl %cr0,%eax # check math chip + andl $0x80000011,%eax # Save PG,PE,ET +/* "orl $0x10020,%eax" here for 486 might be good */ + orl $2,%eax # set MP + movl %eax,%cr0 + call check_x87 + jmp after_page_tables + +/* + * We depend on ET to be correct. This checks for 287/387. + */ +check_x87: + fninit + fstsw %ax + cmpb $0,%al + je 1f /* no coprocessor: have to set bits */ + movl %cr0,%eax + xorl $6,%eax /* reset MP, set EM */ + movl %eax,%cr0 + ret +.align 4 +1: .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ + ret + +/* + * setup_idt + * + * sets up a idt with 256 entries pointing to + * ignore_int, interrupt gates. It then loads + * idt. Everything that wants to install itself + * in the idt-table may do so themselves. Interrupts + * are enabled elsewhere, when we can be relatively + * sure everything is ok. This routine will be over- + * written by the page tables. + */ +setup_idt: + lea ignore_int,%edx + movl $0x00080000,%eax + movw %dx,%ax /* selector = 0x0008 = cs */ + movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ + + lea idt,%edi + mov $256,%ecx +rp_sidt: + movl %eax,(%edi) + movl %edx,4(%edi) + addl $8,%edi + dec %ecx + jne rp_sidt + lidt idt_descr + ret + +/* + * setup_gdt + * + * This routines sets up a new gdt and loads it. + * Only two entries are currently built, the same + * ones that were built in init.s. The routine + * is VERY complicated at two whole lines, so this + * rather long comment is certainly needed :-). + * This routine will beoverwritten by the page tables. + */ +setup_gdt: + lgdt gdt_descr + ret + +/* + * I put the kernel page tables right after the page directory, + * using 4 of them to span 16 Mb of physical memory. People with + * more than 16MB will have to expand this. + */ +.org 0x1000 +pg0: + +.org 0x2000 +pg1: + +.org 0x3000 +pg2: + +.org 0x4000 +pg3: + +.org 0x5000 +/* + * tmp_floppy_area is used by the floppy-driver when DMA cannot + * reach to a buffer-block. It needs to be aligned, so that it isn't + * on a 64kB border. + */ +tmp_floppy_area: + .fill 1024,1,0 + +after_page_tables: + pushl $0 # These are the parameters to main :-) + pushl $0 + pushl $0 + pushl $L6 # return address for main, if it decides to. + pushl $main + jmp setup_paging +L6: + jmp L6 # main should never return here, but + # just in case, we know what happens. + +/* This is the default interrupt "handler" :-) */ +int_msg: + .asciz "Unknown interrupt\n\r" +.align 4 +ignore_int: + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + pushl $int_msg + call printk + popl %eax + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret + + +/* + * Setup_paging + * + * This routine sets up paging by setting the page bit + * in cr0. The page tables are set up, identity-mapping + * the first 16MB. The pager assumes that no illegal + * addresses are produced (ie >4Mb on a 4Mb machine). + * + * NOTE! Although all physical memory should be identity + * mapped by this routine, only the kernel page functions + * use the >1Mb addresses directly. All "normal" functions + * use just the lower 1Mb, or the local data space, which + * will be mapped to some other place - mm keeps track of + * that. + * + * For those with more memory than 16 Mb - tough luck. I've + * not got it, why should you :-) The source is here. Change + * it. (Seriously - it shouldn't be too difficult. Mostly + * change some constants etc. I left it at 16Mb, as my machine + * even cannot be extended past that (ok, but it was cheap :-) + * I've tried to show which constants to change by having + * some kind of marker at them (search for "16Mb"), but I + * won't guarantee that's all :-( ) + */ +.align 4 +setup_paging: + movl $1024*5,%ecx /* 5 pages - pg_dir+4 page tables */ + xorl %eax,%eax + xorl %edi,%edi /* pg_dir is at 0x000 */ + cld;rep;stosl + movl $pg0+7,pg_dir /* set present bit/user r/w */ + movl $pg1+7,pg_dir+4 /* --------- " " --------- */ + movl $pg2+7,pg_dir+8 /* --------- " " --------- */ + movl $pg3+7,pg_dir+12 /* --------- " " --------- */ + movl $pg3+4092,%edi + movl $0xfff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */ + std +1: stosl /* fill pages backwards - more efficient :-) */ + subl $0x1000,%eax + jge 1b + xorl %eax,%eax /* pg_dir is at 0x0000 */ + movl %eax,%cr3 /* cr3 - page directory start */ + movl %cr0,%eax + orl $0x80000000,%eax + movl %eax,%cr0 /* set paging (PG) bit */ + cld /* by wyj */ + ret /* this also flushes prefetch-queue */ + +.align 4 +.word 0 +idt_descr: + .word 256*8-1 # idt contains 256 entries + .long idt +.align 4 +.word 0 +gdt_descr: + .word 256*8-1 # so does gdt (not that that's any + .long gdt # magic number, but it works for me :^) + + .align 8 +idt: .fill 256,8,0 # idt is uninitialized + +gdt: .quad 0x0000000000000000 /* NULL descriptor */ + .quad 0x00c09a0000000fff /* 16Mb */ + .quad 0x00c0920000000fff /* 16Mb */ + .quad 0x0000000000000000 /* TEMPORARY - don't use */ + .fill 252,8,0 /* space for LDT's and TSS's etc */ diff --git a/4/linux/boot/setup b/4/linux/boot/setup new file mode 100644 index 0000000000000000000000000000000000000000..0e70f4846819f560fb2e22a8eb1e7186c7956ab0 GIT binary patch literal 344 zcmZQ%7GP0eU|_Id1QJNVq7lN`!7!ok#ujFS|7Qg{#TXd2bet7k%*3#TAIOzsVOY$@ zutjLIz*&LC91NXuTnwFjJPbP+82WA;HDh4}X+5yNfnlcr!{;SXQ3*l_<@!izwL4T8@UUNgLAc(wpUZcuop2&OfjEd 86) ++ printk(" --syscall: sid=%d, pid=%d\n", sid, current->pid); ++} +diff -Naur 0/linux/kernel/system_call.s 4/linux/kernel/system_call.s +--- 0/linux/kernel/system_call.s 2015-09-03 20:21:09.000000000 +0800 ++++ 4/linux/kernel/system_call.s 2021-07-06 14:18:56.000000000 +0800 +@@ -91,6 +91,11 @@ + mov %dx,%es + movl $0x17,%edx # fs points to local data space + mov %dx,%fs ++ ++ pushl %eax #by wyj ++ call print_nr ++ popl %eax ++ + call sys_call_table(,%eax,4) + pushl %eax + movl current,%eax +diff -Naur 0/linux/mm/memory.c 4/linux/mm/memory.c +--- 0/linux/mm/memory.c 2015-09-04 15:24:20.000000000 +0800 ++++ 4/linux/mm/memory.c 2021-07-06 14:21:45.000000000 +0800 +@@ -370,6 +370,9 @@ + unsigned long page; + int block,i; + ++ if (current->pid > 5) ++ printk(" --do_no_page: address=%x, pid=%d\n", address, current->pid); ++ + address &= 0xfffff000; + tmp = address - current->start_code; + if (!current->executable || tmp >= current->end_data) { diff --git a/4/linux/fs/Makefile b/4/linux/fs/Makefile new file mode 100644 index 0000000..970acd4 --- /dev/null +++ b/4/linux/fs/Makefile @@ -0,0 +1,101 @@ +AR =ar +AS =as +LD =ld +LDFLAGS =-s -x +CC =gcc -march=i386 +CFLAGS =-w -g -fstrength-reduce -fomit-frame-pointer -mcld \ + -finline-functions -nostdinc -fno-stack-protector -I../include +CPP =gcc -E -nostdinc -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< + +OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \ + block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \ + bitmap.o fcntl.o ioctl.o truncate.o select.o + +fs.o: $(OBJS) + $(LD) -r -o fs.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h +block_dev.o : block_dev.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ + ../include/asm/segment.h ../include/asm/system.h +buffer.o : buffer.c ../include/stdarg.h ../include/linux/config.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/mm.h ../include/signal.h \ + ../include/linux/kernel.h ../include/asm/system.h ../include/asm/io.h +char_dev.o : char_dev.c ../include/errno.h ../include/sys/types.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ + ../include/asm/segment.h ../include/asm/io.h +exec.o : exec.c ../include/errno.h ../include/string.h \ + ../include/sys/stat.h ../include/sys/types.h ../include/a.out.h \ + ../include/linux/fs.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ + ../include/asm/segment.h +fcntl.o : fcntl.c ../include/string.h ../include/errno.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/mm.h ../include/signal.h \ + ../include/linux/kernel.h ../include/asm/segment.h ../include/fcntl.h \ + ../include/sys/stat.h +file_dev.o : file_dev.c ../include/errno.h ../include/fcntl.h \ + ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \ + ../include/linux/kernel.h ../include/asm/segment.h +file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h +inode.o : inode.c ../include/string.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \ + ../include/linux/kernel.h ../include/asm/system.h +ioctl.o : ioctl.c ../include/string.h ../include/errno.h \ + ../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/signal.h +namei.o : namei.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h \ + ../include/string.h ../include/fcntl.h ../include/errno.h \ + ../include/const.h ../include/sys/stat.h +open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.h \ + ../include/sys/types.h ../include/utime.h ../include/sys/stat.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \ + ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h +pipe.o : pipe.c ../include/signal.h ../include/sys/types.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/linux/mm.h ../include/asm/segment.h +read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.h \ + ../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/signal.h ../include/asm/segment.h +stat.o : stat.c ../include/errno.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/mm.h ../include/signal.h \ + ../include/linux/kernel.h ../include/asm/segment.h +super.o : super.c ../include/linux/config.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ + ../include/asm/system.h ../include/errno.h ../include/sys/stat.h +truncate.o : truncate.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/signal.h ../include/sys/stat.h diff --git a/4/linux/fs/bitmap.c b/4/linux/fs/bitmap.c new file mode 100644 index 0000000..73951a8 --- /dev/null +++ b/4/linux/fs/bitmap.c @@ -0,0 +1,168 @@ +/* + * linux/fs/bitmap.c + * + * (C) 1991 Linus Torvalds + */ + +/* bitmap.c contains the code that handles the inode and block bitmaps */ +#include + +#include +#include + +#define clear_block(addr) \ +__asm__ __volatile__ ("cld\n\t" \ + "rep\n\t" \ + "stosl" \ + ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr))) + +#define set_bit(nr,addr) ({\ +register int res __asm__("ax"); \ +__asm__ __volatile__("btsl %2,%3\n\tsetb %%al": \ +"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \ +res;}) + +#define clear_bit(nr,addr) ({\ +register int res __asm__("ax"); \ +__asm__ __volatile__("btrl %2,%3\n\tsetnb %%al": \ +"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \ +res;}) + +#define find_first_zero(addr) ({ \ +int __res; \ +__asm__ __volatile__ ("cld\n" \ + "1:\tlodsl\n\t" \ + "notl %%eax\n\t" \ + "bsfl %%eax,%%edx\n\t" \ + "je 2f\n\t" \ + "addl %%edx,%%ecx\n\t" \ + "jmp 3f\n" \ + "2:\taddl $32,%%ecx\n\t" \ + "cmpl $8192,%%ecx\n\t" \ + "jl 1b\n" \ + "3:" \ + :"=c" (__res):"c" (0),"S" (addr)); \ +__res;}) + +void free_block(int dev, int block) +{ + struct super_block * sb; + struct buffer_head * bh; + + if (!(sb = get_super(dev))) + panic("trying to free block on nonexistent device"); + if (block < sb->s_firstdatazone || block >= sb->s_nzones) + panic("trying to free block not in datazone"); + bh = get_hash_table(dev,block); + if (bh) { + if (bh->b_count != 1) { + printk("trying to free block (%04x:%d), count=%d\n", + dev,block,bh->b_count); + return; + } + bh->b_dirt=0; + bh->b_uptodate=0; + brelse(bh); + } + block -= sb->s_firstdatazone - 1 ; + if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) { + printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1); + panic("free_block: bit already cleared"); + } + sb->s_zmap[block/8192]->b_dirt = 1; +} + +int new_block(int dev) +{ + struct buffer_head * bh; + struct super_block * sb; + int i,j; + + if (!(sb = get_super(dev))) + panic("trying to get new block from nonexistant device"); + j = 8192; + for (i=0 ; i<8 ; i++) + if (bh=sb->s_zmap[i]) + if ((j=find_first_zero(bh->b_data))<8192) + break; + if (i>=8 || !bh || j>=8192) + return 0; + if (set_bit(j,bh->b_data)) + panic("new_block: bit already set"); + bh->b_dirt = 1; + j += i*8192 + sb->s_firstdatazone-1; + if (j >= sb->s_nzones) + return 0; + if (!(bh=getblk(dev,j))) + panic("new_block: cannot get block"); + if (bh->b_count != 1) + panic("new block: count is != 1"); + clear_block(bh->b_data); + bh->b_uptodate = 1; + bh->b_dirt = 1; + brelse(bh); + return j; +} + +void free_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + + if (!inode) + return; + if (!inode->i_dev) { + memset(inode,0,sizeof(*inode)); + return; + } + if (inode->i_count>1) { + printk("trying to free inode with count=%d\n",inode->i_count); + panic("free_inode"); + } + if (inode->i_nlinks) + panic("trying to free inode with links"); + if (!(sb = get_super(inode->i_dev))) + panic("trying to free inode on nonexistent device"); + if (inode->i_num < 1 || inode->i_num > sb->s_ninodes) + panic("trying to free inode 0 or nonexistant inode"); + if (!(bh=sb->s_imap[inode->i_num>>13])) + panic("nonexistent imap in superblock"); + if (clear_bit(inode->i_num&8191,bh->b_data)) + printk("free_inode: bit already cleared.\n\r"); + bh->b_dirt = 1; + memset(inode,0,sizeof(*inode)); +} + +struct m_inode * new_inode(int dev) +{ + struct m_inode * inode; + struct super_block * sb; + struct buffer_head * bh; + int i,j; + + if (!(inode=get_empty_inode())) + return NULL; + if (!(sb = get_super(dev))) + panic("new_inode with unknown device"); + j = 8192; + for (i=0 ; i<8 ; i++) + if (bh=sb->s_imap[i]) + if ((j=find_first_zero(bh->b_data))<8192) + break; + if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) { + iput(inode); + return NULL; + } + if (set_bit(j,bh->b_data)) + panic("new_inode: bit already set"); + bh->b_dirt = 1; + inode->i_count=1; + inode->i_nlinks=1; + inode->i_dev=dev; + inode->i_uid=current->euid; + inode->i_gid=current->egid; + inode->i_dirt=1; + inode->i_num = j + i*8192; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + return inode; +} diff --git a/4/linux/fs/block_dev.c b/4/linux/fs/block_dev.c new file mode 100644 index 0000000..a50ae3f --- /dev/null +++ b/4/linux/fs/block_dev.c @@ -0,0 +1,73 @@ +/* + * linux/fs/block_dev.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +#include +#include +#include +#include + +int block_write(int dev, long * pos, char * buf, int count) +{ + int block = *pos >> BLOCK_SIZE_BITS; + int offset = *pos & (BLOCK_SIZE-1); + int chars; + int written = 0; + struct buffer_head * bh; + register char * p; + + while (count>0) { + chars = BLOCK_SIZE - offset; + if (chars > count) + chars=count; + if (chars == BLOCK_SIZE) + bh = getblk(dev,block); + else + bh = breada(dev,block,block+1,block+2,-1); + block++; + if (!bh) + return written?written:-EIO; + p = offset + bh->b_data; + offset = 0; + *pos += chars; + written += chars; + count -= chars; + while (chars-->0) + *(p++) = get_fs_byte(buf++); + bh->b_dirt = 1; + brelse(bh); + } + return written; +} + +int block_read(int dev, unsigned long * pos, char * buf, int count) +{ + int block = *pos >> BLOCK_SIZE_BITS; + int offset = *pos & (BLOCK_SIZE-1); + int chars; + int read = 0; + struct buffer_head * bh; + register char * p; + + while (count>0) { + chars = BLOCK_SIZE-offset; + if (chars > count) + chars = count; + if (!(bh = breada(dev,block,block+1,block+2,-1))) + return read?read:-EIO; + block++; + p = offset + bh->b_data; + offset = 0; + *pos += chars; + read += chars; + count -= chars; + while (chars-->0) + put_fs_byte(*(p++),buf++); + brelse(bh); + } + return read; +} diff --git a/4/linux/fs/buffer.c b/4/linux/fs/buffer.c new file mode 100644 index 0000000..a6643aa --- /dev/null +++ b/4/linux/fs/buffer.c @@ -0,0 +1,384 @@ +/* + * linux/fs/buffer.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 'buffer.c' implements the buffer-cache functions. Race-conditions have + * been avoided by NEVER letting a interrupt change a buffer (except for the + * data, of course), but instead letting the caller do it. NOTE! As interrupts + * can wake up a caller, some cli-sti sequences are needed to check for + * sleep-on-calls. These should be extremely quick, though (I hope). + */ + +/* + * NOTE! There is one discordant note here: checking floppies for + * disk change. This is where it fits best, I think, as it should + * invalidate changed floppy-disk-caches. + */ + +#include + +#include +#include +#include +#include +#include + +extern void put_super(int dev); +extern void invalidate_inodes(int dev); + +extern int end; +struct buffer_head * start_buffer = (struct buffer_head *) &end; +struct buffer_head * hash_table[NR_HASH]; +static struct buffer_head * free_list; +static struct task_struct * buffer_wait = NULL; +int NR_BUFFERS = 0; + +static inline void wait_on_buffer(struct buffer_head * bh) +{ + cli(); + while (bh->b_lock) + sleep_on(&bh->b_wait); + sti(); +} + +int sys_sync(void) +{ + int i; + struct buffer_head * bh; + + sync_inodes(); /* write out inodes into buffers */ + bh = start_buffer; + for (i=0 ; ib_dirt) + ll_rw_block(WRITE,bh); + } + return 0; +} + +int sync_dev(int dev) +{ + int i; + struct buffer_head * bh; + + bh = start_buffer; + for (i=0 ; ib_dev != dev) + continue; + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_dirt) + ll_rw_block(WRITE,bh); + } + sync_inodes(); + bh = start_buffer; + for (i=0 ; ib_dev != dev) + continue; + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_dirt) + ll_rw_block(WRITE,bh); + } + return 0; +} + +void inline invalidate_buffers(int dev) +{ + int i; + struct buffer_head * bh; + + bh = start_buffer; + for (i=0 ; ib_dev != dev) + continue; + wait_on_buffer(bh); + if (bh->b_dev == dev) + bh->b_uptodate = bh->b_dirt = 0; + } +} + +/* + * This routine checks whether a floppy has been changed, and + * invalidates all buffer-cache-entries in that case. This + * is a relatively slow routine, so we have to try to minimize using + * it. Thus it is called only upon a 'mount' or 'open'. This + * is the best way of combining speed and utility, I think. + * People changing diskettes in the middle of an operation deserve + * to loose :-) + * + * NOTE! Although currently this is only for floppies, the idea is + * that any additional removable block-device will use this routine, + * and that mount/open needn't know that floppies/whatever are + * special. + */ +void check_disk_change(int dev) +{ + int i; + + if (MAJOR(dev) != 2) + return; + if (!floppy_change(dev & 0x03)) + return; + for (i=0 ; ib_next) + bh->b_next->b_prev = bh->b_prev; + if (bh->b_prev) + bh->b_prev->b_next = bh->b_next; + if (hash(bh->b_dev,bh->b_blocknr) == bh) + hash(bh->b_dev,bh->b_blocknr) = bh->b_next; +/* remove from free list */ + if (!(bh->b_prev_free) || !(bh->b_next_free)) + panic("Free block list corrupted"); + bh->b_prev_free->b_next_free = bh->b_next_free; + bh->b_next_free->b_prev_free = bh->b_prev_free; + if (free_list == bh) + free_list = bh->b_next_free; +} + +static inline void insert_into_queues(struct buffer_head * bh) +{ +/* put at end of free list */ + bh->b_next_free = free_list; + bh->b_prev_free = free_list->b_prev_free; + free_list->b_prev_free->b_next_free = bh; + free_list->b_prev_free = bh; +/* put the buffer in new hash-queue if it has a device */ + bh->b_prev = NULL; + bh->b_next = NULL; + if (!bh->b_dev) + return; + bh->b_next = hash(bh->b_dev,bh->b_blocknr); + hash(bh->b_dev,bh->b_blocknr) = bh; + bh->b_next->b_prev = bh; +} + +static struct buffer_head * find_buffer(int dev, int block) +{ + struct buffer_head * tmp; + + for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next) + if (tmp->b_dev==dev && tmp->b_blocknr==block) + return tmp; + return NULL; +} + +/* + * Why like this, I hear you say... The reason is race-conditions. + * As we don't lock buffers (unless we are readint them, that is), + * something might happen to it while we sleep (ie a read-error + * will force it bad). This shouldn't really happen currently, but + * the code is ready. + */ +struct buffer_head * get_hash_table(int dev, int block) +{ + struct buffer_head * bh; + + for (;;) { + if (!(bh=find_buffer(dev,block))) + return NULL; + bh->b_count++; + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_blocknr == block) + return bh; + bh->b_count--; + } +} + +/* + * Ok, this is getblk, and it isn't very clear, again to hinder + * race-conditions. Most of the code is seldom used, (ie repeating), + * so it should be much more efficient than it looks. + * + * The algoritm is changed: hopefully better, and an elusive bug removed. + */ +#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock) +struct buffer_head * getblk(int dev,int block) +{ + struct buffer_head * tmp, * bh; + +repeat: + if (bh = get_hash_table(dev,block)) + return bh; + tmp = free_list; + do { + if (tmp->b_count) + continue; + if (!bh || BADNESS(tmp)b_next_free) != free_list); + if (!bh) { + sleep_on(&buffer_wait); + goto repeat; + } + wait_on_buffer(bh); + if (bh->b_count) + goto repeat; + while (bh->b_dirt) { + sync_dev(bh->b_dev); + wait_on_buffer(bh); + if (bh->b_count) + goto repeat; + } +/* NOTE!! While we slept waiting for this block, somebody else might */ +/* already have added "this" block to the cache. check it */ + if (find_buffer(dev,block)) + goto repeat; +/* OK, FINALLY we know that this buffer is the only one of it's kind, */ +/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */ + bh->b_count=1; + bh->b_dirt=0; + bh->b_uptodate=0; + remove_from_queues(bh); + bh->b_dev=dev; + bh->b_blocknr=block; + insert_into_queues(bh); + return bh; +} + +void brelse(struct buffer_head * buf) +{ + if (!buf) + return; + wait_on_buffer(buf); + if (!(buf->b_count--)) + panic("Trying to free free buffer"); + wake_up(&buffer_wait); +} + +/* + * bread() reads a specified block and returns the buffer that contains + * it. It returns NULL if the block was unreadable. + */ +struct buffer_head * bread(int dev,int block) +{ + struct buffer_head * bh; + + if (!(bh=getblk(dev,block))) + panic("bread: getblk returned NULL\n"); + if (bh->b_uptodate) + return bh; + ll_rw_block(READ,bh); + wait_on_buffer(bh); + if (bh->b_uptodate) + return bh; + brelse(bh); + return NULL; +} + +#define COPYBLK(from,to) \ +__asm__("cld\n\t" \ + "rep\n\t" \ + "movsl\n\t" \ + ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \ + ) + +/* + * bread_page reads four buffers into memory at the desired address. It's + * a function of its own, as there is some speed to be got by reading them + * all at the same time, not waiting for one to be read, and then another + * etc. + */ +void bread_page(unsigned long address,int dev,int b[4]) +{ + struct buffer_head * bh[4]; + int i; + + for (i=0 ; i<4 ; i++) + if (b[i]) { + if (bh[i] = getblk(dev,b[i])) + if (!bh[i]->b_uptodate) + ll_rw_block(READ,bh[i]); + } else + bh[i] = NULL; + for (i=0 ; i<4 ; i++,address += BLOCK_SIZE) + if (bh[i]) { + wait_on_buffer(bh[i]); + if (bh[i]->b_uptodate) + COPYBLK((unsigned long) bh[i]->b_data,address); + brelse(bh[i]); + } +} + +/* + * Ok, breada can be used as bread, but additionally to mark other + * blocks for reading as well. End the argument list with a negative + * number. + */ +struct buffer_head * breada(int dev,int first, ...) +{ + va_list args; + struct buffer_head * bh, *tmp; + + va_start(args,first); + if (!(bh=getblk(dev,first))) + panic("bread: getblk returned NULL\n"); + if (!bh->b_uptodate) + ll_rw_block(READ,bh); + while ((first=va_arg(args,int))>=0) { + tmp=getblk(dev,first); + if (tmp) { + if (!tmp->b_uptodate) + ll_rw_block(READA,bh); + tmp->b_count--; + } + } + va_end(args); + wait_on_buffer(bh); + if (bh->b_uptodate) + return bh; + brelse(bh); + return (NULL); +} + +void buffer_init(long buffer_end) +{ + struct buffer_head * h = start_buffer; + void * b; + int i; + + if (buffer_end == 1<<20) + b = (void *) (640*1024); + else + b = (void *) buffer_end; + while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { + h->b_dev = 0; + h->b_dirt = 0; + h->b_count = 0; + h->b_lock = 0; + h->b_uptodate = 0; + h->b_wait = NULL; + h->b_next = NULL; + h->b_prev = NULL; + h->b_data = (char *) b; + h->b_prev_free = h-1; + h->b_next_free = h+1; + h++; + NR_BUFFERS++; + if (b == (void *) 0x100000) + b = (void *) 0xA0000; + } + h--; + free_list = start_buffer; + free_list->b_prev_free = h; + h->b_next_free = free_list; + for (i=0;i +#include + +#include +#include + +#include +#include + +extern int tty_read(unsigned minor,char * buf,int count); +extern int tty_write(unsigned minor,char * buf,int count); + +typedef (*crw_ptr)(int rw,unsigned minor,char * buf,int count,off_t * pos); + +static int rw_ttyx(int rw,unsigned minor,char * buf,int count,off_t * pos) +{ + return ((rw==READ)?tty_read(minor,buf,count): + tty_write(minor,buf,count)); +} + +static int rw_tty(int rw,unsigned minor,char * buf,int count, off_t * pos) +{ + if (current->tty<0) + return -EPERM; + return rw_ttyx(rw,current->tty,buf,count,pos); +} + +static int rw_ram(int rw,char * buf, int count, off_t *pos) +{ + return -EIO; +} + +static int rw_mem(int rw,char * buf, int count, off_t * pos) +{ + return -EIO; +} + +static int rw_kmem(int rw,char * buf, int count, off_t * pos) +{ + return -EIO; +} + +static int rw_port(int rw,char * buf, int count, off_t * pos) +{ + int i=*pos; + + while (count-->0 && i<65536) { + if (rw==READ) + put_fs_byte(inb(i),buf++); + else + outb(get_fs_byte(buf++),i); + i++; + } + i -= *pos; + *pos += i; + return i; +} + +static int rw_memory(int rw, unsigned minor, char * buf, int count, off_t * pos) +{ + switch(minor) { + case 0: + return rw_ram(rw,buf,count,pos); + case 1: + return rw_mem(rw,buf,count,pos); + case 2: + return rw_kmem(rw,buf,count,pos); + case 3: + return (rw==READ)?0:count; /* rw_null */ + case 4: + return rw_port(rw,buf,count,pos); + default: + return -EIO; + } +} + +#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr))) + +static crw_ptr crw_table[]={ + NULL, /* nodev */ + rw_memory, /* /dev/mem etc */ + NULL, /* /dev/fd */ + NULL, /* /dev/hd */ + rw_ttyx, /* /dev/ttyx */ + rw_tty, /* /dev/tty */ + NULL, /* /dev/lp */ + NULL}; /* unnamed pipes */ + +int rw_char(int rw,int dev, char * buf, int count, off_t * pos) +{ + crw_ptr call_addr; + + if (MAJOR(dev)>=NRDEVS) + return -ENODEV; + if (!(call_addr=crw_table[MAJOR(dev)])) + return -ENODEV; + return call_addr(rw,MINOR(dev),buf,count,pos); +} diff --git a/4/linux/fs/exec.c b/4/linux/fs/exec.c new file mode 100644 index 0000000..da13a61 --- /dev/null +++ b/4/linux/fs/exec.c @@ -0,0 +1,578 @@ +/* + * linux/fs/exec.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * #!-checking implemented by tytso. + */ + +/* + * Demand-loading implemented 01.12.91 - no need to read anything but + * the header into memory. The inode of the executable is put into + * "current->executable", and page faults do the actual loading. Clean. + * + * Once more I can proudly say that linux stood up to being changed: it + * was less than 2 hours work to get demand-loading completely implemented. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int sys_exit(int exit_code); +extern int sys_close(int fd); + +/* + * MAX_ARG_PAGES defines the number of pages allocated for arguments + * and envelope for the new program. 32 should suffice, this gives + * a maximum env+arg of 128kB ! + */ +#define MAX_ARG_PAGES 32 + +#define DIRBUF 8192 /* buffer size for fs-indep. dirs */ + /* must in general be larger than the filesystem buffer size */ +#define NAME_MAX 14 +typedef struct + { + int dd_fd; /* file descriptor */ + int dd_loc; /* offset in block */ + int dd_size; /* amount of valid data */ + char *dd_buf; /* -> directory block */ +} DIR; /* stream data from opendir() */ + +struct direct { + ino_t d_ino; + char d_name[NAME_MAX]; +}; + +int sys_uselib() +{ + return -ENOSYS; +} + +/* + * create_tables() parses the env- and arg-strings in new user + * memory and creates the pointer tables from them, and puts their + * addresses on the "stack", returning the new stack pointer value. + */ +static unsigned long * create_tables(char * p,int argc,int envc) +{ + unsigned long *argv,*envp; + unsigned long * sp; + + sp = (unsigned long *) (0xfffffffc & (unsigned long) p); + sp -= envc+1; + envp = sp; + sp -= argc+1; + argv = sp; + put_fs_long((unsigned long)envp,--sp); + put_fs_long((unsigned long)argv,--sp); + put_fs_long((unsigned long)argc,--sp); + while (argc-->0) { + put_fs_long((unsigned long) p,argv++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,argv); + while (envc-->0) { + put_fs_long((unsigned long) p,envp++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,envp); + return sp; +} + +/* + * count() counts the number of arguments/envelopes + */ +static int count(char ** argv) +{ + int i=0; + char ** tmp; + + if (tmp = argv) + while (get_fs_long((unsigned long *) (tmp++))) + i++; + + return i; +} + +/* + * 'copy_string()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + * + * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies + * whether the string and the string array are from user or kernel segments: + * + * from_kmem argv * argv ** + * 0 user space user space + * 1 kernel space user space + * 2 kernel space kernel space + * + * We do this by playing games with the fs segment register. Since it + * it is expensive to load a segment register, we try to avoid calling + * set_fs() unless we absolutely have to. + */ +static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, + unsigned long p, int from_kmem) +{ + char *tmp, *pag; + int len, offset = 0; + unsigned long old_fs, new_fs; + + if (!p) + return 0; /* bullet-proofing */ + new_fs = get_ds(); + old_fs = get_fs(); + if (from_kmem==2) + set_fs(new_fs); + while (argc-- > 0) { + if (from_kmem == 1) + set_fs(new_fs); + if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc))) + panic("argc is wrong"); + if (from_kmem == 1) + set_fs(old_fs); + len=0; /* remember zero-padding */ + do { + len++; + } while (get_fs_byte(tmp++)); + if (p-len < 0) { /* this shouldn't happen - 128kB */ + set_fs(old_fs); + return 0; + } + while (len) { + --p; --tmp; --len; + if (--offset < 0) { + offset = p % PAGE_SIZE; + if (from_kmem==2) + set_fs(old_fs); + if (!(pag = (char *) page[p/PAGE_SIZE]) && + !(pag = (char *) (page[p/PAGE_SIZE] = + (unsigned long *) get_free_page()))) + return 0; + if (from_kmem==2) + set_fs(new_fs); + + } + *(pag + offset) = get_fs_byte(tmp); + } + } + if (from_kmem==2) + set_fs(old_fs); + return p; +} + +static unsigned long change_ldt(unsigned long text_size,unsigned long * page) +{ + unsigned long code_limit,data_limit,code_base,data_base; + int i; + + code_limit = text_size+PAGE_SIZE -1; + code_limit &= 0xFFFFF000; + data_limit = 0x4000000; + code_base = get_base(current->ldt[1]); + data_base = code_base; + set_base(current->ldt[1],code_base); + set_limit(current->ldt[1],code_limit); + set_base(current->ldt[2],data_base); + set_limit(current->ldt[2],data_limit); +/* make sure fs points to the NEW data segment */ + __asm__("pushl $0x17\n\tpop %%fs"::); + data_base += data_limit; + for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) { + data_base -= PAGE_SIZE; + if (page[i]) + put_page(page[i],data_base); + } + return data_limit; +} + +/* + * 'do_execve()' executes a new program. + */ +int do_execve(unsigned long * eip,long tmp,char * filename, + char ** argv, char ** envp) +{ + struct m_inode * inode; + struct buffer_head * bh; + struct exec ex; + unsigned long page[MAX_ARG_PAGES]; + int i,argc,envc; + int e_uid, e_gid; + int retval; + int sh_bang = 0; + unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4; + + if ((0xffff & eip[1]) != 0x000f) + panic("execve called from supervisor mode"); + for (i=0 ; ii_mode)) { /* must be regular file */ + retval = -EACCES; + goto exec_error2; + } + i = inode->i_mode; + e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; + e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; + if (current->euid == inode->i_uid) + i >>= 6; + else if (current->egid == inode->i_gid) + i >>= 3; + if (!(i & 1) && + !((inode->i_mode & 0111) && suser())) { + retval = -ENOEXEC; + goto exec_error2; + } + if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) { + retval = -EACCES; + goto exec_error2; + } + ex = *((struct exec *) bh->b_data); /* read exec-header */ + if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { + /* + * This section does the #! interpretation. + * Sorta complicated, but hopefully it will work. -TYT + */ + + char buf[1023], *cp, *interp, *i_name, *i_arg; + unsigned long old_fs; + + strncpy(buf, bh->b_data+2, 1022); + brelse(bh); + iput(inode); + buf[1022] = '\0'; + if (cp = strchr(buf, '\n')) { + *cp = '\0'; + for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++); + } + if (!cp || *cp == '\0') { + retval = -ENOEXEC; /* No interpreter name found */ + goto exec_error1; + } + interp = i_name = cp; + i_arg = 0; + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { + if (*cp == '/') + i_name = cp+1; + } + if (*cp) { + *cp++ = '\0'; + i_arg = cp; + } + /* + * OK, we've parsed out the interpreter name and + * (optional) argument. + */ + if (sh_bang++ == 0) { + p = copy_strings(envc, envp, page, p, 0); + p = copy_strings(--argc, argv+1, page, p, 0); + } + /* + * Splice in (1) the interpreter's name for argv[0] + * (2) (optional) argument to interpreter + * (3) filename of shell script + * + * This is done in reverse order, because of how the + * user environment and arguments are stored. + */ + p = copy_strings(1, &filename, page, p, 1); + argc++; + if (i_arg) { + p = copy_strings(1, &i_arg, page, p, 2); + argc++; + } + p = copy_strings(1, &i_name, page, p, 2); + argc++; + if (!p) { + retval = -ENOMEM; + goto exec_error1; + } + /* + * OK, now restart the process with the interpreter's inode. + */ + old_fs = get_fs(); + set_fs(get_ds()); + if (!(inode=namei(interp))) { /* get executables inode */ + set_fs(old_fs); + retval = -ENOENT; + goto exec_error1; + } + set_fs(old_fs); + goto restart_interp; + } + brelse(bh); + if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || + ex.a_text+ex.a_data+ex.a_bss>0x3000000 || + inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { + retval = -ENOEXEC; + goto exec_error2; + } + if (N_TXTOFF(ex) != BLOCK_SIZE) { + printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); + retval = -ENOEXEC; + goto exec_error2; + } + if (!sh_bang) { + p = copy_strings(envc,envp,page,p,0); + p = copy_strings(argc,argv,page,p,0); + if (!p) { + retval = -ENOMEM; + goto exec_error2; + } + } +/* OK, This is the point of no return */ + if (current->executable) + iput(current->executable); + current->executable = inode; + for (i=0 ; i<32 ; i++) + current->sigaction[i].sa_handler = NULL; + for (i=0 ; iclose_on_exec>>i)&1) + sys_close(i); + current->close_on_exec = 0; + free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); + if (last_task_used_math == current) + last_task_used_math = NULL; + current->used_math = 0; + p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE; + p = (unsigned long) create_tables((char *)p,argc,envc); + current->brk = ex.a_bss + + (current->end_data = ex.a_data + + (current->end_code = ex.a_text)); + current->start_stack = p & 0xfffff000; + current->euid = e_uid; + current->egid = e_gid; + i = ex.a_text+ex.a_data; + while (i&0xfff) + put_fs_byte(0,(char *) (i++)); + eip[0] = ex.a_entry; /* eip, magic happens :-) */ + eip[3] = p; /* stack pointer */ + return 0; +exec_error2: + iput(inode); +exec_error1: + for (i=0 ; ii_mode)) { /* must be regular file */ + retval = -EACCES; + goto exec_error2; + } + i = inode->i_mode; + e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; + e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; + if (current->euid == inode->i_uid) + i >>= 6; + else if (current->egid == inode->i_gid) + i >>= 3; + if (!(i & 1) && + !((inode->i_mode & 0111) && suser())) { + retval = -ENOEXEC; + goto exec_error2; + } + if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) { + retval = -EACCES; + goto exec_error2; + } + ex = *((struct exec *) bh->b_data); /* read exec-header */ + if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { + /* + * This section does the #! interpretation. + * Sorta complicated, but hopefully it will work. -TYT + */ + + char buf[1023], *cp, *interp, *i_name, *i_arg; + unsigned long old_fs; + + strncpy(buf, bh->b_data+2, 1022); + brelse(bh); + iput(inode); + buf[1022] = '\0'; + if (cp = strchr(buf, '\n')) { + *cp = '\0'; + for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++); + } + if (!cp || *cp == '\0') { + retval = -ENOEXEC; /* No interpreter name found */ + goto exec_error1; + } + interp = i_name = cp; + i_arg = 0; + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { + if (*cp == '/') + i_name = cp+1; + } + if (*cp) { + *cp++ = '\0'; + i_arg = cp; + } + /* + * OK, we've parsed out the interpreter name and + * (optional) argument. + */ + if (sh_bang++ == 0) { + p = copy_strings(envc, envp, page, p, 0); + p = copy_strings(--argc, argv+1, page, p, 0); + } + /* + * Splice in (1) the interpreter's name for argv[0] + * (2) (optional) argument to interpreter + * (3) filename of shell script + * + * This is done in reverse order, because of how the + * user environment and arguments are stored. + */ + p = copy_strings(1, &filename, page, p, 1); + argc++; + if (i_arg) { + p = copy_strings(1, &i_arg, page, p, 2); + argc++; + } + p = copy_strings(1, &i_name, page, p, 2); + argc++; + if (!p) { + retval = -ENOMEM; + goto exec_error1; + } + /* + * OK, now restart the process with the interpreter's inode. + */ + old_fs = get_fs(); + set_fs(get_ds()); + if (!(inode=namei(interp))) { /* get executables inode */ + set_fs(old_fs); + retval = -ENOENT; + goto exec_error1; + } + set_fs(old_fs); + goto restart_interp; + } + brelse(bh); + if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || + ex.a_text+ex.a_data+ex.a_bss>0x3000000 || + inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { + retval = -ENOEXEC; + goto exec_error2; + } + if (N_TXTOFF(ex) != BLOCK_SIZE) { + printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); + retval = -ENOEXEC; + goto exec_error2; + } + if (!sh_bang) { + p = copy_strings(envc,envp,page,p,0); + p = copy_strings(argc,argv,page,p,0); + if (!p) { + retval = -ENOMEM; + goto exec_error2; + } + } +/* OK, This is the point of no return */ + if (current->executable) + iput(current->executable); + current->executable = inode; + for (i=0 ; i<32 ; i++) + current->sigaction[i].sa_handler = NULL; + for (i=0 ; iclose_on_exec>>i)&1) + sys_close(i); + current->close_on_exec = 0; + free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); + if (last_task_used_math == current) + last_task_used_math = NULL; + current->used_math = 0; + p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE; + p = (unsigned long) create_tables((char *)p,argc,envc); + current->brk = ex.a_bss + + (current->end_data = ex.a_data + + (current->end_code = ex.a_text)); + current->start_stack = p & 0xfffff000; + current->euid = e_uid; + current->egid = e_gid; + i = ex.a_text+ex.a_data; + while (i&0xfff) + put_fs_byte(0,(char *) (i++)); + eip[0] = ex.a_entry; /* eip, magic happens :-) */ + eip[3] = p; /* stack pointer */ + + begin &= 0xfffff000; + end = (current->brk - 1) & 0xfffff000; + for (addr = begin; addr <= end; addr += 4096) { + do_no_page1(addr + current->start_code); + } + return 0; +exec_error2: + iput(inode); +exec_error1: + for (i=0 ; ifilp[fd]->f_inode->i_dev, current->filp[fd]->f_inode->i_zone[0]); + de=(struct dir_entry*) bh->b_data; + for (i = 0; i < 100; i++) { + if (de->inode == 0 || (i + 1) * length > count) break; + p.d_off = 0; + p.d_reclen = length; + p.d_ino = de[i].inode; + strcpy(p.d_name, de[i].name); + for(k = 0; k < length; k++) { + put_fs_byte(((char*)&p)[k],((char*)dirp + ct)); + ct++; + } + } + return ct; +} diff --git a/4/linux/fs/fcntl.c b/4/linux/fs/fcntl.c new file mode 100644 index 0000000..c201aa8 --- /dev/null +++ b/4/linux/fs/fcntl.c @@ -0,0 +1,75 @@ +/* + * linux/fs/fcntl.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include +#include +#include + +#include +#include + +extern int sys_close(int fd); + +static int dupfd(unsigned int fd, unsigned int arg) +{ + if (fd >= NR_OPEN || !current->filp[fd]) + return -EBADF; + if (arg >= NR_OPEN) + return -EINVAL; + while (arg < NR_OPEN) + if (current->filp[arg]) + arg++; + else + break; + if (arg >= NR_OPEN) + return -EMFILE; + current->close_on_exec &= ~(1<filp[arg] = current->filp[fd])->f_count++; + return arg; +} + +int sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + sys_close(newfd); + return dupfd(oldfd,newfd); +} + +int sys_dup(unsigned int fildes) +{ + return dupfd(fildes,0); +} + +int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + + if (fd >= NR_OPEN || !(filp = current->filp[fd])) + return -EBADF; + switch (cmd) { + case F_DUPFD: + return dupfd(fd,arg); + case F_GETFD: + return (current->close_on_exec>>fd)&1; + case F_SETFD: + if (arg&1) + current->close_on_exec |= (1<close_on_exec &= ~(1<f_flags; + case F_SETFL: + filp->f_flags &= ~(O_APPEND | O_NONBLOCK); + filp->f_flags |= arg & (O_APPEND | O_NONBLOCK); + return 0; + case F_GETLK: case F_SETLK: case F_SETLKW: + return -1; + default: + return -1; + } +} diff --git a/4/linux/fs/file_dev.c b/4/linux/fs/file_dev.c new file mode 100644 index 0000000..0c50eaa --- /dev/null +++ b/4/linux/fs/file_dev.c @@ -0,0 +1,90 @@ +/* + * linux/fs/file_dev.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +int file_read(struct m_inode * inode, struct file * filp, char * buf, int count) +{ + int left,chars,nr; + struct buffer_head * bh; + + if ((left=count)<=0) + return 0; + while (left) { + if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) { + if (!(bh=bread(inode->i_dev,nr))) + break; + } else + bh = NULL; + nr = filp->f_pos % BLOCK_SIZE; + chars = MIN( BLOCK_SIZE-nr , left ); + filp->f_pos += chars; + left -= chars; + if (bh) { + char * p = nr + bh->b_data; + while (chars-->0) + put_fs_byte(*(p++),buf++); + brelse(bh); + } else { + while (chars-->0) + put_fs_byte(0,buf++); + } + } + inode->i_atime = CURRENT_TIME; + return (count-left)?(count-left):-ERROR; +} + +int file_write(struct m_inode * inode, struct file * filp, char * buf, int count) +{ + off_t pos; + int block,c; + struct buffer_head * bh; + char * p; + int i=0; + +/* + * ok, append may not work when many processes are writing at the same time + * but so what. That way leads to madness anyway. + */ + if (filp->f_flags & O_APPEND) + pos = inode->i_size; + else + pos = filp->f_pos; + while (ii_dev,block))) + break; + c = pos % BLOCK_SIZE; + p = c + bh->b_data; + bh->b_dirt = 1; + c = BLOCK_SIZE-c; + if (c > count-i) c = count-i; + pos += c; + if (pos > inode->i_size) { + inode->i_size = pos; + inode->i_dirt = 1; + } + i += c; + while (c-->0) + *(p++) = get_fs_byte(buf++); + brelse(bh); + } + inode->i_mtime = CURRENT_TIME; + if (!(filp->f_flags & O_APPEND)) { + filp->f_pos = pos; + inode->i_ctime = CURRENT_TIME; + } + return (i?i:-1); +} diff --git a/4/linux/fs/file_table.c b/4/linux/fs/file_table.c new file mode 100644 index 0000000..e0589ac --- /dev/null +++ b/4/linux/fs/file_table.c @@ -0,0 +1,9 @@ +/* + * linux/fs/file_table.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +struct file file_table[NR_FILE]; diff --git a/4/linux/fs/inode.c b/4/linux/fs/inode.c new file mode 100644 index 0000000..9bb10b3 --- /dev/null +++ b/4/linux/fs/inode.c @@ -0,0 +1,340 @@ +/* + * linux/fs/inode.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include +#include + +struct m_inode inode_table[NR_INODE]={{0,},}; + +static void read_inode(struct m_inode * inode); +static void write_inode(struct m_inode * inode); + +static inline void wait_on_inode(struct m_inode * inode) +{ + cli(); + while (inode->i_lock) + sleep_on(&inode->i_wait); + sti(); +} + +static inline void lock_inode(struct m_inode * inode) +{ + cli(); + while (inode->i_lock) + sleep_on(&inode->i_wait); + inode->i_lock=1; + sti(); +} + +static inline void unlock_inode(struct m_inode * inode) +{ + inode->i_lock=0; + wake_up(&inode->i_wait); +} + +void invalidate_inodes(int dev) +{ + int i; + struct m_inode * inode; + + inode = 0+inode_table; + for(i=0 ; ii_dev == dev) { + if (inode->i_count) + printk("inode in use on removed disk\n\r"); + inode->i_dev = inode->i_dirt = 0; + } + } +} + +void sync_inodes(void) +{ + int i; + struct m_inode * inode; + + inode = 0+inode_table; + for(i=0 ; ii_dirt && !inode->i_pipe) + write_inode(inode); + } +} + +static int _bmap(struct m_inode * inode,int block,int create) +{ + struct buffer_head * bh; + int i; + + if (block<0) + panic("_bmap: block<0"); + if (block >= 7+512+512*512) + panic("_bmap: block>big"); + if (block<7) { + if (create && !inode->i_zone[block]) + if (inode->i_zone[block]=new_block(inode->i_dev)) { + inode->i_ctime=CURRENT_TIME; + inode->i_dirt=1; + } + return inode->i_zone[block]; + } + block -= 7; + if (block<512) { + if (create && !inode->i_zone[7]) + if (inode->i_zone[7]=new_block(inode->i_dev)) { + inode->i_dirt=1; + inode->i_ctime=CURRENT_TIME; + } + if (!inode->i_zone[7]) + return 0; + if (!(bh = bread(inode->i_dev,inode->i_zone[7]))) + return 0; + i = ((unsigned short *) (bh->b_data))[block]; + if (create && !i) + if (i=new_block(inode->i_dev)) { + ((unsigned short *) (bh->b_data))[block]=i; + bh->b_dirt=1; + } + brelse(bh); + return i; + } + block -= 512; + if (create && !inode->i_zone[8]) + if (inode->i_zone[8]=new_block(inode->i_dev)) { + inode->i_dirt=1; + inode->i_ctime=CURRENT_TIME; + } + if (!inode->i_zone[8]) + return 0; + if (!(bh=bread(inode->i_dev,inode->i_zone[8]))) + return 0; + i = ((unsigned short *)bh->b_data)[block>>9]; + if (create && !i) + if (i=new_block(inode->i_dev)) { + ((unsigned short *) (bh->b_data))[block>>9]=i; + bh->b_dirt=1; + } + brelse(bh); + if (!i) + return 0; + if (!(bh=bread(inode->i_dev,i))) + return 0; + i = ((unsigned short *)bh->b_data)[block&511]; + if (create && !i) + if (i=new_block(inode->i_dev)) { + ((unsigned short *) (bh->b_data))[block&511]=i; + bh->b_dirt=1; + } + brelse(bh); + return i; +} + +int bmap(struct m_inode * inode,int block) +{ + return _bmap(inode,block,0); +} + +int create_block(struct m_inode * inode, int block) +{ + return _bmap(inode,block,1); +} + +void iput(struct m_inode * inode) +{ + if (!inode) + return; + wait_on_inode(inode); + if (!inode->i_count) + panic("iput: trying to free free inode"); + if (inode->i_pipe) { + wake_up(&inode->i_wait); + if (--inode->i_count) + return; + free_page(inode->i_size); + inode->i_count=0; + inode->i_dirt=0; + inode->i_pipe=0; + return; + } + if (!inode->i_dev) { + inode->i_count--; + return; + } + if (S_ISBLK(inode->i_mode)) { + sync_dev(inode->i_zone[0]); + wait_on_inode(inode); + } +repeat: + if (inode->i_count>1) { + inode->i_count--; + return; + } + if (!inode->i_nlinks) { + truncate(inode); + free_inode(inode); + return; + } + if (inode->i_dirt) { + write_inode(inode); /* we can sleep - so do again */ + wait_on_inode(inode); + goto repeat; + } + inode->i_count--; + return; +} + +struct m_inode * get_empty_inode(void) +{ + struct m_inode * inode; + static struct m_inode * last_inode = inode_table; + int i; + + do { + inode = NULL; + for (i = NR_INODE; i ; i--) { + if (++last_inode >= inode_table + NR_INODE) + last_inode = inode_table; + if (!last_inode->i_count) { + inode = last_inode; + if (!inode->i_dirt && !inode->i_lock) + break; + } + } + if (!inode) { + for (i=0 ; ii_dirt) { + write_inode(inode); + wait_on_inode(inode); + } + } while (inode->i_count); + memset(inode,0,sizeof(*inode)); + inode->i_count = 1; + return inode; +} + +struct m_inode * get_pipe_inode(void) +{ + struct m_inode * inode; + + if (!(inode = get_empty_inode())) + return NULL; + if (!(inode->i_size=get_free_page())) { + inode->i_count = 0; + return NULL; + } + inode->i_count = 2; /* sum of readers/writers */ + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + inode->i_pipe = 1; + return inode; +} + +struct m_inode * iget(int dev,int nr) +{ + struct m_inode * inode, * empty; + + if (!dev) + panic("iget with dev==0"); + empty = get_empty_inode(); + inode = inode_table; + while (inode < NR_INODE+inode_table) { + if (inode->i_dev != dev || inode->i_num != nr) { + inode++; + continue; + } + wait_on_inode(inode); + if (inode->i_dev != dev || inode->i_num != nr) { + inode = inode_table; + continue; + } + inode->i_count++; + if (inode->i_mount) { + int i; + + for (i = 0 ; i= NR_SUPER) { + printk("Mounted inode hasn't got sb\n"); + if (empty) + iput(empty); + return inode; + } + iput(inode); + dev = super_block[i].s_dev; + nr = ROOT_INO; + inode = inode_table; + continue; + } + if (empty) + iput(empty); + return inode; + } + if (!empty) + return (NULL); + inode=empty; + inode->i_dev = dev; + inode->i_num = nr; + read_inode(inode); + return inode; +} + +static void read_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + int block; + + lock_inode(inode); + if (!(sb=get_super(inode->i_dev))) + panic("trying to read inode without dev"); + block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + + (inode->i_num-1)/INODES_PER_BLOCK; + if (!(bh=bread(inode->i_dev,block))) + panic("unable to read i-node block"); + __asm__ volatile ("cld"); /* by wyj */ + *(struct d_inode *)inode = + ((struct d_inode *)bh->b_data) + [(inode->i_num-1)%INODES_PER_BLOCK]; + brelse(bh); + unlock_inode(inode); +} + +static void write_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + int block; + + lock_inode(inode); + if (!inode->i_dirt || !inode->i_dev) { + unlock_inode(inode); + return; + } + if (!(sb=get_super(inode->i_dev))) + panic("trying to write inode without device"); + block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + + (inode->i_num-1)/INODES_PER_BLOCK; + if (!(bh=bread(inode->i_dev,block))) + panic("unable to read i-node block"); + __asm__ volatile ("cld"); /* by wyj */ + ((struct d_inode *)bh->b_data) + [(inode->i_num-1)%INODES_PER_BLOCK] = + *(struct d_inode *)inode; + bh->b_dirt=1; + inode->i_dirt=0; + brelse(bh); + unlock_inode(inode); +} diff --git a/4/linux/fs/ioctl.c b/4/linux/fs/ioctl.c new file mode 100644 index 0000000..36fc976 --- /dev/null +++ b/4/linux/fs/ioctl.c @@ -0,0 +1,46 @@ +/* + * linux/fs/ioctl.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include + +#include + +extern int tty_ioctl(int dev, int cmd, int arg); + +typedef int (*ioctl_ptr)(int dev,int cmd,int arg); + +#define NRDEVS ((sizeof (ioctl_table))/(sizeof (ioctl_ptr))) + +static ioctl_ptr ioctl_table[]={ + NULL, /* nodev */ + NULL, /* /dev/mem */ + NULL, /* /dev/fd */ + NULL, /* /dev/hd */ + tty_ioctl, /* /dev/ttyx */ + tty_ioctl, /* /dev/tty */ + NULL, /* /dev/lp */ + NULL}; /* named pipes */ + + +int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int dev,mode; + + if (fd >= NR_OPEN || !(filp = current->filp[fd])) + return -EBADF; + mode=filp->f_inode->i_mode; + if (!S_ISCHR(mode) && !S_ISBLK(mode)) + return -EINVAL; + dev = filp->f_inode->i_zone[0]; + if (MAJOR(dev) >= NRDEVS) + return -ENODEV; + if (!ioctl_table[MAJOR(dev)]) + return -ENOTTY; + return ioctl_table[MAJOR(dev)](dev,cmd,arg); +} diff --git a/4/linux/fs/namei.c b/4/linux/fs/namei.c new file mode 100644 index 0000000..f8a02b9 --- /dev/null +++ b/4/linux/fs/namei.c @@ -0,0 +1,783 @@ +/* + * linux/fs/namei.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * Some corrections by tytso. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE]) + +/* + * comment out this line if you want names > NAME_LEN chars to be + * truncated. Else they will be disallowed. + */ +/* #define NO_TRUNCATE */ + +#define MAY_EXEC 1 +#define MAY_WRITE 2 +#define MAY_READ 4 + +/* + * permission() + * + * is used to check for read/write/execute permissions on a file. + * I don't know if we should look at just the euid or both euid and + * uid, but that should be easily changed. + */ +static int permission(struct m_inode * inode,int mask) +{ + int mode = inode->i_mode; + +/* special case: not even root can read/write a deleted file */ + if (inode->i_dev && !inode->i_nlinks) + return 0; + else if (current->euid==inode->i_uid) + mode >>= 6; + else if (current->egid==inode->i_gid) + mode >>= 3; + if (((mode & mask & 0007) == mask) || suser()) + return 1; + return 0; +} + +/* + * ok, we cannot use strncmp, as the name is not in our data space. + * Thus we'll have to use match. No big problem. Match also makes + * some sanity tests. + * + * NOTE! unlike strncmp, match returns 1 for success, 0 for failure. + */ +static int match(int len,const char * name,struct dir_entry * de) +{ + register int same __asm__("ax"); + + if (!de || !de->inode || len > NAME_LEN) + return 0; + if (len < NAME_LEN && de->name[len]) + return 0; + __asm__("cld\n\t" + "fs ; repe ; cmpsb\n\t" + "setz %%al" + :"=a" (same) + :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) + ); + return same; +} + +/* + * find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the cache buffer in which the entry was found, and the entry + * itself (as a parameter - res_dir). It does NOT read the inode of the + * entry - you'll have to do that yourself if you want to. + * + * This also takes care of the few special cases due to '..'-traversal + * over a pseudo-root and a mount point. + */ +static struct buffer_head * find_entry(struct m_inode ** dir, + const char * name, int namelen, struct dir_entry ** res_dir) +{ + int entries; + int block,i; + struct buffer_head * bh; + struct dir_entry * de; + struct super_block * sb; + +#ifdef NO_TRUNCATE + if (namelen > NAME_LEN) + return NULL; +#else + if (namelen > NAME_LEN) + namelen = NAME_LEN; +#endif + entries = (*dir)->i_size / (sizeof (struct dir_entry)); + *res_dir = NULL; + if (!namelen) + return NULL; +/* check for '..', as we might have to do some "magic" for it */ + if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') { +/* '..' in a pseudo-root results in a faked '.' (just change namelen) */ + if ((*dir) == current->root) + namelen=1; + else if ((*dir)->i_num == ROOT_INO) { +/* '..' over a mount-point results in 'dir' being exchanged for the mounted + directory-inode. NOTE! We set mounted, so that we can iput the new dir */ + sb=get_super((*dir)->i_dev); + if (sb->s_imount) { + iput(*dir); + (*dir)=sb->s_imount; + (*dir)->i_count++; + } + } + } + if (!(block = (*dir)->i_zone[0])) + return NULL; + if (!(bh = bread((*dir)->i_dev,block))) + return NULL; + i = 0; + de = (struct dir_entry *) bh->b_data; + while (i < entries) { + if ((char *)de >= BLOCK_SIZE+bh->b_data) { + brelse(bh); + bh = NULL; + if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) || + !(bh = bread((*dir)->i_dev,block))) { + i += DIR_ENTRIES_PER_BLOCK; + continue; + } + de = (struct dir_entry *) bh->b_data; + } + if (match(namelen,name,de)) { + *res_dir = de; + return bh; + } + de++; + i++; + } + brelse(bh); + return NULL; +} + +/* + * add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as find_entry(). It returns NULL if it failed. + * + * NOTE!! The inode part of 'de' is left at 0 - which means you + * may not sleep between calling this and putting something into + * the entry, as someone else might have used it while you slept. + */ +static struct buffer_head * add_entry(struct m_inode * dir, + const char * name, int namelen, struct dir_entry ** res_dir) +{ + int block,i; + struct buffer_head * bh; + struct dir_entry * de; + + *res_dir = NULL; +#ifdef NO_TRUNCATE + if (namelen > NAME_LEN) + return NULL; +#else + if (namelen > NAME_LEN) + namelen = NAME_LEN; +#endif + if (!namelen) + return NULL; + if (!(block = dir->i_zone[0])) + return NULL; + if (!(bh = bread(dir->i_dev,block))) + return NULL; + i = 0; + de = (struct dir_entry *) bh->b_data; + while (1) { + if ((char *)de >= BLOCK_SIZE+bh->b_data) { + brelse(bh); + bh = NULL; + block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); + if (!block) + return NULL; + if (!(bh = bread(dir->i_dev,block))) { + i += DIR_ENTRIES_PER_BLOCK; + continue; + } + de = (struct dir_entry *) bh->b_data; + } + if (i*sizeof(struct dir_entry) >= dir->i_size) { + de->inode=0; + dir->i_size = (i+1)*sizeof(struct dir_entry); + dir->i_dirt = 1; + dir->i_ctime = CURRENT_TIME; + } + if (!de->inode) { + dir->i_mtime = CURRENT_TIME; + for (i=0; i < NAME_LEN ; i++) + de->name[i]=(ib_dirt = 1; + *res_dir = de; + return bh; + } + de++; + i++; + } + brelse(bh); + return NULL; +} + +/* + * get_dir() + * + * Getdir traverses the pathname until it hits the topmost directory. + * It returns NULL on failure. + */ +static struct m_inode * get_dir(const char * pathname) +{ + char c; + const char * thisname; + struct m_inode * inode; + struct buffer_head * bh; + int namelen,inr,idev; + struct dir_entry * de; + + if (!current->root || !current->root->i_count) + panic("No root inode"); + if (!current->pwd || !current->pwd->i_count) + panic("No cwd inode"); + if ((c=get_fs_byte(pathname))=='/') { + inode = current->root; + pathname++; + } else if (c) + inode = current->pwd; + else + return NULL; /* empty name is bad */ + inode->i_count++; + while (1) { + thisname = pathname; + if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { + iput(inode); + return NULL; + } + for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) + /* nothing */ ; + if (!c) + return inode; + if (!(bh = find_entry(&inode,thisname,namelen,&de))) { + iput(inode); + return NULL; + } + inr = de->inode; + idev = inode->i_dev; + brelse(bh); + iput(inode); + if (!(inode = iget(idev,inr))) + return NULL; + } +} + +/* + * dir_namei() + * + * dir_namei() returns the inode of the directory of the + * specified name, and the name within that directory. + */ +static struct m_inode * dir_namei(const char * pathname, + int * namelen, const char ** name) +{ + char c; + const char * basename; + struct m_inode * dir; + + if (!(dir = get_dir(pathname))) + return NULL; + basename = pathname; + while (c=get_fs_byte(pathname++)) + if (c=='/') + basename=pathname; + *namelen = pathname-basename-1; + *name = basename; + return dir; +} + +/* + * namei() + * + * is used by most simple commands to get the inode of a specified name. + * Open, link etc use their own routines, but this is enough for things + * like 'chmod' etc. + */ +struct m_inode * namei(const char * pathname) +{ + const char * basename; + int inr,dev,namelen; + struct m_inode * dir; + struct buffer_head * bh; + struct dir_entry * de; + + if (!(dir = dir_namei(pathname,&namelen,&basename))) + return NULL; + if (!namelen) /* special case: '/usr/' etc */ + return dir; + bh = find_entry(&dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return NULL; + } + inr = de->inode; + dev = dir->i_dev; + brelse(bh); + iput(dir); + dir=iget(dev,inr); + if (dir) { + dir->i_atime=CURRENT_TIME; + dir->i_dirt=1; + } + return dir; +} + +/* + * open_namei() + * + * namei for open - this is in fact almost the whole open-routine. + */ +int open_namei(const char * pathname, int flag, int mode, + struct m_inode ** res_inode) +{ + const char * basename; + int inr,dev,namelen; + struct m_inode * dir, *inode; + struct buffer_head * bh; + struct dir_entry * de; + + if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) + flag |= O_WRONLY; + mode &= 0777 & ~current->umask; + mode |= I_REGULAR; + if (!(dir = dir_namei(pathname,&namelen,&basename))) + return -ENOENT; + if (!namelen) { /* special case: '/usr/' etc */ + if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { + *res_inode=dir; + return 0; + } + iput(dir); + return -EISDIR; + } + bh = find_entry(&dir,basename,namelen,&de); + if (!bh) { + if (!(flag & O_CREAT)) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EACCES; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_uid = current->euid; + inode->i_mode = mode; + inode->i_dirt = 1; + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + inode->i_nlinks--; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + *res_inode = inode; + return 0; + } + inr = de->inode; + dev = dir->i_dev; + brelse(bh); + iput(dir); + if (flag & O_EXCL) + return -EEXIST; + if (!(inode=iget(dev,inr))) + return -EACCES; + if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || + !permission(inode,ACC_MODE(flag))) { + iput(inode); + return -EPERM; + } + inode->i_atime = CURRENT_TIME; + if (flag & O_TRUNC) + truncate(inode); + *res_inode = inode; + return 0; +} + +int sys_mknod(const char * filename, int mode, int dev) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!suser()) + return -EPERM; + if (!(dir = dir_namei(filename,&namelen,&basename))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_mode = mode; + if (S_ISBLK(mode) || S_ISCHR(mode)) + inode->i_zone[0] = dev; + inode->i_mtime = inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + inode->i_nlinks=0; + iput(inode); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + iput(dir); + iput(inode); + brelse(bh); + return 0; +} + +int sys_mkdir(const char * pathname, int mode) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh, *dir_block; + struct dir_entry * de; + + if (!suser()) + return -EPERM; + if (!(dir = dir_namei(pathname,&namelen,&basename))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_size = 32; + inode->i_dirt = 1; + inode->i_mtime = inode->i_atime = CURRENT_TIME; + if (!(inode->i_zone[0]=new_block(inode->i_dev))) { + iput(dir); + inode->i_nlinks--; + iput(inode); + return -ENOSPC; + } + inode->i_dirt = 1; + if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) { + iput(dir); + free_block(inode->i_dev,inode->i_zone[0]); + inode->i_nlinks--; + iput(inode); + return -ERROR; + } + de = (struct dir_entry *) dir_block->b_data; + de->inode=inode->i_num; + strcpy(de->name,"."); + de++; + de->inode = dir->i_num; + strcpy(de->name,".."); + inode->i_nlinks = 2; + dir_block->b_dirt = 1; + brelse(dir_block); + inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); + inode->i_dirt = 1; + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + free_block(inode->i_dev,inode->i_zone[0]); + inode->i_nlinks=0; + iput(inode); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + dir->i_nlinks++; + dir->i_dirt = 1; + iput(dir); + iput(inode); + brelse(bh); + return 0; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +static int empty_dir(struct m_inode * inode) +{ + int nr,block; + int len; + struct buffer_head * bh; + struct dir_entry * de; + + len = inode->i_size / sizeof (struct dir_entry); + if (len<2 || !inode->i_zone[0] || + !(bh=bread(inode->i_dev,inode->i_zone[0]))) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } + de = (struct dir_entry *) bh->b_data; + if (de[0].inode != inode->i_num || !de[1].inode || + strcmp(".",de[0].name) || strcmp("..",de[1].name)) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } + nr = 2; + de += 2; + while (nr= (void *) (bh->b_data+BLOCK_SIZE)) { + brelse(bh); + block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK); + if (!block) { + nr += DIR_ENTRIES_PER_BLOCK; + continue; + } + if (!(bh=bread(inode->i_dev,block))) + return 0; + de = (struct dir_entry *) bh->b_data; + } + if (de->inode) { + brelse(bh); + return 0; + } + de++; + nr++; + } + brelse(bh); + return 1; +} + +int sys_rmdir(const char * name) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!suser()) + return -EPERM; + if (!(dir = dir_namei(name,&namelen,&basename))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return -ENOENT; + } + if (!(inode = iget(dir->i_dev, de->inode))) { + iput(dir); + brelse(bh); + return -EPERM; + } + if ((dir->i_mode & S_ISVTX) && current->euid && + inode->i_uid != current->euid) { + iput(dir); + iput(inode); + brelse(bh); + return -EPERM; + } + if (inode->i_dev != dir->i_dev || inode->i_count>1) { + iput(dir); + iput(inode); + brelse(bh); + return -EPERM; + } + if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ + iput(inode); + iput(dir); + brelse(bh); + return -EPERM; + } + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + iput(dir); + brelse(bh); + return -ENOTDIR; + } + if (!empty_dir(inode)) { + iput(inode); + iput(dir); + brelse(bh); + return -ENOTEMPTY; + } + if (inode->i_nlinks != 2) + printk("empty directory has nlink!=2 (%d)",inode->i_nlinks); + de->inode = 0; + bh->b_dirt = 1; + brelse(bh); + inode->i_nlinks=0; + inode->i_dirt=1; + dir->i_nlinks--; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt=1; + iput(dir); + iput(inode); + return 0; +} + +int sys_unlink(const char * name) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!(dir = dir_namei(name,&namelen,&basename))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return -ENOENT; + } + if (!(inode = iget(dir->i_dev, de->inode))) { + iput(dir); + brelse(bh); + return -ENOENT; + } + if ((dir->i_mode & S_ISVTX) && !suser() && + current->euid != inode->i_uid && + current->euid != dir->i_uid) { + iput(dir); + iput(inode); + brelse(bh); + return -EPERM; + } + if (S_ISDIR(inode->i_mode)) { + iput(inode); + iput(dir); + brelse(bh); + return -EPERM; + } + if (!inode->i_nlinks) { + printk("Deleting nonexistent file (%04x:%d), %d\n", + inode->i_dev,inode->i_num,inode->i_nlinks); + inode->i_nlinks=1; + } + de->inode = 0; + bh->b_dirt = 1; + brelse(bh); + inode->i_nlinks--; + inode->i_dirt = 1; + inode->i_ctime = CURRENT_TIME; + iput(inode); + iput(dir); + return 0; +} + +int sys_symlink() +{ + return -ENOSYS; +} + +int sys_link(const char * oldname, const char * newname) +{ + struct dir_entry * de; + struct m_inode * oldinode, * dir; + struct buffer_head * bh; + const char * basename; + int namelen; + + oldinode=namei(oldname); + if (!oldinode) + return -ENOENT; + if (S_ISDIR(oldinode->i_mode)) { + iput(oldinode); + return -EPERM; + } + dir = dir_namei(newname,&namelen,&basename); + if (!dir) { + iput(oldinode); + return -EACCES; + } + if (!namelen) { + iput(oldinode); + iput(dir); + return -EPERM; + } + if (dir->i_dev != oldinode->i_dev) { + iput(dir); + iput(oldinode); + return -EXDEV; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + iput(oldinode); + return -EACCES; + } + bh = find_entry(&dir,basename,namelen,&de); + if (bh) { + brelse(bh); + iput(dir); + iput(oldinode); + return -EEXIST; + } + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + iput(oldinode); + return -ENOSPC; + } + de->inode = oldinode->i_num; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + oldinode->i_nlinks++; + oldinode->i_ctime = CURRENT_TIME; + oldinode->i_dirt = 1; + iput(oldinode); + return 0; +} diff --git a/4/linux/fs/open.c b/4/linux/fs/open.c new file mode 100644 index 0000000..3695ff1 --- /dev/null +++ b/4/linux/fs/open.c @@ -0,0 +1,208 @@ +/* + * linux/fs/open.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +int sys_ustat(int dev, struct ustat * ubuf) +{ + return -ENOSYS; +} + +int sys_utime(char * filename, struct utimbuf * times) +{ + struct m_inode * inode; + long actime,modtime; + + if (!(inode=namei(filename))) + return -ENOENT; + if (times) { + actime = get_fs_long((unsigned long *) ×->actime); + modtime = get_fs_long((unsigned long *) ×->modtime); + } else + actime = modtime = CURRENT_TIME; + inode->i_atime = actime; + inode->i_mtime = modtime; + inode->i_dirt = 1; + iput(inode); + return 0; +} + +/* + * XXX should we use the real or effective uid? BSD uses the real uid, + * so as to make this call useful to setuid programs. + */ +int sys_access(const char * filename,int mode) +{ + struct m_inode * inode; + int res, i_mode; + + mode &= 0007; + if (!(inode=namei(filename))) + return -EACCES; + i_mode = res = inode->i_mode & 0777; + iput(inode); + if (current->uid == inode->i_uid) + res >>= 6; + else if (current->gid == inode->i_gid) + res >>= 6; + if ((res & 0007 & mode) == mode) + return 0; + /* + * XXX we are doing this test last because we really should be + * swapping the effective with the real user id (temporarily), + * and then calling suser() routine. If we do call the + * suser() routine, it needs to be called last. + */ + if ((!current->uid) && + (!(mode & 1) || (i_mode & 0111))) + return 0; + return -EACCES; +} + +int sys_chdir(const char * filename) +{ + struct m_inode * inode; + + if (!(inode = namei(filename))) + return -ENOENT; + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + return -ENOTDIR; + } + iput(current->pwd); + current->pwd = inode; + return (0); +} + +int sys_chroot(const char * filename) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + return -ENOTDIR; + } + iput(current->root); + current->root = inode; + return (0); +} + +int sys_chmod(const char * filename,int mode) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if ((current->euid != inode->i_uid) && !suser()) { + iput(inode); + return -EACCES; + } + inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); + inode->i_dirt = 1; + iput(inode); + return 0; +} + +int sys_chown(const char * filename,int uid,int gid) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if (!suser()) { + iput(inode); + return -EACCES; + } + inode->i_uid=uid; + inode->i_gid=gid; + inode->i_dirt=1; + iput(inode); + return 0; +} + +int sys_open(const char * filename,int flag,int mode) +{ + struct m_inode * inode; + struct file * f; + int i,fd; + + mode &= 0777 & ~current->umask; + for(fd=0 ; fdfilp[fd]) + break; + if (fd>=NR_OPEN) + return -EINVAL; + current->close_on_exec &= ~(1<f_count) break; + if (i>=NR_FILE) + return -EINVAL; + (current->filp[fd]=f)->f_count++; + if ((i=open_namei(filename,flag,mode,&inode))<0) { + current->filp[fd]=NULL; + f->f_count=0; + return i; + } +/* ttys are somewhat special (ttyxx major==4, tty major==5) */ + if (S_ISCHR(inode->i_mode)) + if (MAJOR(inode->i_zone[0])==4) { + if (current->leader && current->tty<0) { + current->tty = MINOR(inode->i_zone[0]); + tty_table[current->tty].pgrp = current->pgrp; + } + } else if (MAJOR(inode->i_zone[0])==5) + if (current->tty<0) { + iput(inode); + current->filp[fd]=NULL; + f->f_count=0; + return -EPERM; + } +/* Likewise with block-devices: check for floppy_change */ + if (S_ISBLK(inode->i_mode)) + check_disk_change(inode->i_zone[0]); + f->f_mode = inode->i_mode; + f->f_flags = flag; + f->f_count = 1; + f->f_inode = inode; + f->f_pos = 0; + return (fd); +} + +int sys_creat(const char * pathname, int mode) +{ + return sys_open(pathname, O_CREAT | O_TRUNC, mode); +} + +int sys_close(unsigned int fd) +{ + struct file * filp; + + if (fd >= NR_OPEN) + return -EINVAL; + current->close_on_exec &= ~(1<filp[fd])) + return -EINVAL; + current->filp[fd] = NULL; + if (filp->f_count == 0) + panic("Close: file count is 0"); + if (--filp->f_count) + return (0); + iput(filp->f_inode); + return (0); +} diff --git a/4/linux/fs/pipe.c b/4/linux/fs/pipe.c new file mode 100644 index 0000000..dfc4480 --- /dev/null +++ b/4/linux/fs/pipe.c @@ -0,0 +1,111 @@ +/* + * linux/fs/pipe.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +#include +#include /* for get_free_page */ +#include + +int read_pipe(struct m_inode * inode, char * buf, int count) +{ + int chars, size, read = 0; + + while (count>0) { + while (!(size=PIPE_SIZE(*inode))) { + wake_up(&inode->i_wait); + if (inode->i_count != 2) /* are there any writers? */ + return read; + sleep_on(&inode->i_wait); + } + chars = PAGE_SIZE-PIPE_TAIL(*inode); + if (chars > count) + chars = count; + if (chars > size) + chars = size; + count -= chars; + read += chars; + size = PIPE_TAIL(*inode); + PIPE_TAIL(*inode) += chars; + PIPE_TAIL(*inode) &= (PAGE_SIZE-1); + while (chars-->0) + put_fs_byte(((char *)inode->i_size)[size++],buf++); + } + wake_up(&inode->i_wait); + return read; +} + +int write_pipe(struct m_inode * inode, char * buf, int count) +{ + int chars, size, written = 0; + + while (count>0) { + while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) { + wake_up(&inode->i_wait); + if (inode->i_count != 2) { /* no readers */ + current->signal |= (1<<(SIGPIPE-1)); + return written?written:-1; + } + sleep_on(&inode->i_wait); + } + chars = PAGE_SIZE-PIPE_HEAD(*inode); + if (chars > count) + chars = count; + if (chars > size) + chars = size; + count -= chars; + written += chars; + size = PIPE_HEAD(*inode); + PIPE_HEAD(*inode) += chars; + PIPE_HEAD(*inode) &= (PAGE_SIZE-1); + while (chars-->0) + ((char *)inode->i_size)[size++]=get_fs_byte(buf++); + } + wake_up(&inode->i_wait); + return written; +} + +int sys_pipe(unsigned long * fildes) +{ + struct m_inode * inode; + struct file * f[2]; + int fd[2]; + int i,j; + + j=0; + for(i=0;j<2 && if_count++; + if (j==1) + f[0]->f_count=0; + if (j<2) + return -1; + j=0; + for(i=0;j<2 && ifilp[i]) { + current->filp[ fd[j]=i ] = f[j]; + j++; + } + if (j==1) + current->filp[fd[0]]=NULL; + if (j<2) { + f[0]->f_count=f[1]->f_count=0; + return -1; + } + if (!(inode=get_pipe_inode())) { + current->filp[fd[0]] = + current->filp[fd[1]] = NULL; + f[0]->f_count = f[1]->f_count = 0; + return -1; + } + f[0]->f_inode = f[1]->f_inode = inode; + f[0]->f_pos = f[1]->f_pos = 0; + f[0]->f_mode = 1; /* read */ + f[1]->f_mode = 2; /* write */ + put_fs_long(fd[0],0+fildes); + put_fs_long(fd[1],1+fildes); + return 0; +} diff --git a/4/linux/fs/read_write.c b/4/linux/fs/read_write.c new file mode 100644 index 0000000..341274a --- /dev/null +++ b/4/linux/fs/read_write.c @@ -0,0 +1,103 @@ +/* + * linux/fs/read_write.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include + +#include +#include +#include + +extern int rw_char(int rw,int dev, char * buf, int count, off_t * pos); +extern int read_pipe(struct m_inode * inode, char * buf, int count); +extern int write_pipe(struct m_inode * inode, char * buf, int count); +extern int block_read(int dev, off_t * pos, char * buf, int count); +extern int block_write(int dev, off_t * pos, char * buf, int count); +extern int file_read(struct m_inode * inode, struct file * filp, + char * buf, int count); +extern int file_write(struct m_inode * inode, struct file * filp, + char * buf, int count); + +int sys_lseek(unsigned int fd,off_t offset, int origin) +{ + struct file * file; + int tmp; + + if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode) + || !IS_SEEKABLE(MAJOR(file->f_inode->i_dev))) + return -EBADF; + if (file->f_inode->i_pipe) + return -ESPIPE; + switch (origin) { + case 0: + if (offset<0) return -EINVAL; + file->f_pos=offset; + break; + case 1: + if (file->f_pos+offset<0) return -EINVAL; + file->f_pos += offset; + break; + case 2: + if ((tmp=file->f_inode->i_size+offset) < 0) + return -EINVAL; + file->f_pos = tmp; + break; + default: + return -EINVAL; + } + return file->f_pos; +} + +int sys_read(unsigned int fd,char * buf,int count) +{ + struct file * file; + struct m_inode * inode; + + if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd])) + return -EINVAL; + if (!count) + return 0; + verify_area(buf,count); + inode = file->f_inode; + if (inode->i_pipe) + return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO; + if (S_ISCHR(inode->i_mode)) + return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos); + if (S_ISBLK(inode->i_mode)) + return block_read(inode->i_zone[0],&file->f_pos,buf,count); + if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) { + if (count+file->f_pos > inode->i_size) + count = inode->i_size - file->f_pos; + if (count<=0) + return 0; + return file_read(inode,file,buf,count); + } + printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode); + return -EINVAL; +} + +int sys_write(unsigned int fd,char * buf,int count) +{ + struct file * file; + struct m_inode * inode; + + if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd])) + return -EINVAL; + if (!count) + return 0; + inode=file->f_inode; + if (inode->i_pipe) + return (file->f_mode&2)?write_pipe(inode,buf,count):-EIO; + if (S_ISCHR(inode->i_mode)) + return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos); + if (S_ISBLK(inode->i_mode)) + return block_write(inode->i_zone[0],&file->f_pos,buf,count); + if (S_ISREG(inode->i_mode)) + return file_write(inode,file,buf,count); + printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode); + return -EINVAL; +} diff --git a/4/linux/fs/select.c b/4/linux/fs/select.c new file mode 100644 index 0000000..cef8b43 --- /dev/null +++ b/4/linux/fs/select.c @@ -0,0 +1,10 @@ +/* nothing , only the stub */ +/* gohigh */ + +#include + +int sys_select() +{ + return -ENOSYS; +} + diff --git a/4/linux/fs/stat.c b/4/linux/fs/stat.c new file mode 100644 index 0000000..ef09856 --- /dev/null +++ b/4/linux/fs/stat.c @@ -0,0 +1,66 @@ +/* + * linux/fs/stat.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include +#include + +static void cp_stat(struct m_inode * inode, struct stat * statbuf) +{ + struct stat tmp; + int i; + + verify_area(statbuf,sizeof (* statbuf)); + tmp.st_dev = inode->i_dev; + tmp.st_ino = inode->i_num; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlinks; + tmp.st_uid = inode->i_uid; + tmp.st_gid = inode->i_gid; + tmp.st_rdev = inode->i_zone[0]; + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + for (i=0 ; i= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode)) + return -EBADF; + cp_stat(inode,statbuf); + return 0; +} + +int sys_readlink() +{ + return -ENOSYS; +} diff --git a/4/linux/fs/super.c b/4/linux/fs/super.c new file mode 100644 index 0000000..6a4ccb1 --- /dev/null +++ b/4/linux/fs/super.c @@ -0,0 +1,282 @@ +/* + * linux/fs/super.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * super.c contains code to handle the super-block tables. + */ +#include +#include +#include +#include + +#include +#include + +int sync_dev(int dev); +void wait_for_keypress(void); + +/* set_bit uses setb, as gas doesn't recognize setc */ +#define set_bit(bitnr,addr) ({ \ +register int __res __asm__("ax"); \ +__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \ +__res; }) + +struct super_block super_block[NR_SUPER]; +/* this is initialized in init/main.c */ +int ROOT_DEV = 0; + +static void lock_super(struct super_block * sb) +{ + cli(); + while (sb->s_lock) + sleep_on(&(sb->s_wait)); + sb->s_lock = 1; + sti(); +} + +static void free_super(struct super_block * sb) +{ + cli(); + sb->s_lock = 0; + wake_up(&(sb->s_wait)); + sti(); +} + +static void wait_on_super(struct super_block * sb) +{ + cli(); + while (sb->s_lock) + sleep_on(&(sb->s_wait)); + sti(); +} + +struct super_block * get_super(int dev) +{ + struct super_block * s; + + if (!dev) + return NULL; + s = 0+super_block; + while (s < NR_SUPER+super_block) + if (s->s_dev == dev) { + wait_on_super(s); + if (s->s_dev == dev) + return s; + s = 0+super_block; + } else + s++; + return NULL; +} + +void put_super(int dev) +{ + struct super_block * sb; +/* struct m_inode * inode; */ + int i; + + if (dev == ROOT_DEV) { + printk("root diskette changed: prepare for armageddon\n\r"); + return; + } + if (!(sb = get_super(dev))) + return; + if (sb->s_imount) { + printk("Mounted disk changed - tssk, tssk\n\r"); + return; + } + lock_super(sb); + sb->s_dev = 0; + for(i=0;is_imap[i]); + for(i=0;is_zmap[i]); + free_super(sb); + return; +} + +static struct super_block * read_super(int dev) +{ + struct super_block * s; + struct buffer_head * bh; + int i,block; + + if (!dev) + return NULL; + check_disk_change(dev); + if (s = get_super(dev)) + return s; + for (s = 0+super_block ;; s++) { + if (s >= NR_SUPER+super_block) + return NULL; + if (!s->s_dev) + break; + } + s->s_dev = dev; + s->s_isup = NULL; + s->s_imount = NULL; + s->s_time = 0; + s->s_rd_only = 0; + s->s_dirt = 0; + lock_super(s); + if (!(bh = bread(dev,1))) { + s->s_dev=0; + free_super(s); + return NULL; + } + __asm__ volatile ("cld"); /* by wyj */ + *((struct d_super_block *) s) = + *((struct d_super_block *) bh->b_data); + brelse(bh); + if (s->s_magic != SUPER_MAGIC) { + s->s_dev = 0; + free_super(s); + return NULL; + } + for (i=0;is_imap[i] = NULL; + for (i=0;is_zmap[i] = NULL; + block=2; + for (i=0 ; i < s->s_imap_blocks ; i++) + if (s->s_imap[i]=bread(dev,block)) + block++; + else + break; + for (i=0 ; i < s->s_zmap_blocks ; i++) + if (s->s_zmap[i]=bread(dev,block)) + block++; + else + break; + if (block != 2+s->s_imap_blocks+s->s_zmap_blocks) { + for(i=0;is_imap[i]); + for(i=0;is_zmap[i]); + s->s_dev=0; + free_super(s); + return NULL; + } + s->s_imap[0]->b_data[0] |= 1; + s->s_zmap[0]->b_data[0] |= 1; + free_super(s); + return s; +} + +int sys_umount(char * dev_name) +{ + struct m_inode * inode; + struct super_block * sb; + int dev; + + if (!(inode=namei(dev_name))) + return -ENOENT; + dev = inode->i_zone[0]; + if (!S_ISBLK(inode->i_mode)) { + iput(inode); + return -ENOTBLK; + } + iput(inode); + if (dev==ROOT_DEV) + return -EBUSY; + if (!(sb=get_super(dev)) || !(sb->s_imount)) + return -ENOENT; + if (!sb->s_imount->i_mount) + printk("Mounted inode has i_mount=0\n"); + for (inode=inode_table+0 ; inodei_dev==dev && inode->i_count) + return -EBUSY; + sb->s_imount->i_mount=0; + iput(sb->s_imount); + sb->s_imount = NULL; + iput(sb->s_isup); + sb->s_isup = NULL; + put_super(dev); + sync_dev(dev); + return 0; +} + +int sys_mount(char * dev_name, char * dir_name, int rw_flag) +{ + struct m_inode * dev_i, * dir_i; + struct super_block * sb; + int dev; + + if (!(dev_i=namei(dev_name))) + return -ENOENT; + dev = dev_i->i_zone[0]; + if (!S_ISBLK(dev_i->i_mode)) { + iput(dev_i); + return -EPERM; + } + iput(dev_i); + if (!(dir_i=namei(dir_name))) + return -ENOENT; + if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) { + iput(dir_i); + return -EBUSY; + } + if (!S_ISDIR(dir_i->i_mode)) { + iput(dir_i); + return -EPERM; + } + if (!(sb=read_super(dev))) { + iput(dir_i); + return -EBUSY; + } + if (sb->s_imount) { + iput(dir_i); + return -EBUSY; + } + if (dir_i->i_mount) { + iput(dir_i); + return -EPERM; + } + sb->s_imount=dir_i; + dir_i->i_mount=1; + dir_i->i_dirt=1; /* NOTE! we don't iput(dir_i) */ + return 0; /* we do that in umount */ +} + +void mount_root(void) +{ + int i,free; + struct super_block * p; + struct m_inode * mi; + + if (32 != sizeof (struct d_inode)) + panic("bad i-node size"); + for(i=0;is_dev = 0; + p->s_lock = 0; + p->s_wait = NULL; + } + if (!(p=read_super(ROOT_DEV))) + panic("Unable to mount root"); + if (!(mi=iget(ROOT_DEV,ROOT_INO))) + panic("Unable to read root i-node"); + mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ + p->s_isup = p->s_imount = mi; + current->pwd = mi; + current->root = mi; + free=0; + i=p->s_nzones; + while (-- i >= 0) + if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data)) + free++; + printk("%d/%d free blocks\n\r",free,p->s_nzones); + free=0; + i=p->s_ninodes+1; + while (-- i >= 0) + if (!set_bit(i&8191,p->s_imap[i>>13]->b_data)) + free++; + printk("%d/%d free inodes\n\r",free,p->s_ninodes); +} diff --git a/4/linux/fs/truncate.c b/4/linux/fs/truncate.c new file mode 100644 index 0000000..36f3ea2 --- /dev/null +++ b/4/linux/fs/truncate.c @@ -0,0 +1,65 @@ +/* + * linux/fs/truncate.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +#include + +static void free_ind(int dev,int block) +{ + struct buffer_head * bh; + unsigned short * p; + int i; + + if (!block) + return; + if (bh=bread(dev,block)) { + p = (unsigned short *) bh->b_data; + for (i=0;i<512;i++,p++) + if (*p) + free_block(dev,*p); + brelse(bh); + } + free_block(dev,block); +} + +static void free_dind(int dev,int block) +{ + struct buffer_head * bh; + unsigned short * p; + int i; + + if (!block) + return; + if (bh=bread(dev,block)) { + p = (unsigned short *) bh->b_data; + for (i=0;i<512;i++,p++) + if (*p) + free_ind(dev,*p); + brelse(bh); + } + free_block(dev,block); +} + +void truncate(struct m_inode * inode) +{ + int i; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) + return; + for (i=0;i<7;i++) + if (inode->i_zone[i]) { + free_block(inode->i_dev,inode->i_zone[i]); + inode->i_zone[i]=0; + } + free_ind(inode->i_dev,inode->i_zone[7]); + free_dind(inode->i_dev,inode->i_zone[8]); + inode->i_zone[7] = inode->i_zone[8] = 0; + inode->i_size = 0; + inode->i_dirt = 1; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; +} + diff --git a/4/linux/include/.DS_Store b/4/linux/include/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b7d4bb16244f79c20edeec49d891fe89f30ff307 GIT binary patch literal 6148 zcmeHKJ5Iwu5S<|@f@mVqQ0^5{(XJwMf?NP8mY~3Lq>zHn9jD|9oPjqVKG7;EDd3GX z`@A#r*7hqr9ud*?pdpm>JCnV<$o}b} z;b&$TyRJVxZ$;kp{r2+un=_34iOlY?IqlqYFh>Qb02QDDRN%iTfSzr(xC-P-1*iZO z_)@^W4+U;m6Q@A`bYSrj0BFDQZn*YY0$3~ntcg<~A}|dqFsPa%h6Ww+l65t43Jkhv z4j-C#)|^n(?~e0}my6awu2g^u94j!3?b7=H9sI=ne@x x&U$Tu-@`u*wO)?JTQSgEF*eqUZ+_GjyGFhyPJvEG-04962$(K3D)1W$d;pa~BPRd= literal 0 HcmV?d00001 diff --git a/4/linux/include/a.out.h b/4/linux/include/a.out.h new file mode 100644 index 0000000..3e67974 --- /dev/null +++ b/4/linux/include/a.out.h @@ -0,0 +1,220 @@ +#ifndef _A_OUT_H +#define _A_OUT_H + +#define __GNU_EXEC_MACROS__ + +struct exec { + unsigned long a_magic; /* Use macros N_MAGIC, etc for access */ + unsigned a_text; /* length of text, in bytes */ + unsigned a_data; /* length of data, in bytes */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + unsigned a_entry; /* start address */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ +}; + +#ifndef N_MAGIC +#define N_MAGIC(exec) ((exec).a_magic) +#endif + +#ifndef OMAGIC +/* Code indicating object file or impure executable. */ +#define OMAGIC 0407 +/* Code indicating pure executable. */ +#define NMAGIC 0410 +/* Code indicating demand-paged executable. */ +#define ZMAGIC 0413 +#endif /* not OMAGIC */ + +#ifndef N_BADMAG +#define N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) +#endif + +#define _N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) + +#define _N_HDROFF(x) (SEGMENT_SIZE - sizeof (struct exec)) + +#ifndef N_TXTOFF +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : sizeof (struct exec)) +#endif + +#ifndef N_DATOFF +#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) +#endif + +#ifndef N_TRELOFF +#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data) +#endif + +#ifndef N_DRELOFF +#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize) +#endif + +#ifndef N_SYMOFF +#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize) +#endif + +#ifndef N_STROFF +#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms) +#endif + +/* Address of text segment in memory after it is loaded. */ +#ifndef N_TXTADDR +#define N_TXTADDR(x) 0 +#endif + +/* Address of data segment in memory after it is loaded. + Note that it is up to you to define SEGMENT_SIZE + on machines not listed here. */ +#if defined(vax) || defined(hp300) || defined(pyr) +#define SEGMENT_SIZE PAGE_SIZE +#endif +#ifdef hp300 +#define PAGE_SIZE 4096 +#endif +#ifdef sony +#define SEGMENT_SIZE 0x2000 +#endif /* Sony. */ +#ifdef is68k +#define SEGMENT_SIZE 0x20000 +#endif +#if defined(m68k) && defined(PORTAR) +#define PAGE_SIZE 0x400 +#define SEGMENT_SIZE PAGE_SIZE +#endif + +#define PAGE_SIZE 4096 +#define SEGMENT_SIZE 1024 + +#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1)) + +#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) + +#ifndef N_DATADDR +#define N_DATADDR(x) \ + (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \ + : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) +#endif + +/* Address of bss segment in memory after it is loaded. */ +#ifndef N_BSSADDR +#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data) +#endif + +#ifndef N_NLIST_DECLARED +struct nlist { + union { + char *n_name; + struct nlist *n_next; + long n_strx; + } n_un; + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; +#endif + +#ifndef N_UNDF +#define N_UNDF 0 +#endif +#ifndef N_ABS +#define N_ABS 2 +#endif +#ifndef N_TEXT +#define N_TEXT 4 +#endif +#ifndef N_DATA +#define N_DATA 6 +#endif +#ifndef N_BSS +#define N_BSS 8 +#endif +#ifndef N_COMM +#define N_COMM 18 +#endif +#ifndef N_FN +#define N_FN 15 +#endif + +#ifndef N_EXT +#define N_EXT 1 +#endif +#ifndef N_TYPE +#define N_TYPE 036 +#endif +#ifndef N_STAB +#define N_STAB 0340 +#endif + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#define N_INDR 0xa + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +/* These appear as input to LD, in a .o file. */ +#define N_SETA 0x14 /* Absolute set element symbol */ +#define N_SETT 0x16 /* Text set element symbol */ +#define N_SETD 0x18 /* Data set element symbol */ +#define N_SETB 0x1A /* Bss set element symbol */ + +/* This is output from LD. */ +#define N_SETV 0x1C /* Pointer to set vector in data area. */ + +#ifndef N_RELOCATION_INFO_DECLARED + +/* This structure describes a single relocation to be performed. + The text-relocation section of the file is a vector of these structures, + all of which apply to the text section. + Likewise, the data-relocation section applies to the data section. */ + +struct relocation_info +{ + /* Address (within segment) to be relocated. */ + int r_address; + /* The meaning of r_symbolnum depends on r_extern. */ + unsigned int r_symbolnum:24; + /* Nonzero means value is a pc-relative offset + and it should be relocated for changes in its own address + as well as for changes in the symbol or section specified. */ + unsigned int r_pcrel:1; + /* Length (as exponent of 2) of the field to be relocated. + Thus, a value of 2 indicates 1<<2 bytes. */ + unsigned int r_length:2; + /* 1 => relocate with value of symbol. + r_symbolnum is the index of the symbol + in file's the symbol table. + 0 => relocate with the address of a segment. + r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS + (the N_EXT bit may be set also, but signifies nothing). */ + unsigned int r_extern:1; + /* Four bits that aren't used, but when writing an object file + it is desirable to clear them. */ + unsigned int r_pad:4; +}; +#endif /* no N_RELOCATION_INFO_DECLARED. */ + + +#endif /* __A_OUT_GNU_H__ */ diff --git a/4/linux/include/asm/io.h b/4/linux/include/asm/io.h new file mode 100644 index 0000000..d5cc42a --- /dev/null +++ b/4/linux/include/asm/io.h @@ -0,0 +1,24 @@ +#define outb(value,port) \ +__asm__ ("outb %%al,%%dx"::"a" (value),"d" (port)) + + +#define inb(port) ({ \ +unsigned char _v; \ +__asm__ volatile ("inb %%dx,%%al":"=a" (_v):"d" (port)); \ +_v; \ +}) + +#define outb_p(value,port) \ +__asm__ ("outb %%al,%%dx\n" \ + "\tjmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:"::"a" (value),"d" (port)) + +#define inb_p(port) ({ \ +unsigned char _v; \ +__asm__ volatile ("inb %%dx,%%al\n" \ + "\tjmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:":"=a" (_v):"d" (port)); \ +_v; \ +}) diff --git a/4/linux/include/asm/memory.h b/4/linux/include/asm/memory.h new file mode 100644 index 0000000..51b69e7 --- /dev/null +++ b/4/linux/include/asm/memory.h @@ -0,0 +1,15 @@ +/* + * NOTE!!! memcpy(dest,src,n) assumes ds=es=normal data segment. This + * goes for all kernel functions (ds=es=kernel space, fs=local data, + * gs=null), as well as for all well-behaving user programs (ds=es= + * user data space). This is NOT a bug, as any user program that changes + * es deserves to die if it isn't careful. + */ +#define memcpy(dest,src,n) ({ \ +void * _res = dest; \ +__asm__ ("cld;rep;movsb" \ + ::"D" ((long)(_res)),"S" ((long)(src)),"c" ((long) (n)) \ + ); \ +_res; \ +}) + diff --git a/4/linux/include/asm/segment.h b/4/linux/include/asm/segment.h new file mode 100644 index 0000000..94dd102 --- /dev/null +++ b/4/linux/include/asm/segment.h @@ -0,0 +1,65 @@ +static inline unsigned char get_fs_byte(const char * addr) +{ + unsigned register char _v; + + __asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + +static inline unsigned short get_fs_word(const unsigned short *addr) +{ + unsigned short _v; + + __asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + +static inline unsigned long get_fs_long(const unsigned long *addr) +{ + unsigned long _v; + + __asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); \ + return _v; +} + +static inline void put_fs_byte(char val,char *addr) +{ +__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +static inline void put_fs_word(short val,short * addr) +{ +__asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +static inline void put_fs_long(unsigned long val,unsigned long * addr) +{ +__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +/* + * Someone who knows GNU asm better than I should double check the followig. + * It seems to work, but I don't know if I'm doing something subtly wrong. + * --- TYT, 11/24/91 + * [ nothing wrong here, Linus ] + */ + +static inline unsigned long get_fs() +{ + unsigned short _v; + __asm__("mov %%fs,%%ax":"=a" (_v):); + return _v; +} + +static inline unsigned long get_ds() +{ + unsigned short _v; + __asm__("mov %%ds,%%ax":"=a" (_v):); + return _v; +} + +static inline void set_fs(unsigned long val) +{ + __asm__("mov %0,%%fs"::"a" ((unsigned short) val)); +} + diff --git a/4/linux/include/asm/system.h b/4/linux/include/asm/system.h new file mode 100644 index 0000000..0b5a21d --- /dev/null +++ b/4/linux/include/asm/system.h @@ -0,0 +1,66 @@ +#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") diff --git a/4/linux/include/assert.h b/4/linux/include/assert.h new file mode 100644 index 0000000..26b5d13 --- /dev/null +++ b/4/linux/include/assert.h @@ -0,0 +1,34 @@ +/* Allow this file to be included multiple times + with different settings of NDEBUG. */ +#undef assert +#undef __assert + +#ifdef NDEBUG +#define assert(ignore) ((void)0) +#else + +void __eprintf (); /* Defined in gnulib */ + +#ifdef __STDC__ + +#define assert(expression) \ + ((expression) ? 0 : (__assert (#expression, __FILE__, __LINE__), 0)) + +#define __assert(expression, file, line) \ + (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ + expression, line, file), \ + abort ()) + +#else /* no __STDC__; i.e. -traditional. */ + +#define assert(expression) \ + ((expression) ? 0 : __assert (expression, __FILE__, __LINE__)) + +#define __assert(expression, file, lineno) \ + (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n", \ + "expression", lineno, file), \ + abort ()) + +#endif /* no __STDC__; i.e. -traditional. */ + +#endif diff --git a/4/linux/include/const.h b/4/linux/include/const.h new file mode 100644 index 0000000..7828e61 --- /dev/null +++ b/4/linux/include/const.h @@ -0,0 +1,15 @@ +#ifndef _CONST_H +#define _CONST_H + +#define BUFFER_END 0x200000 + +#define I_TYPE 0170000 +#define I_DIRECTORY 0040000 +#define I_REGULAR 0100000 +#define I_BLOCK_SPECIAL 0060000 +#define I_CHAR_SPECIAL 0020000 +#define I_NAMED_PIPE 0010000 +#define I_SET_UID_BIT 0004000 +#define I_SET_GID_BIT 0002000 + +#endif diff --git a/4/linux/include/ctype.h b/4/linux/include/ctype.h new file mode 100644 index 0000000..7acf55d --- /dev/null +++ b/4/linux/include/ctype.h @@ -0,0 +1,34 @@ +#ifndef _CTYPE_H +#define _CTYPE_H + +#define _U 0x01 /* upper */ +#define _L 0x02 /* lower */ +#define _D 0x04 /* digit */ +#define _C 0x08 /* cntrl */ +#define _P 0x10 /* punct */ +#define _S 0x20 /* white space (space/lf/tab) */ +#define _X 0x40 /* hex digit */ +#define _SP 0x80 /* hard space (0x20) */ + +extern unsigned char _ctype[]; +extern char _ctmp; + +#define isalnum(c) ((_ctype+1)[c]&(_U|_L|_D)) +#define isalpha(c) ((_ctype+1)[c]&(_U|_L)) +#define iscntrl(c) ((_ctype+1)[c]&(_C)) +#define isdigit(c) ((_ctype+1)[c]&(_D)) +#define isgraph(c) ((_ctype+1)[c]&(_P|_U|_L|_D)) +#define islower(c) ((_ctype+1)[c]&(_L)) +#define isprint(c) ((_ctype+1)[c]&(_P|_U|_L|_D|_SP)) +#define ispunct(c) ((_ctype+1)[c]&(_P)) +#define isspace(c) ((_ctype+1)[c]&(_S)) +#define isupper(c) ((_ctype+1)[c]&(_U)) +#define isxdigit(c) ((_ctype+1)[c]&(_D|_X)) + +#define isascii(c) (((unsigned) c)<=0x7f) +#define toascii(c) (((unsigned) c)&0x7f) + +#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp-('A'-'a'):_ctmp) +#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp-('a'-'A'):_ctmp) + +#endif diff --git a/4/linux/include/errno.h b/4/linux/include/errno.h new file mode 100644 index 0000000..c282f69 --- /dev/null +++ b/4/linux/include/errno.h @@ -0,0 +1,60 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +/* + * ok, as I hadn't got any other source of information about + * possible error numbers, I was forced to use the same numbers + * as minix. + * Hopefully these are posix or something. I wouldn't know (and posix + * isn't telling me - they want $$$ for their f***ing standard). + * + * We don't use the _SIGN cludge of minix, so kernel returns must + * see to the sign by themselves. + * + * NOTE! Remember to change strerror() if you change this file! + */ + +extern int errno; + +#define ERROR 99 +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define ENOTBLK 15 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define ETXTBSY 26 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 35 +#define ENAMETOOLONG 36 +#define ENOLCK 37 +#define ENOSYS 38 +#define ENOTEMPTY 39 + +#endif diff --git a/4/linux/include/fcntl.h b/4/linux/include/fcntl.h new file mode 100644 index 0000000..0eadd32 --- /dev/null +++ b/4/linux/include/fcntl.h @@ -0,0 +1,56 @@ +#ifndef _FCNTL_H +#define _FCNTL_H + +/* I don't think this should be included like this, but it's easier */ +#include + +/* open/fcntl - NOCTTY, NDELAY isn't implemented yet */ +#define O_ACCMODE 00003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 00100 /* not fcntl */ +#define O_EXCL 00200 /* not fcntl */ +#define O_NOCTTY 00400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 /* not fcntl */ +#define O_NDELAY O_NONBLOCK + +/* Defines for fcntl-commands. Note that currently + * locking isn't supported, and other things aren't really + * tested. + */ +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get f_flags */ +#define F_SETFD 2 /* set f_flags */ +#define F_GETFL 3 /* more flags (cloexec) */ +#define F_SETFL 4 +#define F_GETLK 5 /* not implemented */ +#define F_SETLK 6 +#define F_SETLKW 7 + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* Ok, these are locking features, and aren't implemented at any + * level. POSIX wants them. + */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* Once again - not implemented, but ... */ +struct flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; + +extern int creat(const char * filename,mode_t mode); +extern int fcntl(int fildes,int cmd, ...); +extern int open(const char * filename, int flags, ...); + +#endif diff --git a/4/linux/include/linux/config.h b/4/linux/include/linux/config.h new file mode 100644 index 0000000..c979fb3 --- /dev/null +++ b/4/linux/include/linux/config.h @@ -0,0 +1,48 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +/* + * The root-device is no longer hard-coded. You can change the default + * root-device by changing the line ROOT_DEV = XXX in boot/bootsect.s + */ + +/* + * define your keyboard here - + * KBD_FINNISH for Finnish keyboards + * KBD_US for US-type + * KBD_GR for German keyboards + * KBD_FR for Frech keyboard + */ +#define KBD_US +/*#define KBD_GR */ +/*#define KBD_FR */ +/*#define KBD_FINNISH */ + +/* + * Normally, Linux can get the drive parameters from the BIOS at + * startup, but if this for some unfathomable reason fails, you'd + * be left stranded. For this case, you can define HD_TYPE, which + * contains all necessary info on your harddisk. + * + * The HD_TYPE macro should look like this: + * + * #define HD_TYPE { head, sect, cyl, wpcom, lzone, ctl} + * + * In case of two harddisks, the info should be sepatated by + * commas: + * + * #define HD_TYPE { h,s,c,wpcom,lz,ctl },{ h,s,c,wpcom,lz,ctl } + */ +/* + This is an example, two drives, first is type 2, second is type 3: + +#define HD_TYPE { 4,17,615,300,615,8 }, { 6,17,615,300,615,0 } + + NOTE: ctl is 0 for all drives with heads<=8, and ctl=8 for drives + with more than 8 heads. + + If you want the BIOS to tell what kind of drive you have, just + leave HD_TYPE undefined. This is the normal thing to do. +*/ + +#endif diff --git a/4/linux/include/linux/fdreg.h b/4/linux/include/linux/fdreg.h new file mode 100644 index 0000000..01355af --- /dev/null +++ b/4/linux/include/linux/fdreg.h @@ -0,0 +1,71 @@ +/* + * This file contains some defines for the floppy disk controller. + * Various sources. Mostly "IBM Microcomputers: A Programmers + * Handbook", Sanches and Canton. + */ +#ifndef _FDREG_H +#define _FDREG_H + +extern int ticks_to_floppy_on(unsigned int nr); +extern void floppy_on(unsigned int nr); +extern void floppy_off(unsigned int nr); +extern void floppy_select(unsigned int nr); +extern void floppy_deselect(unsigned int nr); + +/* Fd controller regs. S&C, about page 340 */ +#define FD_STATUS 0x3f4 +#define FD_DATA 0x3f5 +#define FD_DOR 0x3f2 /* Digital Output Register */ +#define FD_DIR 0x3f7 /* Digital Input Register (read) */ +#define FD_DCR 0x3f7 /* Diskette Control Register (write)*/ + +/* Bits of main status register */ +#define STATUS_BUSYMASK 0x0F /* drive busy mask */ +#define STATUS_BUSY 0x10 /* FDC busy */ +#define STATUS_DMA 0x20 /* 0- DMA mode */ +#define STATUS_DIR 0x40 /* 0- cpu->fdc */ +#define STATUS_READY 0x80 /* Data reg ready */ + +/* Bits of FD_ST0 */ +#define ST0_DS 0x03 /* drive select mask */ +#define ST0_HA 0x04 /* Head (Address) */ +#define ST0_NR 0x08 /* Not Ready */ +#define ST0_ECE 0x10 /* Equipment chech error */ +#define ST0_SE 0x20 /* Seek end */ +#define ST0_INTR 0xC0 /* Interrupt code mask */ + +/* Bits of FD_ST1 */ +#define ST1_MAM 0x01 /* Missing Address Mark */ +#define ST1_WP 0x02 /* Write Protect */ +#define ST1_ND 0x04 /* No Data - unreadable */ +#define ST1_OR 0x10 /* OverRun */ +#define ST1_CRC 0x20 /* CRC error in data or addr */ +#define ST1_EOC 0x80 /* End Of Cylinder */ + +/* Bits of FD_ST2 */ +#define ST2_MAM 0x01 /* Missing Addess Mark (again) */ +#define ST2_BC 0x02 /* Bad Cylinder */ +#define ST2_SNS 0x04 /* Scan Not Satisfied */ +#define ST2_SEH 0x08 /* Scan Equal Hit */ +#define ST2_WC 0x10 /* Wrong Cylinder */ +#define ST2_CRC 0x20 /* CRC error in data field */ +#define ST2_CM 0x40 /* Control Mark = deleted */ + +/* Bits of FD_ST3 */ +#define ST3_HA 0x04 /* Head (Address) */ +#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */ +#define ST3_WP 0x40 /* Write Protect */ + +/* Values for FD_COMMAND */ +#define FD_RECALIBRATE 0x07 /* move to track 0 */ +#define FD_SEEK 0x0F /* seek track */ +#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */ +#define FD_WRITE 0xC5 /* write with MT, MFM */ +#define FD_SENSEI 0x08 /* Sense Interrupt Status */ +#define FD_SPECIFY 0x03 /* specify HUT etc */ + +/* DMA commands */ +#define DMA_READ 0x46 +#define DMA_WRITE 0x4A + +#endif diff --git a/4/linux/include/linux/fs.h b/4/linux/include/linux/fs.h new file mode 100644 index 0000000..7a90b10 --- /dev/null +++ b/4/linux/include/linux/fs.h @@ -0,0 +1,202 @@ +/* + * This file has definitions for some important file table + * structures etc. + */ + +#ifndef _FS_H +#define _FS_H + +#include + +/* devices are as follows: (same as minix, so we can use the minix + * file system. These are major numbers.) + * + * 0 - unused (nodev) + * 1 - /dev/mem + * 2 - /dev/fd + * 3 - /dev/hd + * 4 - /dev/ttyx + * 5 - /dev/tty + * 6 - /dev/lp + * 7 - unnamed pipes + */ + +#define IS_SEEKABLE(x) ((x)>=1 && (x)<=3) + +#define READ 0 +#define WRITE 1 +#define READA 2 /* read-ahead - don't pause */ +#define WRITEA 3 /* "write-ahead" - silly, but somewhat useful */ + +void buffer_init(long buffer_end); + +#define MAJOR(a) (((unsigned)(a))>>8) +#define MINOR(a) ((a)&0xff) + +#define NAME_LEN 14 +#define ROOT_INO 1 + +#define I_MAP_SLOTS 8 +#define Z_MAP_SLOTS 8 +#define SUPER_MAGIC 0x137F + +#define NR_OPEN 20 +#define NR_INODE 32 +#define NR_FILE 64 +#define NR_SUPER 8 +#define NR_HASH 307 +#define NR_BUFFERS nr_buffers +#define BLOCK_SIZE 1024 +#define BLOCK_SIZE_BITS 10 +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode))) +#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry))) + +#define PIPE_HEAD(inode) ((inode).i_zone[0]) +#define PIPE_TAIL(inode) ((inode).i_zone[1]) +#define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1)) +#define PIPE_EMPTY(inode) (PIPE_HEAD(inode)==PIPE_TAIL(inode)) +#define PIPE_FULL(inode) (PIPE_SIZE(inode)==(PAGE_SIZE-1)) +#define INC_PIPE(head) \ +__asm__("incl %0\n\tandl $4095,%0"::"m" (head)) + +typedef char buffer_block[BLOCK_SIZE]; + +struct buffer_head { + char * b_data; /* pointer to data block (1024 bytes) */ + unsigned long b_blocknr; /* block number */ + unsigned short b_dev; /* device (0 = free) */ + unsigned char b_uptodate; + unsigned char b_dirt; /* 0-clean,1-dirty */ + unsigned char b_count; /* users using this block */ + unsigned char b_lock; /* 0 - ok, 1 -locked */ + struct task_struct * b_wait; + struct buffer_head * b_prev; + struct buffer_head * b_next; + struct buffer_head * b_prev_free; + struct buffer_head * b_next_free; +}; + +struct d_inode { + unsigned short i_mode; + unsigned short i_uid; + unsigned long i_size; + unsigned long i_time; + unsigned char i_gid; + unsigned char i_nlinks; + unsigned short i_zone[9]; +}; + +struct m_inode { + unsigned short i_mode; + unsigned short i_uid; + unsigned long i_size; + unsigned long i_mtime; + unsigned char i_gid; + unsigned char i_nlinks; + unsigned short i_zone[9]; +/* these are in memory also */ + struct task_struct * i_wait; + unsigned long i_atime; + unsigned long i_ctime; + unsigned short i_dev; + unsigned short i_num; + unsigned short i_count; + unsigned char i_lock; + unsigned char i_dirt; + unsigned char i_pipe; + unsigned char i_mount; + unsigned char i_seek; + unsigned char i_update; +}; + +struct file { + unsigned short f_mode; + unsigned short f_flags; + unsigned short f_count; + struct m_inode * f_inode; + off_t f_pos; +}; + +struct super_block { + unsigned short s_ninodes; + unsigned short s_nzones; + unsigned short s_imap_blocks; + unsigned short s_zmap_blocks; + unsigned short s_firstdatazone; + unsigned short s_log_zone_size; + unsigned long s_max_size; + unsigned short s_magic; +/* These are only in memory */ + struct buffer_head * s_imap[8]; + struct buffer_head * s_zmap[8]; + unsigned short s_dev; + struct m_inode * s_isup; + struct m_inode * s_imount; + unsigned long s_time; + struct task_struct * s_wait; + unsigned char s_lock; + unsigned char s_rd_only; + unsigned char s_dirt; +}; + +struct d_super_block { + unsigned short s_ninodes; + unsigned short s_nzones; + unsigned short s_imap_blocks; + unsigned short s_zmap_blocks; + unsigned short s_firstdatazone; + unsigned short s_log_zone_size; + unsigned long s_max_size; + unsigned short s_magic; +}; + +struct dir_entry { + unsigned short inode; + char name[NAME_LEN]; +}; + +extern struct m_inode inode_table[NR_INODE]; +extern struct file file_table[NR_FILE]; +extern struct super_block super_block[NR_SUPER]; +extern struct buffer_head * start_buffer; +extern int nr_buffers; + +extern void check_disk_change(int dev); +extern int floppy_change(unsigned int nr); +extern int ticks_to_floppy_on(unsigned int dev); +extern void floppy_on(unsigned int dev); +extern void floppy_off(unsigned int dev); +extern void truncate(struct m_inode * inode); +extern void sync_inodes(void); +extern void wait_on(struct m_inode * inode); +extern int bmap(struct m_inode * inode,int block); +extern int create_block(struct m_inode * inode,int block); +extern struct m_inode * namei(const char * pathname); +extern int open_namei(const char * pathname, int flag, int mode, + struct m_inode ** res_inode); +extern void iput(struct m_inode * inode); +extern struct m_inode * iget(int dev,int nr); +extern struct m_inode * get_empty_inode(void); +extern struct m_inode * get_pipe_inode(void); +extern struct buffer_head * get_hash_table(int dev, int block); +extern struct buffer_head * getblk(int dev, int block); +extern void ll_rw_block(int rw, struct buffer_head * bh); +extern void brelse(struct buffer_head * buf); +extern struct buffer_head * bread(int dev,int block); +extern void bread_page(unsigned long addr,int dev,int b[4]); +extern struct buffer_head * breada(int dev,int block,...); +extern int new_block(int dev); +extern void free_block(int dev, int block); +extern struct m_inode * new_inode(int dev); +extern void free_inode(struct m_inode * inode); +extern int sync_dev(int dev); +extern struct super_block * get_super(int dev); +extern int ROOT_DEV; + +extern void mount_root(void); + +#endif diff --git a/4/linux/include/linux/hdreg.h b/4/linux/include/linux/hdreg.h new file mode 100644 index 0000000..e6c593f --- /dev/null +++ b/4/linux/include/linux/hdreg.h @@ -0,0 +1,65 @@ +/* + * This file contains some defines for the AT-hd-controller. + * Various sources. Check out some definitions (see comments with + * a ques). + */ +#ifndef _HDREG_H +#define _HDREG_H + +/* Hd controller regs. Ref: IBM AT Bios-listing */ +#define HD_DATA 0x1f0 /* _CTL when writing */ +#define HD_ERROR 0x1f1 /* see err-bits */ +#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ +#define HD_SECTOR 0x1f3 /* starting sector */ +#define HD_LCYL 0x1f4 /* starting cylinder */ +#define HD_HCYL 0x1f5 /* high byte of starting cyl */ +#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ +#define HD_STATUS 0x1f7 /* see status-bits */ +#define HD_PRECOMP HD_ERROR /* same io address, read=error, write=precomp */ +#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ + +#define HD_CMD 0x3f6 + +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 /* Corrected error */ +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +/* Values for HD_COMMAND */ +#define WIN_RESTORE 0x10 +#define WIN_READ 0x20 +#define WIN_WRITE 0x30 +#define WIN_VERIFY 0x40 +#define WIN_FORMAT 0x50 +#define WIN_INIT 0x60 +#define WIN_SEEK 0x70 +#define WIN_DIAGNOSE 0x90 +#define WIN_SPECIFY 0x91 + +/* Bits for HD_ERROR */ +#define MARK_ERR 0x01 /* Bad address mark ? */ +#define TRK0_ERR 0x02 /* couldn't find track 0 */ +#define ABRT_ERR 0x04 /* ? */ +#define ID_ERR 0x10 /* ? */ +#define ECC_ERR 0x40 /* ? */ +#define BBD_ERR 0x80 /* ? */ + +struct partition { + unsigned char boot_ind; /* 0x80 - active (unused) */ + unsigned char head; /* ? */ + unsigned char sector; /* ? */ + unsigned char cyl; /* ? */ + unsigned char sys_ind; /* ? */ + unsigned char end_head; /* ? */ + unsigned char end_sector; /* ? */ + unsigned char end_cyl; /* ? */ + unsigned int start_sect; /* starting sector counting from 0 */ + unsigned int nr_sects; /* nr of sectors in partition */ +}; + +#endif diff --git a/4/linux/include/linux/head.h b/4/linux/include/linux/head.h new file mode 100644 index 0000000..db3dda2 --- /dev/null +++ b/4/linux/include/linux/head.h @@ -0,0 +1,20 @@ +#ifndef _HEAD_H +#define _HEAD_H + +typedef struct desc_struct { + unsigned long a,b; +} desc_table[256]; + +extern unsigned long pg_dir[1024]; +extern desc_table idt,gdt; + +#define GDT_NUL 0 +#define GDT_CODE 1 +#define GDT_DATA 2 +#define GDT_TMP 3 + +#define LDT_NUL 0 +#define LDT_CODE 1 +#define LDT_DATA 2 + +#endif diff --git a/4/linux/include/linux/kernel.h b/4/linux/include/linux/kernel.h new file mode 100644 index 0000000..cb40dd5 --- /dev/null +++ b/4/linux/include/linux/kernel.h @@ -0,0 +1,22 @@ +/* + * 'kernel.h' contains some often-used function prototypes etc + */ +void verify_area(void * addr,int count); +volatile void panic(const char * str); +int printf(const char * fmt, ...); +int printk(const char * fmt, ...); +int tty_write(unsigned ch,char * buf,int count); +void * malloc(unsigned int size); +void free_s(void * obj, int size); + +#define free(x) free_s((x), 0) + +/* + * This is defined as a macro, but at some point this might become a + * real subroutine that sets a flag if it returns true (to do + * BSD-style accounting where the process is flagged if it uses root + * privs). The implication of this is that you should do normal + * permissions checks first, and check suser() last. + */ +#define suser() (current->euid == 0) + diff --git a/4/linux/include/linux/mm.h b/4/linux/include/linux/mm.h new file mode 100644 index 0000000..5a160f3 --- /dev/null +++ b/4/linux/include/linux/mm.h @@ -0,0 +1,10 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SIZE 4096 + +extern unsigned long get_free_page(void); +extern unsigned long put_page(unsigned long page,unsigned long address); +extern void free_page(unsigned long addr); + +#endif diff --git a/4/linux/include/linux/sched.h b/4/linux/include/linux/sched.h new file mode 100644 index 0000000..921c060 --- /dev/null +++ b/4/linux/include/linux/sched.h @@ -0,0 +1,239 @@ +#ifndef _SCHED_H +#define _SCHED_H + +#define NR_TASKS 64 +#define HZ 100 + +#define FIRST_TASK task[0] +#define LAST_TASK task[NR_TASKS-1] + +#include +#include +#include +#include + +#if (NR_OPEN > 32) +#error "Currently the close-on-exec-flags are in one word, max 32 files/proc" +#endif + +#define TASK_RUNNING 0 +#define TASK_INTERRUPTIBLE 1 +#define TASK_UNINTERRUPTIBLE 2 +#define TASK_ZOMBIE 3 +#define TASK_STOPPED 4 + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +extern int copy_page_tables(unsigned long from, unsigned long to, long size); +extern int free_page_tables(unsigned long from, unsigned long size); +void do_no_page1(unsigned long address); +extern void sched_init(void); +extern void schedule(void); +extern void trap_init(void); +extern void panic(const char * str); +extern int tty_write(unsigned minor,char * buf,int count); + +typedef int (*fn_ptr)(); + +struct i387_struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ +}; + +struct tss_struct { + long back_link; /* 16 high bits zero */ + long esp0; + long ss0; /* 16 high bits zero */ + long esp1; + long ss1; /* 16 high bits zero */ + long esp2; + long ss2; /* 16 high bits zero */ + long cr3; + long eip; + long eflags; + long eax,ecx,edx,ebx; + long esp; + long ebp; + long esi; + long edi; + long es; /* 16 high bits zero */ + long cs; /* 16 high bits zero */ + long ss; /* 16 high bits zero */ + long ds; /* 16 high bits zero */ + long fs; /* 16 high bits zero */ + long gs; /* 16 high bits zero */ + long ldt; /* 16 high bits zero */ + long trace_bitmap; /* bits: trace 0, bitmap 16-31 */ + struct i387_struct i387; +}; + +struct task_struct { +/* these are hardcoded - don't touch */ + long state; /* -1 unrunnable, 0 runnable, >0 stopped */ + long counter; + long priority; + long signal; + struct sigaction sigaction[32]; + long blocked; /* bitmap of masked signals */ +/* various fields */ + int exit_code; + unsigned long start_code,end_code,end_data,brk,start_stack; + long pid,father,pgrp,session,leader; + unsigned short uid,euid,suid; + unsigned short gid,egid,sgid; + long alarm; + long utime,stime,cutime,cstime,start_time; + unsigned short used_math; +/* file system info */ + int tty; /* -1 if no tty, so it must be signed */ + unsigned short umask; + struct m_inode * pwd; + struct m_inode * root; + struct m_inode * executable; + unsigned long close_on_exec; + struct file * filp[NR_OPEN]; +/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ + struct desc_struct ldt[3]; +/* tss for this task */ + struct tss_struct tss; +}; + +/* + * INIT_TASK is used to set up the first task table, touch at + * your own risk!. Base=0, limit=0x9ffff (=640kB) + */ +#define INIT_TASK \ +/* state etc */ { 0,15,15, \ +/* signals */ 0,{{},},0, \ +/* ec,brk... */ 0,0,0,0,0,0, \ +/* pid etc.. */ 0,-1,0,0,0, \ +/* uid etc */ 0,0,0,0,0,0, \ +/* alarm */ 0,0,0,0,0,0, \ +/* math */ 0, \ +/* fs info */ -1,0022,NULL,NULL,NULL,0, \ +/* filp */ {NULL,}, \ + { \ + {0,0}, \ +/* ldt */ {0x9f,0xc0fa00}, \ + {0x9f,0xc0f200}, \ + }, \ +/*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ + 0,0,0,0,0,0,0,0, \ + 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ + _LDT(0),0x80000000, \ + {} \ + }, \ +} + +extern struct task_struct *task[NR_TASKS]; +extern struct task_struct *last_task_used_math; +extern struct task_struct *current; +extern long volatile jiffies; +extern long startup_time; + +#define CURRENT_TIME (startup_time+jiffies/HZ) + +extern void add_timer(long jiffies, void (*fn)(void)); +extern void sleep_on(struct task_struct ** p); +extern void interruptible_sleep_on(struct task_struct ** p); +extern void wake_up(struct task_struct ** p); + +/* + * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall + * 4-TSS0, 5-LDT0, 6-TSS1 etc ... + */ +#define FIRST_TSS_ENTRY 4 +#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1) +#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3)) +#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3)) +#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))) +#define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n))) +#define str(n) \ +__asm__("str %%ax\n\t" \ + "subl %2,%%eax\n\t" \ + "shrl $4,%%eax" \ + :"=a" (n) \ + :"a" (0),"i" (FIRST_TSS_ENTRY<<3)) +/* + * switch_to(n) should switch tasks to task nr n, first + * checking that n isn't the current task, in which case it does nothing. + * This also clears the TS-flag if the task we switched to has used + * tha math co-processor latest. + */ +#define switch_to(n) {\ +struct {long a,b;} __tmp; \ +__asm__("cmpl %%ecx,current\n\t" \ + "je 1f\n\t" \ + "movw %%dx,%1\n\t" \ + "xchgl %%ecx,current\n\t" \ + "ljmp *%0\n\t" \ + "cmpl %%ecx,last_task_used_math\n\t" \ + "jne 1f\n\t" \ + "clts\n" \ + "1:" \ + ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ + "d" (_TSS(n)),"c" ((long) task[n])); \ +} + +#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000) + +#define _set_base(addr,base) \ +__asm__ ("push %%edx\n\t" \ + "movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %%dl,%1\n\t" \ + "movb %%dh,%2\n\t" \ + "pop %%edx" \ + ::"m" (*((addr)+2)), \ + "m" (*((addr)+4)), \ + "m" (*((addr)+7)), \ + "d" (base) \ + ) + +#define _set_limit(addr,limit) \ +__asm__ ("push %%edx\n\t" \ + "movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %1,%%dh\n\t" \ + "andb $0xf0,%%dh\n\t" \ + "orb %%dh,%%dl\n\t" \ + "movb %%dl,%1\n\t" \ + "pop %%edx" \ + ::"m" (*(addr)), \ + "m" (*((addr)+6)), \ + "d" (limit) \ + ) + +#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , (base) ) +#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 ) + +static inline unsigned long _get_base(char * addr) +{ + unsigned long __base; + __asm__("movb %3,%%dh\n\t" + "movb %2,%%dl\n\t" + "shll $16,%%edx\n\t" + "movw %1,%%dx" + :"=&d" (__base) + :"m" (*((addr)+2)), + "m" (*((addr)+4)), + "m" (*((addr)+7))); + return __base; +} + +#define get_base(ldt) _get_base( ((char *)&(ldt)) ) + +#define get_limit(segment) ({ \ +unsigned long __limit; \ +__asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \ +__limit;}) + +#endif diff --git a/4/linux/include/linux/sys.h b/4/linux/include/linux/sys.h new file mode 100644 index 0000000..2922fde --- /dev/null +++ b/4/linux/include/linux/sys.h @@ -0,0 +1,122 @@ +/* + * Why isn't this a .c file? Enquiring minds.... + */ + +extern int sys_setup(); +extern int sys_exit(); +extern int sys_fork(); +extern int sys_read(); +extern int sys_write(); +extern int sys_open(); +extern int sys_close(); +extern int sys_waitpid(); +extern int sys_creat(); +extern int sys_link(); +extern int sys_unlink(); +extern int sys_execve(); +extern int sys_chdir(); +extern int sys_time(); +extern int sys_mknod(); +extern int sys_chmod(); +extern int sys_chown(); +extern int sys_break(); +extern int sys_stat(); +extern int sys_lseek(); +extern int sys_getpid(); +extern int sys_mount(); +extern int sys_umount(); +extern int sys_setuid(); +extern int sys_getuid(); +extern int sys_stime(); +extern int sys_ptrace(); +extern int sys_alarm(); +extern int sys_fstat(); +extern int sys_pause(); +extern int sys_utime(); +extern int sys_stty(); +extern int sys_gtty(); +extern int sys_access(); +extern int sys_nice(); +extern int sys_ftime(); +extern int sys_sync(); +extern int sys_kill(); +extern int sys_rename(); +extern int sys_mkdir(); +extern int sys_rmdir(); +extern int sys_dup(); +extern int sys_pipe(); +extern int sys_times(); +extern int sys_prof(); +extern int sys_brk(); +extern int sys_setgid(); +extern int sys_getgid(); +extern int sys_signal(); +extern int sys_geteuid(); +extern int sys_getegid(); +extern int sys_acct(); +extern int sys_phys(); +extern int sys_lock(); +extern int sys_ioctl(); +extern int sys_fcntl(); +extern int sys_mpx(); +extern int sys_setpgid(); +extern int sys_ulimit(); +extern int sys_uname(); +extern int sys_umask(); +extern int sys_chroot(); +extern int sys_ustat(); +extern int sys_dup2(); +extern int sys_getppid(); +extern int sys_getpgrp(); +extern int sys_setsid(); +extern int sys_sigaction(); +extern int sys_sgetmask(); +extern int sys_ssetmask(); +extern int sys_setreuid(); +extern int sys_setregid(); +extern int sys_sigpending(); +extern int sys_sigsuspend(); +extern int sys_sethostname(); +extern int sys_setrlimit(); +extern int sys_getrlimit(); +extern int sys_getrusage(); +extern int sys_gettimeofday(); +extern int sys_settimeofday(); +extern int sys_getgroups(); +extern int sys_setgroups(); +extern int sys_select(); +extern int sys_symlink(); +extern int sys_lstat(); +extern int sys_readlink(); +extern int sys_uselib(); +extern int sys_sleep(); +extern int sys_execve2(); +extern int sys_getdents(); +extern int sys_pipe2(); +extern int sys_sleep(); +extern int sys_getcwd(); +extern int sys_mmap(); +extern int sys_munmap(); +extern int sys_clone(); + + +fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, +sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, +sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod, +sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount, +sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm, +sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access, +sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir, +sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, +sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, +sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit, +sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, +sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask, +sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending, sys_sethostname, +sys_setrlimit, sys_getrlimit, sys_getrusage, sys_gettimeofday, +sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink, +sys_lstat, sys_readlink, sys_uselib, sys_execve2,sys_getdents,sys_pipe2,sys_sleep, +sys_getcwd,sys_mmap,sys_munmap,sys_clone}; + +/* So we don't have to do any more manual updating.... */ +int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr); diff --git a/4/linux/include/linux/tty.h b/4/linux/include/linux/tty.h new file mode 100644 index 0000000..ad846b3 --- /dev/null +++ b/4/linux/include/linux/tty.h @@ -0,0 +1,77 @@ +/* + * 'tty.h' defines some structures used by tty_io.c and some defines. + * + * NOTE! Don't touch this without checking that nothing in rs_io.s or + * con_io.s breaks. Some constants are hardwired into the system (mainly + * offsets into 'tty_queue' + */ + +#ifndef _TTY_H +#define _TTY_H + +#include + +#define TTY_BUF_SIZE 1024 + +struct tty_queue { + unsigned long data; + unsigned long head; + unsigned long tail; + struct task_struct * proc_list; + char buf[TTY_BUF_SIZE]; +}; + +#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1)) +#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1)) +#define EMPTY(a) ((a).head == (a).tail) +#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1)) +#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)]) +#define FULL(a) (!LEFT(a)) +#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1)) +#define GETCH(queue,c) \ +(void)({c=(queue).buf[(queue).tail];INC((queue).tail);}) +#define PUTCH(c,queue) \ +(void)({(queue).buf[(queue).head]=(c);INC((queue).head);}) + +#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) +#define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT]) +#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE]) +#define KILL_CHAR(tty) ((tty)->termios.c_cc[VKILL]) +#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF]) +#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART]) +#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) +#define SUSPEND_CHAR(tty) ((tty)->termios.c_cc[VSUSP]) + +struct tty_struct { + struct termios termios; + int pgrp; + int stopped; + void (*write)(struct tty_struct * tty); + struct tty_queue read_q; + struct tty_queue write_q; + struct tty_queue secondary; + }; + +extern struct tty_struct tty_table[]; + +/* intr=^C quit=^| erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Z eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" + +void rs_init(void); +void con_init(void); +void tty_init(void); + +int tty_read(unsigned c, char * buf, int n); +int tty_write(unsigned c, char * buf, int n); + +void rs_write(struct tty_struct * tty); +void con_write(struct tty_struct * tty); + +void copy_to_cooked(struct tty_struct * tty); + +#endif diff --git a/4/linux/include/new.h b/4/linux/include/new.h new file mode 100644 index 0000000..c254ea8 --- /dev/null +++ b/4/linux/include/new.h @@ -0,0 +1,30 @@ +#ifndef _NEW_H +#define _NEW_H + +#include + +struct linux_dirent { + long d_ino; + off_t d_off; + unsigned short d_reclen; + char d_name[14]; +}; + +/* Return value of `mmap' in case of an error. */ +#define MAP_FAILED ((void *) -1) + +#define PROT_READ 0x1 /* page can be read */ +#define PROT_WRITE 0x2 /* page can be written */ +#define PROT_EXEC 0x4 /* page can be executed */ +#define PROT_SEM 0x8 /* page may be used for atomic ops */ +#define PROT_NONE 0x0 /* page can not be accessed */ + +/* compatibility flags */ +#define MAP_FILE 0 + +#define MAP_SHARED 0x01 /* Share changes */ +#define MAP_PRIVATE 0x02 /* Changes are private */ + +#define CLONE_VM 0x00000100 /* set if VM shared between processes */ + +#endif diff --git a/4/linux/include/signal.h b/4/linux/include/signal.h new file mode 100644 index 0000000..ad599f0 --- /dev/null +++ b/4/linux/include/signal.h @@ -0,0 +1,69 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include + +typedef int sig_atomic_t; +typedef unsigned int sigset_t; /* 32 bits */ + +#define _NSIG 32 +#define NSIG _NSIG + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGUNUSED 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 + +/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */ +#define SA_NOCLDSTOP 1 +#define SA_NOMASK 0x40000000 +#define SA_ONESHOT 0x80000000 + +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ + +#define SIG_DFL ((void (*)(int))0) /* default signal handling */ +#define SIG_IGN ((void (*)(int))1) /* ignore signal */ +#define SIG_ERR ((void (*)(int))-1) /* error return from signal */ + +struct sigaction { + void (*sa_handler)(int); + sigset_t sa_mask; + int sa_flags; + void (*sa_restorer)(void); +}; + +void (*signal(int _sig, void (*_func)(int)))(int); +int raise(int sig); +int kill(pid_t pid, int sig); +int sigaddset(sigset_t *mask, int signo); +int sigdelset(sigset_t *mask, int signo); +int sigemptyset(sigset_t *mask); +int sigfillset(sigset_t *mask); +int sigismember(sigset_t *mask, int signo); /* 1 - is, 0 - not, -1 error */ +int sigpending(sigset_t *set); +int sigprocmask(int how, sigset_t *set, sigset_t *oldset); +int sigsuspend(sigset_t *sigmask); +int sigaction(int sig, struct sigaction *act, struct sigaction *oldact); + +#endif /* _SIGNAL_H */ diff --git a/4/linux/include/stdarg.h b/4/linux/include/stdarg.h new file mode 100644 index 0000000..fd79ec0 --- /dev/null +++ b/4/linux/include/stdarg.h @@ -0,0 +1,28 @@ +#ifndef _STDARG_H +#define _STDARG_H + +typedef char *va_list; + +/* Amount of space required in an argument list for an arg of type TYPE. + TYPE may alternatively be an expression whose type is used. */ + +#define __va_rounded_size(TYPE) \ + (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) + +#ifndef __sparc__ +#define va_start(AP, LASTARG) \ + (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#else +#define va_start(AP, LASTARG) \ + (__builtin_saveregs (), \ + AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#endif + +void va_end (va_list); /* Defined in gnulib */ +#define va_end(AP) + +#define va_arg(AP, TYPE) \ + (AP += __va_rounded_size (TYPE), \ + *((TYPE *) (AP - __va_rounded_size (TYPE)))) + +#endif /* _STDARG_H */ diff --git a/4/linux/include/stddef.h b/4/linux/include/stddef.h new file mode 100644 index 0000000..97f72ff --- /dev/null +++ b/4/linux/include/stddef.h @@ -0,0 +1,19 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef long ptrdiff_t; +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned long size_t; +#endif + +#undef NULL +#define NULL ((void *)0) + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#endif diff --git a/4/linux/include/stdio.h b/4/linux/include/stdio.h new file mode 100644 index 0000000..a779059 --- /dev/null +++ b/4/linux/include/stdio.h @@ -0,0 +1,256 @@ +#ifndef _STDIO_H +#define _STDIO_H + +/* s t d i o + * + * Author: C. E. Chew + * Date: August 1989 + * + * (C) Copyright C E Chew + * + * Feel free to copy, use and distribute this software provided: + * + * 1. you do not pretend that you wrote it + * 2. you leave this copyright notice intact. + * + * Definitions and user interface for the stream io package. + * + * Patchlevel 2.0 + * + * Edit History: + */ + +/* Site specific definitions */ +/*@*/ +#ifndef NULL +# define NULL ((void *)0) +#endif +#define _STDIO_UCHAR_ 0 +#define _STDIO_VA_LIST_ char * +#define _STDIO_SIZE_T_ unsigned int /* type returned by sizeof */ +#define _STDIO_USIZE_T_ unsigned int +/*=*/ + +/* Definitions based on ANSI compiler */ +#ifdef __STDC__ +# ifndef _STDIO_P_ +# define _STDIO_P_(x) x +# endif +# ifndef _STDIO_VA_ +# define _STDIO_VA_ , ... +# endif +# ifndef _STDIO_UCHAR_ +# define _STDIO_UCHAR_ 0 +# endif +#else +# ifndef _STDIO_P_ +# define _STDIO_P_(x) () +# endif +# ifndef _STDIO_VA_ +# define _STDIO_VA_ +# endif +# ifndef _STDIO_UCHAR_ +# define _STDIO_UCHAR_ (0xff) +# endif +#endif + +#ifndef _STDIO_VA_LIST_ +# define _STDIO_VA_LIST_ void * +#endif + +#ifndef _STDIO_SIZE_T_ +# define _STDIO_SIZE_T_ unsigned int +#endif + +#ifndef _STDIO_USIZE_T_ +# define _STDIO_USIZE_T_ unsigned int +#endif + +/* ANSI Definitions */ +#define BUFSIZ 1024 /* default buffer size */ + +#ifndef NULL +# define NULL ((void *) 0) /* null pointer */ +#endif + +#define EOF (-1) /* eof flag */ +#define FOPEN_MAX 16 /* minimum guarantee */ +#define FILENAME_MAX 127 /* maximum length of file name */ + +#define SEEK_SET 0 /* seek from beginning */ +#define SEEK_CUR 1 /* seek from here */ +#define SEEK_END 2 /* seek from end */ + +#define TMP_MAX (0xffff) /* maximum number of temporaries */ + +#define L_tmpnam (5 + 8 + 4 + 1 + 1) /* length of temporary file name */ + +#ifndef _FPOS_T +# define _FPOS_T + typedef long fpos_t; /* stream positioning */ +#endif + +#ifndef _SIZE_T +# define _SIZE_T + typedef _STDIO_SIZE_T_ size_t; /* sizeof type */ +#endif + +#define _IOFBF 000000 /* fully buffered io */ +#define _IOREAD 000001 /* opened for reading */ +#define _IOWRITE 000002 /* opened for writing */ +#define _IONBF 000004 /* unbuffered */ +#define _IOMYBUF 000010 /* allocated buffer */ +#define _IOPOOLBUF 000020 /* buffer belongs to pool */ +#define _IOEOF 000040 /* eof encountered */ +#define _IOERR 000100 /* error encountered */ +#define _IOSTRING 000200 /* strings */ +#define _IOLBF 000400 /* line buffered */ +#define _IORW 001000 /* opened for reading and writing */ +#define _IOAPPEND 002000 /* append mode */ +#define _IOINSERT 004000 /* insert into __iop chain */ +#define _IOSTDX 030000 /* standard stream */ + +#define _IOSTDIN 010000 /* stdin indication */ +#define _IOSTDOUT 020000 /* stdout indication */ +#define _IOSTDERR 030000 /* stderr indication */ + +#define _IORETAIN (_IOSTDX | _IOINSERT) /* flags to be retained */ + +/* Implementation Definitions */ + +typedef char __stdiobuf_t; /* stdio buffer type */ +typedef _STDIO_USIZE_T_ __stdiosize_t; /* unsigned size_t */ + +typedef struct __iobuf { + __stdiobuf_t *__rptr; /* pointer into read buffer */ + __stdiobuf_t *__rend; /* point at end of read buffer */ + __stdiobuf_t *__wptr; /* pointer into write buffer */ + __stdiobuf_t *__wend; /* point at end of write buffer */ + __stdiobuf_t *__base; /* base of buffer */ + __stdiosize_t __bufsiz; /* size of buffer */ + short __flag; /* flags */ + char __file; /* channel number */ + __stdiobuf_t __buf; /* small buffer */ + int (*__filbuf) _STDIO_P_((struct __iobuf *)); /* fill input buffer */ + int (*__flsbuf) _STDIO_P_((int, struct __iobuf *)); /* flush output buffer */ + int (*__flush) _STDIO_P_((struct __iobuf *)); /* flush buffer */ + struct __iobuf *__next; /* next in chain */ +} FILE; + +extern FILE __stdin; /* stdin */ +extern FILE __stdout; /* stdout */ +extern FILE __stderr; /* stderr */ + +#define stdin (&__stdin) +#define stdout (&__stdout) +#define stderr (&__stderr) + +/* ANSI Stdio Requirements */ + +int getc _STDIO_P_((FILE *)); +#if _STDIO_UCHAR_ +# define getc(p) ((p)->__rptr>=(p)->__rend\ + ?(*(p)->__filbuf)(p)\ + :(int)(*(p)->__rptr++&_STDIO_UCHAR_)) +#else +# define getc(p) ((p)->__rptr>=(p)->__rend\ + ?(*(p)->__filbuf)(p)\ + :(int)((unsigned char)(*(p)->__rptr++))) +#endif + +int getchar _STDIO_P_((void)); +#define getchar() getc(stdin) + +int putc _STDIO_P_((int, FILE *)); +#if _STDIO_UCHAR_ +# define putc(x,p) ((p)->__wptr>=(p)->__wend\ + ?(*(p)->__flsbuf)((x),(p))\ + :(int)(*(p)->__wptr++=(x)&_STDIO_UCHAR_)) +#else +# define putc(x,p) ((p)->__wptr>=(p)->__wend\ + ?(*(p)->__flsbuf)((x),(p))\ + :(int)((unsigned char)(*(p)->__wptr++=(x)))) +#endif + +int putchar _STDIO_P_((int)); +#define putchar(x) putc(x,stdout) + +int feof _STDIO_P_((FILE *)); +#define feof(p) (((p)->__flag&_IOEOF)!=0) + +int ferror _STDIO_P_((FILE *)); +#define ferror(p) (((p)->__flag&_IOERR)!=0) + +void clearerr _STDIO_P_((FILE *)); +#define clearerr(p) ((p)->__flag&=~(_IOEOF|_IOERR)) + +FILE *fopen _STDIO_P_((const char *, const char *)); +FILE *freopen _STDIO_P_((const char *, const char *, FILE *)); +int fflush _STDIO_P_((FILE *)); +int fclose _STDIO_P_((FILE *)); + +int fgetpos _STDIO_P_((FILE *, fpos_t *)); +int fsetpos _STDIO_P_((FILE *, fpos_t *)); +long ftell _STDIO_P_((FILE *)); +int fseek _STDIO_P_((FILE *, long, int)); +void rewind _STDIO_P_((FILE *)); + +int fgetc _STDIO_P_((FILE *)); +int fputc _STDIO_P_((int, FILE *)); +__stdiosize_t fread _STDIO_P_((void *, __stdiosize_t, + __stdiosize_t, FILE *)); +__stdiosize_t fwrite _STDIO_P_((void *, __stdiosize_t, + __stdiosize_t, FILE *)); + +int getw _STDIO_P_((FILE *)); +int putw _STDIO_P_((int, FILE *)); +char *gets _STDIO_P_((char *)); +char *fgets _STDIO_P_((char *, int, FILE *)); +int puts _STDIO_P_((const char *)); +int fputs _STDIO_P_((const char *, FILE *)); + +int ungetc _STDIO_P_((int, FILE *)); + +int printf _STDIO_P_((const char * _STDIO_VA_)); +int fprintf _STDIO_P_((FILE *, const char * _STDIO_VA_)); +int sprintf _STDIO_P_((char *, const char * _STDIO_VA_)); +int vprintf _STDIO_P_((const char *, _STDIO_VA_LIST_)); +int vfprintf _STDIO_P_((FILE *, const char *, _STDIO_VA_LIST_)); +int vsprintf _STDIO_P_((char *, const char *, _STDIO_VA_LIST_)); +int scanf _STDIO_P_((const char * _STDIO_VA_)); +int fscanf _STDIO_P_((FILE *, const char * _STDIO_VA_)); +int sscanf _STDIO_P_((const char *, const char * _STDIO_VA_)); + +void setbuf _STDIO_P_((FILE *, char *)); +int setvbuf _STDIO_P_((FILE *, char *, int, __stdiosize_t)); + +int rename _STDIO_P_((const char *, const char *)); +int remove _STDIO_P_((const char *)); + +void perror _STDIO_P_((const char *)); + +char * tmpnam _STDIO_P_((char *)); +FILE * tmpfile _STDIO_P_((void)); + +/* Posix Definitions */ +int unlink _STDIO_P_((const char *)); +#define remove(x) unlink((x)) + +#define L_ctermid 9 +char * ctermid _STDIO_P_((char *s)); + +#define L_cuserid 9 +char * cuserid _STDIO_P_((char *s)); + +FILE *fdopen _STDIO_P_((int, const char *)); + +int fileno _STDIO_P_((FILE *)); +#define fileno(p) ((p)->__file) + +#undef _STDIO_P_ +#undef _STDIO_VA_ +#undef _STDIO_VA_LIST_ +/*ndef _STDIO_UCHAR_*/ +#undef _STDIO_SIZE_T_ +#undef _STDIO_USIZE_T_ +#endif diff --git a/4/linux/include/stdlib.h b/4/linux/include/stdlib.h new file mode 100644 index 0000000..7880598 --- /dev/null +++ b/4/linux/include/stdlib.h @@ -0,0 +1,43 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + +extern double atof(const char * s); +extern int atoi(const char *s); +extern long atol(const char *s); +extern double strtod(const char *s, char **endp); +extern long strtol(const char *s, char **endp, int base); +extern unsigned long strtoul(const char *s, char **endp, int base); +extern int rand(void); +extern void srand(unsigned int seed); +extern void * calloc(size_t nobj, size_t size); +extern void * malloc(size_t size); +extern void * realloc(void * p, size_t size); +extern void free(void * p); +extern void abort(void); +extern volatile void exit(int status); +extern int atexit(void (*fcn)(void)); +extern int system(const char *s); +extern char * getenv(const char *name); +extern void * bsearch(const void *key, const void *base, + size_t n, size_t size, + int (*cmp)(const void *keyval, const void *datum)); +extern void qsort(void *base, size_t n, size_t size, + int (*cmp)(const void *,const void *)); +extern int abs(int n); +extern long labs(long n); +extern div_t div(int num, int denom); +extern ldiv_t ldiv(long num, long denom); +extern char * getcwd(char * buf, size_t size); + +#ifdef __GNUC__ +#define __alloca(n) __builtin_alloca(n) +#else +#define __alloca(n) alloca(n) +#endif + +#endif diff --git a/4/linux/include/string.h b/4/linux/include/string.h new file mode 100644 index 0000000..05a6d9c --- /dev/null +++ b/4/linux/include/string.h @@ -0,0 +1,405 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +extern char * strerror(int errno); + +/* + * This string-include defines all string functions as inline + * functions. Use gcc. It also assumes ds=es=data space, this should be + * normal. Most of the string-functions are rather heavily hand-optimized, + * see especially strtok,strstr,str[c]spn. They should work, but are not + * very easy to understand. Everything is done entirely within the register + * set, making the functions fast and clean. String instructions have been + * used through-out, making for "slightly" unclear code :-) + * + * (C) 1991 Linus Torvalds + */ + +extern inline char * strcpy(char * dest,const char *src) +{ +__asm__("cld\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + ::"S" (src),"D" (dest):"si","di","ax"); +return dest; +} + +extern inline char * strncpy(char * dest,const char *src,int count) +{ +__asm__("cld\n" + "1:\tdecl %2\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "rep\n\t" + "stosb\n" + "2:" + ::"S" (src),"D" (dest),"c" (count):"si","di","ax","cx"); +return dest; +} + +extern inline char * strcat(char * dest,const char * src) +{ +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx"); +return dest; +} + +extern inline char * strncat(char * dest,const char * src,int count) +{ +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n\t" + "movl %4,%3\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %2,%2\n\t" + "stosb" + ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count) + :"si","di","ax","cx"); +return dest; +} + +extern inline int strcmp(const char * cs,const char * ct) +{ +register int __res __asm__("ax"); +__asm__("cld\n" + "1:\tlodsb\n\t" + "scasb\n\t" + "jne 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "jmp 3f\n" + "2:\tmovl $1,%%eax\n\t" + "jl 3f\n\t" + "negl %%eax\n" + "3:" + :"=a" (__res):"D" (cs),"S" (ct):"si","di"); +return __res; +} + +extern inline int strncmp(const char * cs,const char * ct,int count) +{ +register int __res __asm__("ax"); +__asm__("cld\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "scasb\n\t" + "jne 3f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %%eax,%%eax\n\t" + "jmp 4f\n" + "3:\tmovl $1,%%eax\n\t" + "jl 4f\n\t" + "negl %%eax\n" + "4:" + :"=a" (__res):"D" (cs),"S" (ct),"c" (count):"si","di","cx"); +return __res; +} + +extern inline char * strchr(const char * s,char c) +{ +register char * __res __asm__("ax"); +__asm__("cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "je 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "movl $1,%1\n" + "2:\tmovl %1,%0\n\t" + "decl %0" + :"=a" (__res):"S" (s),"0" (c):"si"); +return __res; +} + +extern inline char * strrchr(const char * s,char c) +{ +register char * __res __asm__("dx"); +__asm__("cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "jne 2f\n\t" + "movl %%esi,%0\n\t" + "decl %0\n" + "2:\ttestb %%al,%%al\n\t" + "jne 1b" + :"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si"); +return __res; +} + +extern inline int strspn(const char * cs, const char * ct) +{ +register char * __res __asm__("si"); +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res-cs; +} + +extern inline int strcspn(const char * cs, const char * ct) +{ +register char * __res __asm__("si"); +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res-cs; +} + +extern inline char * strpbrk(const char * cs,const char * ct) +{ +register char * __res __asm__("si"); +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n\t" + "decl %0\n\t" + "jmp 3f\n" + "2:\txorl %0,%0\n" + "3:" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res; +} + +extern inline char * strstr(const char * cs,const char * ct) +{ +register char * __res __asm__("ax"); +__asm__("cld\n\t" \ + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */ + "movl %%ecx,%%edx\n" + "1:\tmovl %4,%%edi\n\t" + "movl %%esi,%%eax\n\t" + "movl %%edx,%%ecx\n\t" + "repe\n\t" + "cmpsb\n\t" + "je 2f\n\t" /* also works for empty string, see above */ + "xchgl %%eax,%%esi\n\t" + "incl %%esi\n\t" + "cmpb $0,-1(%%eax)\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "2:" + :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct) + :"cx","dx","di","si"); +return __res; +} + +extern inline int strlen(const char * s) +{ +register int __res __asm__("cx"); +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "notl %0\n\t" + "decl %0" + :"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di"); +return __res; +} + +extern char * ___strtok; + +extern inline char * strtok(char * s,const char * ct) +{ +register char * __res __asm__("si"); +__asm__("testl %1,%1\n\t" + "jne 1f\n\t" + "testl %0,%0\n\t" + "je 8f\n\t" + "movl %0,%1\n" + "1:\txorl %0,%0\n\t" + "movl $-1,%%ecx\n\t" + "xorl %%eax,%%eax\n\t" + "cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "je 7f\n\t" /* empty delimeter-string */ + "movl %%ecx,%%edx\n" + "2:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 7f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 2b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 7f\n\t" + "movl %1,%0\n" + "3:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 5f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 3b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 5f\n\t" + "movb $0,(%1)\n\t" + "incl %1\n\t" + "jmp 6f\n" + "5:\txorl %1,%1\n" + "6:\tcmpb $0,(%0)\n\t" + "jne 7f\n\t" + "xorl %0,%0\n" + "7:\ttestl %0,%0\n\t" + "jne 8f\n\t" + "movl %0,%1\n" + "8:" + :"=b" (__res),"=S" (___strtok) + :"0" (___strtok),"1" (s),"g" (ct) + :"ax","cx","dx","di"); +return __res; +} + +extern inline void * memcpy(void * dest,const void * src, int n) +{ +__asm__("cld\n\t" + "rep\n\t" + "movsb" + ::"c" (n),"S" (src),"D" (dest) + :"cx","si","di"); +return dest; +} + +extern inline void * memmove(void * dest,const void * src, int n) +{ +if (dest + +struct stat { + dev_t st_dev; + ino_t st_ino; + umode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + off_t st_size; + time_t st_atime; + time_t st_mtime; + time_t st_ctime; +}; + +#define S_IFMT 00170000 +#define S_IFREG 0100000 +#define S_IFBLK 0060000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFIFO 0010000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 + +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) + +#define S_IRWXU 00700 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 + +#define S_IRWXG 00070 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 + +#define S_IRWXO 00007 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 + +extern int chmod(const char *_path, mode_t mode); +extern int fstat(int fildes, struct stat *stat_buf); +extern int mkdir(const char *_path, mode_t mode); +extern int mkfifo(const char *_path, mode_t mode); +extern int stat(const char *filename, struct stat *stat_buf); +extern mode_t umask(mode_t mask); + +#endif diff --git a/4/linux/include/sys/times.h b/4/linux/include/sys/times.h new file mode 100644 index 0000000..68d5bfb --- /dev/null +++ b/4/linux/include/sys/times.h @@ -0,0 +1,15 @@ +#ifndef _TIMES_H +#define _TIMES_H + +#include + +struct tms { + time_t tms_utime; + time_t tms_stime; + time_t tms_cutime; + time_t tms_cstime; +}; + +extern time_t times(struct tms * tp); + +#endif diff --git a/4/linux/include/sys/types.h b/4/linux/include/sys/types.h new file mode 100644 index 0000000..557aa31 --- /dev/null +++ b/4/linux/include/sys/types.h @@ -0,0 +1,46 @@ +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; +#endif + +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef long ptrdiff_t; +#endif + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +typedef int pid_t; +typedef unsigned short uid_t; +typedef unsigned char gid_t; +typedef unsigned short dev_t; +typedef unsigned short ino_t; +typedef unsigned short mode_t; +typedef unsigned short umode_t; +typedef unsigned char nlink_t; +typedef int daddr_t; +typedef long off_t; +typedef unsigned char u_char; +typedef unsigned short ushort; + +typedef struct { int quot,rem; } div_t; +typedef struct { long quot,rem; } ldiv_t; + +struct ustat { + daddr_t f_tfree; + ino_t f_tinode; + char f_fname[6]; + char f_fpack[6]; +}; + +#endif diff --git a/4/linux/include/sys/utsname.h b/4/linux/include/sys/utsname.h new file mode 100644 index 0000000..0a1c5a0 --- /dev/null +++ b/4/linux/include/sys/utsname.h @@ -0,0 +1,16 @@ +#ifndef _SYS_UTSNAME_H +#define _SYS_UTSNAME_H + +#include + +struct utsname { + char sysname[9]; + char nodename[9]; + char release[9]; + char version[9]; + char machine[9]; +}; + +extern int uname(struct utsname * utsbuf); + +#endif diff --git a/4/linux/include/sys/wait.h b/4/linux/include/sys/wait.h new file mode 100644 index 0000000..53190c2 --- /dev/null +++ b/4/linux/include/sys/wait.h @@ -0,0 +1,23 @@ +#ifndef _SYS_WAIT_H +#define _SYS_WAIT_H + +#include + +#define _LOW(v) ( (v) & 0377) +#define _HIGH(v) ( ((v) >> 8) & 0377) + +/* options for waitpid, WUNTRACED not supported */ +#define WNOHANG 1 +#define WUNTRACED 2 + +#define WIFEXITED(s) (!((s)&0xFF) +#define WIFSTOPPED(s) (((s)&0xFF)==0x7F) +#define WEXITSTATUS(s) (((s)>>8)&0xFF) +#define WTERMSIG(s) ((s)&0x7F) +#define WSTOPSIG(s) (((s)>>8)&0xFF) +#define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF) + +pid_t wait(int *stat_loc); +pid_t waitpid(pid_t pid, int *stat_loc, int options); + +#endif diff --git a/4/linux/include/termios.h b/4/linux/include/termios.h new file mode 100644 index 0000000..2b7b913 --- /dev/null +++ b/4/linux/include/termios.h @@ -0,0 +1,228 @@ +#ifndef _TERMIOS_H +#define _TERMIOS_H + +#define TTY_BUF_SIZE 1024 + +/* 0x54 is just a magic number to make these relatively uniqe ('T') */ + +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCGETA 0x5405 +#define TCSETA 0x5406 +#define TCSETAW 0x5407 +#define TCSETAF 0x5408 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TCFLSH 0x540B +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define TIOCINQ 0x541B + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +#define NCCS 17 +struct termios { + unsigned long c_iflag; /* input mode flags */ + unsigned long c_oflag; /* output mode flags */ + unsigned long c_cflag; /* control mode flags */ + unsigned long c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCCS]; /* control characters */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0040000 +#define FF0 0000000 +#define FF1 0040000 + +/* c_cflag bit meaning */ +#define CBAUD 0000017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define CPARENB 0000400 +#define CPARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CIBAUD 03600000 /* input baud rate (not used) */ +#define CRTSCTS 020000000000 /* flow control */ + +#define PARENB CPARENB +#define PARODD CPARODD + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* modem lines */ +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +typedef int speed_t; + +extern speed_t cfgetispeed(struct termios *termios_p); +extern speed_t cfgetospeed(struct termios *termios_p); +extern int cfsetispeed(struct termios *termios_p, speed_t speed); +extern int cfsetospeed(struct termios *termios_p, speed_t speed); +extern int tcdrain(int fildes); +extern int tcflow(int fildes, int action); +extern int tcflush(int fildes, int queue_selector); +extern int tcgetattr(int fildes, struct termios *termios_p); +extern int tcsendbreak(int fildes, int duration); +extern int tcsetattr(int fildes, int optional_actions, + struct termios *termios_p); + +#endif diff --git a/4/linux/include/time.h b/4/linux/include/time.h new file mode 100644 index 0000000..d0a765d --- /dev/null +++ b/4/linux/include/time.h @@ -0,0 +1,42 @@ +#ifndef _TIME_H +#define _TIME_H + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#define CLOCKS_PER_SEC 100 + +typedef long clock_t; + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +clock_t clock(void); +time_t time(time_t * tp); +double difftime(time_t time2, time_t time1); +time_t mktime(struct tm * tp); + +char * asctime(const struct tm * tp); +char * ctime(const time_t * tp); +struct tm * gmtime(const time_t *tp); +struct tm *localtime(const time_t * tp); +size_t strftime(char * s, size_t smax, const char * fmt, const struct tm * tp); +void tzset(void); + +#endif diff --git a/4/linux/include/unistd.h b/4/linux/include/unistd.h new file mode 100644 index 0000000..18ab9e5 --- /dev/null +++ b/4/linux/include/unistd.h @@ -0,0 +1,278 @@ +#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 +#include +#include +#include + +#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 __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_lstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_execve2 87 +#define __NR_getdents 88 +#define __NR_pipe2 89 +#define __NR_sleep 90 +#define __NR_getcwd 91 +#define __NR_mmap 92 +#define __NR_munmap 93 +#define __NR_clone 94 + +#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); +int execve2(const char* path,char*argv[],char*envp[]); +#define __always_inline inline __attribute__((always_inline)) + +#endif diff --git a/4/linux/include/utime.h b/4/linux/include/utime.h new file mode 100644 index 0000000..83f07c7 --- /dev/null +++ b/4/linux/include/utime.h @@ -0,0 +1,13 @@ +#ifndef _UTIME_H +#define _UTIME_H + +#include /* I know - shouldn't do this, but .. */ + +struct utimbuf { + time_t actime; + time_t modtime; +}; + +extern int utime(const char *filename, struct utimbuf *times); + +#endif diff --git a/4/linux/init/main.c b/4/linux/init/main.c new file mode 100644 index 0000000..bd86a68 --- /dev/null +++ b/4/linux/init/main.c @@ -0,0 +1,216 @@ +/* + * linux/init/main.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include +#include + +/* + * 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. + */ + +__always_inline _syscall0(int,fork) +__always_inline _syscall0(int,pause) +__always_inline _syscall1(int,setup,void *,BIOS) +__always_inline _syscall0(int,sync) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +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; + __asm__ volatile ("cld"); /* by wyj */ + 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, NULL }; + +static char * argv[] = { "-/bin/sh",NULL }; +static char * envp[] = { "HOME=/usr/root", NULL, 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() */ +} + +void print_nr(int sid) +{ + if (sid > 86) + printk(" --syscall: sid=%d, pid=%d\n", sid, current->pid); +} diff --git a/4/linux/kernel/Makefile b/4/linux/kernel/Makefile new file mode 100644 index 0000000..29b8f8d --- /dev/null +++ b/4/linux/kernel/Makefile @@ -0,0 +1,83 @@ +# +# Makefile for the FREAX-kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +AR =ar +AS =as +LD =ld +LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32 +CC =gcc -march=i386 +CFLAGS =-w -g -fomit-frame-pointer -finline-functions \ + -nostdinc -fno-stack-protector -mcld -I../include +CPP =gcc -E -nostdinc -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = sched.o system_call.o traps.o asm.o fork.o \ + panic.o printk.o vsprintf.o sys.o exit.o \ + signal.o mktime.o + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +clean: + rm -f core *.o *.a tmp_make keyboard.s + for i in *.c;do rm -f `basename $$i .c`.s;done + (cd chr_drv; make clean) + (cd blk_drv; make clean) + (cd math; make clean) + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + (cd chr_drv; make dep) + (cd blk_drv; make dep) + +### Dependencies: +exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \ + ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \ + ../include/asm/segment.h +fork.s fork.o : fork.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ + ../include/asm/segment.h ../include/asm/system.h +mktime.s mktime.o : mktime.c ../include/time.h +panic.s panic.o : panic.c ../include/linux/kernel.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h +printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \ + ../include/linux/kernel.h +sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/signal.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 +signal.s signal.o : signal.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h +sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \ + ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \ + ../include/sys/times.h ../include/sys/utsname.h +traps.s traps.o : traps.c ../include/string.h ../include/linux/head.h \ + ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \ + ../include/asm/system.h ../include/asm/segment.h ../include/asm/io.h +vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h diff --git a/4/linux/kernel/asm.s b/4/linux/kernel/asm.s new file mode 100644 index 0000000..1022817 --- /dev/null +++ b/4/linux/kernel/asm.s @@ -0,0 +1,146 @@ +/* + * linux/kernel/asm.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * asm.s contains the low-level code for most hardware faults. + * page_exception is handled by the mm, so that isn't here. This + * file also handles (hopefully) fpu-exceptions due to TS-bit, as + * the fpu must be properly saved/resored. This hasn't been tested. + */ + +.globl divide_error,debug,nmi,int3,overflow,bounds,invalid_op +.globl double_fault,coprocessor_segment_overrun +.globl invalid_TSS,segment_not_present,stack_segment +.globl general_protection,coprocessor_error,irq13,reserved + +divide_error: + pushl $do_divide_error +no_error_code: + xchgl %eax,(%esp) + pushl %ebx + pushl %ecx + pushl %edx + pushl %edi + pushl %esi + pushl %ebp + push %ds + push %es + push %fs + pushl $0 # "error code" + lea 44(%esp),%edx + pushl %edx + movl $0x10,%edx + mov %dx,%ds + mov %dx,%es + mov %dx,%fs + call *%eax + addl $8,%esp + pop %fs + pop %es + pop %ds + popl %ebp + popl %esi + popl %edi + popl %edx + popl %ecx + popl %ebx + popl %eax + iret + +debug: + pushl $do_int3 # _do_debug + jmp no_error_code + +nmi: + pushl $do_nmi + jmp no_error_code + +int3: + pushl $do_int3 + jmp no_error_code + +overflow: + pushl $do_overflow + jmp no_error_code + +bounds: + pushl $do_bounds + jmp no_error_code + +invalid_op: + pushl $do_invalid_op + jmp no_error_code + +coprocessor_segment_overrun: + pushl $do_coprocessor_segment_overrun + jmp no_error_code + +reserved: + pushl $do_reserved + jmp no_error_code + +irq13: + pushl %eax + xorb %al,%al + outb %al,$0xF0 + movb $0x20,%al + outb %al,$0x20 + jmp 1f +1: jmp 1f +1: outb %al,$0xA0 + popl %eax + jmp coprocessor_error + +double_fault: + pushl $do_double_fault +error_code: + xchgl %eax,4(%esp) # error code <-> %eax + xchgl %ebx,(%esp) # &function <-> %ebx + pushl %ecx + pushl %edx + pushl %edi + pushl %esi + pushl %ebp + push %ds + push %es + push %fs + pushl %eax # error code + lea 44(%esp),%eax # offset + pushl %eax + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + call *%ebx + addl $8,%esp + pop %fs + pop %es + pop %ds + popl %ebp + popl %esi + popl %edi + popl %edx + popl %ecx + popl %ebx + popl %eax + iret + +invalid_TSS: + pushl $do_invalid_TSS + jmp error_code + +segment_not_present: + pushl $do_segment_not_present + jmp error_code + +stack_segment: + pushl $do_stack_segment + jmp error_code + +general_protection: + pushl $do_general_protection + jmp error_code + diff --git a/4/linux/kernel/blk_drv/Makefile b/4/linux/kernel/blk_drv/Makefile new file mode 100644 index 0000000..1fb57f9 --- /dev/null +++ b/4/linux/kernel/blk_drv/Makefile @@ -0,0 +1,58 @@ +# +# Makefile for the FREAX-kernel block device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +AR =ar +AS =as +LD =ld +LDFLAGS =-s -x +CC =gcc -march=i386 +CFLAGS =-w -g -fstrength-reduce -fomit-frame-pointer -mcld \ + -finline-functions -nostdinc -fno-stack-protector -I../../include +CPP =gcc -E -nostdinc -I../../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o + +blk_drv.a: $(OBJS) + $(AR) rcs blk_drv.a $(OBJS) + sync + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +floppy.s floppy.o : floppy.c ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/signal.h ../../include/linux/kernel.h \ + ../../include/linux/fdreg.h ../../include/asm/system.h \ + ../../include/asm/io.h ../../include/asm/segment.h blk.h +hd.s hd.o : hd.c ../../include/linux/config.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \ + ../../include/linux/kernel.h ../../include/linux/hdreg.h \ + ../../include/asm/system.h ../../include/asm/io.h \ + ../../include/asm/segment.h blk.h +ll_rw_blk.s ll_rw_blk.o : ll_rw_blk.c ../../include/errno.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \ + ../../include/linux/kernel.h ../../include/asm/system.h blk.h diff --git a/4/linux/kernel/blk_drv/blk.h b/4/linux/kernel/blk_drv/blk.h new file mode 100644 index 0000000..7a69b71 --- /dev/null +++ b/4/linux/kernel/blk_drv/blk.h @@ -0,0 +1,140 @@ +#ifndef _BLK_H +#define _BLK_H + +#define NR_BLK_DEV 7 +/* + * NR_REQUEST is the number of entries in the request-queue. + * NOTE that writes may use only the low 2/3 of these: reads + * take precedence. + * + * 32 seems to be a reasonable number: enough to get some benefit + * from the elevator-mechanism, but not so much as to lock a lot of + * buffers when they are in the queue. 64 seems to be too many (easily + * long pauses in reading when heavy writing/syncing is going on) + */ +#define NR_REQUEST 32 + +/* + * Ok, this is an expanded form so that we can use the same + * request for paging requests when that is implemented. In + * paging, 'bh' is NULL, and 'waiting' is used to wait for + * read/write completion. + */ +struct request { + int dev; /* -1 if no request */ + int cmd; /* READ or WRITE */ + int errors; + unsigned long sector; + unsigned long nr_sectors; + char * buffer; + struct task_struct * waiting; + struct buffer_head * bh; + struct request * next; +}; + +/* + * This is used in the elevator algorithm: Note that + * reads always go before writes. This is natural: reads + * are much more time-critical than writes. + */ +#define IN_ORDER(s1,s2) \ +((s1)->cmd<(s2)->cmd || (s1)->cmd==(s2)->cmd && \ +((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \ +(s1)->sector < (s2)->sector))) + +struct blk_dev_struct { + void (*request_fn)(void); + struct request * current_request; +}; + +extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; +extern struct request request[NR_REQUEST]; +extern struct task_struct * wait_for_request; + +#ifdef MAJOR_NR + +/* + * Add entries as needed. Currently the only block devices + * supported are hard-disks and floppies. + */ + +#if (MAJOR_NR == 1) +/* ram disk */ +#define DEVICE_NAME "ramdisk" +#define DEVICE_REQUEST do_rd_request +#define DEVICE_NR(device) ((device) & 7) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == 2) +/* floppy */ +#define DEVICE_NAME "floppy" +#define DEVICE_INTR do_floppy +#define DEVICE_REQUEST do_fd_request +#define DEVICE_NR(device) ((device) & 3) +#define DEVICE_ON(device) floppy_on(DEVICE_NR(device)) +#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device)) + +#elif (MAJOR_NR == 3) +/* harddisk */ +#define DEVICE_NAME "harddisk" +#define DEVICE_INTR do_hd +#define DEVICE_REQUEST do_hd_request +#define DEVICE_NR(device) (MINOR(device)/5) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR > 3) +/* unknown blk device */ +#error "unknown blk device" + +#endif + +#define CURRENT (blk_dev[MAJOR_NR].current_request) +#define CURRENT_DEV DEVICE_NR(CURRENT->dev) + +#ifdef DEVICE_INTR +void (*DEVICE_INTR)(void) = NULL; +#endif +static void (DEVICE_REQUEST)(void); + +static inline void unlock_buffer(struct buffer_head * bh) +{ + if (!bh->b_lock) + printk(DEVICE_NAME ": free buffer being unlocked\n"); + bh->b_lock=0; + wake_up(&bh->b_wait); +} + +static inline void end_request(int uptodate) +{ + DEVICE_OFF(CURRENT->dev); + if (CURRENT->bh) { + CURRENT->bh->b_uptodate = uptodate; + unlock_buffer(CURRENT->bh); + } + if (!uptodate) { + printk(DEVICE_NAME " I/O error\n\r"); + printk("dev %04x, block %d\n\r",CURRENT->dev, + CURRENT->bh->b_blocknr); + } + wake_up(&CURRENT->waiting); + wake_up(&wait_for_request); + CURRENT->dev = -1; + CURRENT = CURRENT->next; +} + +#define INIT_REQUEST \ +repeat: \ + if (!CURRENT) \ + return; \ + if (MAJOR(CURRENT->dev) != MAJOR_NR) \ + panic(DEVICE_NAME ": request list destroyed"); \ + if (CURRENT->bh) { \ + if (!CURRENT->bh->b_lock) \ + panic(DEVICE_NAME ": block not locked"); \ + } + +#endif + +#endif diff --git a/4/linux/kernel/blk_drv/floppy.c b/4/linux/kernel/blk_drv/floppy.c new file mode 100644 index 0000000..b1a7f3a --- /dev/null +++ b/4/linux/kernel/blk_drv/floppy.c @@ -0,0 +1,463 @@ +/* + * linux/kernel/floppy.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 02.12.91 - Changed to static variables to indicate need for reset + * and recalibrate. This makes some things easier (output_byte reset + * checking etc), and means less interrupt jumping in case of errors, + * so the code is hopefully easier to understand. + */ + +/* + * This file is certainly a mess. I've tried my best to get it working, + * but I don't like programming floppies, and I have only one anyway. + * Urgel. I should check for more errors, and do more graceful error + * recovery. Seems there are problems with several drives. I've tried to + * correct them. No promises. + */ + +/* + * As with hd.c, all routines within this file can (and will) be called + * by interrupts, so extreme caution is needed. A hardware interrupt + * handler may not sleep, or a kernel panic will happen. Thus I cannot + * call "floppy-on" directly, but have to set a special timer interrupt + * etc. + * + * Also, I'm not certain this works on more than 1 floppy. Bugs may + * abund. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR 2 +#include "blk.h" + +static int recalibrate = 0; +static int reset = 0; +static int seek = 0; + +extern unsigned char current_DOR; + +#define immoutb_p(val,port) \ +__asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port)) + +#define TYPE(x) ((x)>>2) +#define DRIVE(x) ((x)&0x03) +/* + * Note that MAX_ERRORS=8 doesn't imply that we retry every bad read + * max 8 times - some types of errors increase the errorcount by 2, + * so we might actually retry only 5-6 times before giving up. + */ +#define MAX_ERRORS 8 + +/* + * globals used by 'result()' + */ +#define MAX_REPLIES 7 +static unsigned char reply_buffer[MAX_REPLIES]; +#define ST0 (reply_buffer[0]) +#define ST1 (reply_buffer[1]) +#define ST2 (reply_buffer[2]) +#define ST3 (reply_buffer[3]) + +/* + * This struct defines the different floppy types. Unlike minix + * linux doesn't have a "search for right type"-type, as the code + * for that is convoluted and weird. I've got enough problems with + * this driver as it is. + * + * The 'stretch' tells if the tracks need to be boubled for some + * types (ie 360kB diskette in 1.2MB drive etc). Others should + * be self-explanatory. + */ +static struct floppy_struct { + unsigned int size, sect, head, track, stretch; + unsigned char gap,rate,spec1; +} floppy_type[] = { + { 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */ + { 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */ + { 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */ + { 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */ + { 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */ + { 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */ + { 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */ + { 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */ +}; +/* + * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps + * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), + * H is head unload time (1=16ms, 2=32ms, etc) + * + * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc) + * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). + */ + +extern void floppy_interrupt(void); +extern char tmp_floppy_area[1024]; + +/* + * These are global variables, as that's the easiest way to give + * information to interrupts. They are the data used for the current + * request. + */ +static int cur_spec1 = -1; +static int cur_rate = -1; +static struct floppy_struct * floppy = floppy_type; +static unsigned char current_drive = 0; +static unsigned char sector = 0; +static unsigned char head = 0; +static unsigned char track = 0; +static unsigned char seek_track = 0; +static unsigned char current_track = 255; +static unsigned char command = 0; +unsigned char selected = 0; +struct task_struct * wait_on_floppy_select = NULL; + +void floppy_deselect(unsigned int nr) +{ + if (nr != (current_DOR & 3)) + printk("floppy_deselect: drive not selected\n\r"); + selected = 0; + wake_up(&wait_on_floppy_select); +} + +/* + * floppy-change is never called from an interrupt, so we can relax a bit + * here, sleep etc. Note that floppy-on tries to set current_DOR to point + * to the desired drive, but it will probably not survive the sleep if + * several floppies are used at the same time: thus the loop. + */ +int floppy_change(unsigned int nr) +{ +repeat: + floppy_on(nr); + while ((current_DOR & 3) != nr && selected) + interruptible_sleep_on(&wait_on_floppy_select); + if ((current_DOR & 3) != nr) + goto repeat; + if (inb(FD_DIR) & 0x80) { + floppy_off(nr); + return 1; + } + floppy_off(nr); + return 0; +} + +#define copy_buffer(from,to) \ +__asm__("cld ; rep ; movsl" \ + ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \ + ) + +static void setup_DMA(void) +{ + long addr = (long) CURRENT->buffer; + + cli(); + if (addr >= 0x100000) { + addr = (long) tmp_floppy_area; + if (command == FD_WRITE) + copy_buffer(CURRENT->buffer,tmp_floppy_area); + } +/* mask DMA 2 */ + immoutb_p(4|2,10); +/* output command byte. I don't know why, but everyone (minix, */ +/* sanches & canton) output this twice, first to 12 then to 11 */ + __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t" + "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:":: + "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE))); +/* 8 low bits of addr */ + immoutb_p(addr,4); + addr >>= 8; +/* bits 8-15 of addr */ + immoutb_p(addr,4); + addr >>= 8; +/* bits 16-19 of addr */ + immoutb_p(addr,0x81); +/* low 8 bits of count-1 (1024-1=0x3ff) */ + immoutb_p(0xff,5); +/* high 8 bits of count-1 */ + immoutb_p(3,5); +/* activate DMA 2 */ + immoutb_p(0|2,10); + sti(); +} + +static void output_byte(char byte) +{ + int counter; + unsigned char status; + + if (reset) + return; + for(counter = 0 ; counter < 10000 ; counter++) { + status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR); + if (status == STATUS_READY) { + outb(byte,FD_DATA); + return; + } + } + reset = 1; + printk("Unable to send byte to FDC\n\r"); +} + +static int result(void) +{ + int i = 0, counter, status; + + if (reset) + return -1; + for (counter = 0 ; counter < 10000 ; counter++) { + status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY); + if (status == STATUS_READY) + return i; + if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) { + if (i >= MAX_REPLIES) + break; + reply_buffer[i++] = inb_p(FD_DATA); + } + } + reset = 1; + printk("Getstatus times out\n\r"); + return -1; +} + +static void bad_flp_intr(void) +{ + CURRENT->errors++; + if (CURRENT->errors > MAX_ERRORS) { + floppy_deselect(current_drive); + end_request(0); + } + if (CURRENT->errors > MAX_ERRORS/2) + reset = 1; + else + recalibrate = 1; +} + +/* + * Ok, this interrupt is called after a DMA read/write has succeeded, + * so we check the results, and copy any buffers. + */ +static void rw_interrupt(void) +{ + if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) { + if (ST1 & 0x02) { + printk("Drive %d is write protected\n\r",current_drive); + floppy_deselect(current_drive); + end_request(0); + } else + bad_flp_intr(); + do_fd_request(); + return; + } + if (command == FD_READ && (unsigned long)(CURRENT->buffer) >= 0x100000) + copy_buffer(tmp_floppy_area,CURRENT->buffer); + floppy_deselect(current_drive); + end_request(1); + do_fd_request(); +} + +inline void setup_rw_floppy(void) +{ + setup_DMA(); + do_floppy = rw_interrupt; + output_byte(command); + output_byte(head<<2 | current_drive); + output_byte(track); + output_byte(head); + output_byte(sector); + output_byte(2); /* sector size = 512 */ + output_byte(floppy->sect); + output_byte(floppy->gap); + output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ + if (reset) + do_fd_request(); +} + +/* + * This is the routine called after every seek (or recalibrate) interrupt + * from the floppy controller. Note that the "unexpected interrupt" routine + * also does a recalibrate, but doesn't come here. + */ +static void seek_interrupt(void) +{ +/* sense drive status */ + output_byte(FD_SENSEI); + if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) { + bad_flp_intr(); + do_fd_request(); + return; + } + current_track = ST1; + setup_rw_floppy(); +} + +/* + * This routine is called when everything should be correctly set up + * for the transfer (ie floppy motor is on and the correct floppy is + * selected). + */ +static void transfer(void) +{ + if (cur_spec1 != floppy->spec1) { + cur_spec1 = floppy->spec1; + output_byte(FD_SPECIFY); + output_byte(cur_spec1); /* hut etc */ + output_byte(6); /* Head load time =6ms, DMA */ + } + if (cur_rate != floppy->rate) + outb_p(cur_rate = floppy->rate,FD_DCR); + if (reset) { + do_fd_request(); + return; + } + if (!seek) { + setup_rw_floppy(); + return; + } + do_floppy = seek_interrupt; + if (seek_track) { + output_byte(FD_SEEK); + output_byte(head<<2 | current_drive); + output_byte(seek_track); + } else { + output_byte(FD_RECALIBRATE); + output_byte(head<<2 | current_drive); + } + if (reset) + do_fd_request(); +} + +/* + * Special case - used after a unexpected interrupt (or reset) + */ +static void recal_interrupt(void) +{ + output_byte(FD_SENSEI); + if (result()!=2 || (ST0 & 0xE0) == 0x60) + reset = 1; + else + recalibrate = 0; + do_fd_request(); +} + +void unexpected_floppy_interrupt(void) +{ + output_byte(FD_SENSEI); + if (result()!=2 || (ST0 & 0xE0) == 0x60) + reset = 1; + else + recalibrate = 1; +} + +static void recalibrate_floppy(void) +{ + recalibrate = 0; + current_track = 0; + current_drive = 1; /* by wyj, ?? */ + do_floppy = recal_interrupt; + output_byte(FD_RECALIBRATE); + output_byte(head<<2 | current_drive); + if (reset) + do_fd_request(); +} + +static void reset_interrupt(void) +{ + output_byte(FD_SENSEI); + (void) result(); + output_byte(FD_SPECIFY); + output_byte(cur_spec1); /* hut etc */ + output_byte(6); /* Head load time =6ms, DMA */ + do_fd_request(); +} + +/* + * reset is done by pulling bit 2 of DOR low for a while. + */ +static void reset_floppy(void) +{ + int i; + + reset = 0; + cur_spec1 = -1; + cur_rate = -1; + recalibrate = 1; + printk("Reset-floppy called\n\r"); + cli(); + do_floppy = reset_interrupt; + outb_p(current_DOR & ~0x04,FD_DOR); + for (i=0 ; i<100 ; i++) + __asm__("nop"); + outb(current_DOR,FD_DOR); + sti(); +} + +static void floppy_on_interrupt(void) +{ +/* We cannot do a floppy-select, as that might sleep. We just force it */ + selected = 1; + if (current_drive != (current_DOR & 3)) { + current_DOR &= 0xFC; + current_DOR |= current_drive; + outb(current_DOR,FD_DOR); + add_timer(2,&transfer); + } else + transfer(); +} + +void do_fd_request(void) +{ + unsigned int block; + + seek = 0; + if (reset) { + reset_floppy(); + return; + } + if (recalibrate) { + recalibrate_floppy(); + return; + } + INIT_REQUEST; + floppy = (MINOR(CURRENT->dev)>>2) + floppy_type; + if (current_drive != CURRENT_DEV) + seek = 1; + current_drive = CURRENT_DEV; + block = CURRENT->sector; + if (block+2 > floppy->size) { + end_request(0); + goto repeat; + } + sector = block % floppy->sect; + block /= floppy->sect; + head = block % floppy->head; + track = block / floppy->head; + seek_track = track << floppy->stretch; + if (seek_track != current_track) + seek = 1; + sector++; + if (CURRENT->cmd == READ) + command = FD_READ; + else if (CURRENT->cmd == WRITE) + command = FD_WRITE; + else + panic("do_fd_request: unknown command"); + add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt); +} + +void floppy_init(void) +{ + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + set_trap_gate(0x26,&floppy_interrupt); + outb(inb_p(0x21)&~0x40,0x21); +} diff --git a/4/linux/kernel/blk_drv/hd.c b/4/linux/kernel/blk_drv/hd.c new file mode 100644 index 0000000..c0e908f --- /dev/null +++ b/4/linux/kernel/blk_drv/hd.c @@ -0,0 +1,351 @@ +/* + * linux/kernel/hd.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This is the low-level hd interrupt support. It traverses the + * request-list, using interrupts to jump between functions. As + * all the functions are called within interrupts, we may not + * sleep. Special care is recommended. + * + * modified by Drew Eckhardt to check nr of hd's from the CMOS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR 3 +#include "blk.h" + +#define CMOS_READ(addr) ({ \ +outb_p(0x80|addr,0x70); \ +inb_p(0x71); \ +}) + +/* Max read/write errors/sector */ +#define MAX_ERRORS 7 +#define MAX_HD 2 + +static void recal_intr(void); + +static int recalibrate = 0; /* 1, wen */ +static int reset = 0; + +/* + * This struct defines the HD's and their types. + */ +struct hd_i_struct { + int head,sect,cyl,wpcom,lzone,ctl; + }; +#ifdef HD_TYPE +struct hd_i_struct hd_info[] = { HD_TYPE }; +#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) +#else +struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; +static int NR_HD = 0; +#endif + +static struct hd_struct { + long start_sect; + long nr_sects; +} hd[5*MAX_HD]={{0,0},}; + +#define port_read(port,buf,nr) \ +__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr)) + +#define port_write(port,buf,nr) \ +__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr)) + +extern void hd_interrupt(void); +extern void rd_load(void); + +/* This may be used only once, enforced by 'static int callable' */ +int sys_setup(void * BIOS) +{ + static int callable = 1; + int i,drive; + unsigned char cmos_disks; + struct partition *p; + struct buffer_head * bh; + + if (!callable) + return -1; + callable = 0; +#ifndef HD_TYPE + for (drive=0 ; drive<2 ; drive++) { + hd_info[drive].cyl = *(unsigned short *) BIOS; + hd_info[drive].head = *(unsigned char *) (2+BIOS); + hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); + hd_info[drive].ctl = *(unsigned char *) (8+BIOS); + hd_info[drive].lzone = *(unsigned short *) (12+BIOS); + hd_info[drive].sect = *(unsigned char *) (14+BIOS); + BIOS += 16; + } + if (hd_info[1].cyl) + NR_HD=2; + else + NR_HD=1; +#endif + for (i=0 ; i are the primary drives in the system, and + the ones reflected as drive 1 or 2. + + The first drive is stored in the high nibble of CMOS + byte 0x12, the second in the low nibble. This will be + either a 4 bit drive type or 0xf indicating use byte 0x19 + for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. + + Needless to say, a non-zero value means we have + an AT controller hard disk for that drive. + + + */ + + if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) + if (cmos_disks & 0x0f) + NR_HD = 2; + else + NR_HD = 1; + else + NR_HD = 0; + for (i = NR_HD ; i < 2 ; i++) { + hd[i*5].start_sect = 0; + hd[i*5].nr_sects = 0; + } + for (drive=0 ; driveb_data[510] != 0x55 || (unsigned char) + bh->b_data[511] != 0xAA) { + printk("Bad partition table on drive %d\n\r",drive); + panic(""); + } + p = 0x1BE + (void *)bh->b_data; + for (i=1;i<5;i++,p++) { + hd[i+5*drive].start_sect = p->start_sect; + hd[i+5*drive].nr_sects = p->nr_sects; + } + brelse(bh); + } + if (NR_HD) + printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); + rd_load(); + mount_root(); + return (0); +} + +static int controller_ready(void) +{ + /* int retries=10000; */ + int retries=100000; + + /* while (--retries && (inb_p(HD_STATUS)&0xc0)!=0x40); */ + while (--retries && (inb_p(HD_STATUS)&0x80)); + return (retries); +} + +static int win_result(void) +{ + int i=inb_p(HD_STATUS); + + if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) + == (READY_STAT | SEEK_STAT)) + return(0); /* ok */ + if (i&1) i=inb(HD_ERROR); + return (1); +} + +static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, + unsigned int head,unsigned int cyl,unsigned int cmd, + void (*intr_addr)(void)) +{ + register int port asm("dx"); + + if (drive>1 || head>15) + panic("Trying to write bad sector"); + if (!controller_ready()) + panic("HD controller not ready"); + do_hd = intr_addr; + outb_p(hd_info[drive].ctl,HD_CMD); + port=HD_DATA; + outb_p(hd_info[drive].wpcom>>2,++port); + outb_p(nsect,++port); + outb_p(sect,++port); + outb_p(cyl,++port); + outb_p(cyl>>8,++port); + outb_p(0xA0|(drive<<4)|head,++port); + outb(cmd,++port); +} + +static int drive_busy(void) +{ + unsigned int i; + + for (i = 0; i < 10000; i++) + if (READY_STAT == (inb_p(HD_STATUS) & (BUSY_STAT|READY_STAT))) + break; + i = inb(HD_STATUS); + i &= BUSY_STAT | READY_STAT | SEEK_STAT; + if (i == READY_STAT | SEEK_STAT) + return(0); + printk("HD controller times out\n\r"); + return(1); +} + +static void reset_controller(void) +{ + int i; + + outb(4,HD_CMD); + for(i = 0; i < 100; i++) nop(); + outb(hd_info[0].ctl & 0x0f ,HD_CMD); + if (drive_busy()) + printk("HD-controller still busy\n\r"); + if ((i = inb(HD_ERROR)) != 1) + printk("HD-controller reset failed: %02x\n\r",i); +} + +static void reset_hd(int nr) +{ + reset_controller(); + hd_out(nr,hd_info[nr].sect,hd_info[nr].sect,hd_info[nr].head-1, + hd_info[nr].cyl,WIN_SPECIFY,&recal_intr); +} + +void unexpected_hd_interrupt(void) +{ + printk("Unexpected HD interrupt\n\r"); +} + +static void bad_rw_intr(void) +{ + if (++CURRENT->errors >= MAX_ERRORS) + end_request(0); + if (CURRENT->errors > MAX_ERRORS/2) + reset = 1; +} + +static void read_intr(void) +{ + if (win_result()) { + bad_rw_intr(); + do_hd_request(); + return; + } + port_read(HD_DATA,CURRENT->buffer,256); + CURRENT->errors = 0; + CURRENT->buffer += 512; + CURRENT->sector++; + if (--CURRENT->nr_sectors) { + do_hd = &read_intr; + return; + } + end_request(1); + do_hd_request(); +} + +static void write_intr(void) +{ + if (win_result()) { + bad_rw_intr(); + do_hd_request(); + return; + } + if (--CURRENT->nr_sectors) { + CURRENT->sector++; + CURRENT->buffer += 512; + do_hd = &write_intr; + port_write(HD_DATA,CURRENT->buffer,256); + return; + } + end_request(1); + do_hd_request(); +} + +static void recal_intr(void) +{ + if (win_result()) + bad_rw_intr(); + do_hd_request(); +} + +void do_hd_request(void) +{ + int i,r; + unsigned int block,dev; + unsigned int sec,head,cyl; + unsigned int nsect; + + INIT_REQUEST; + dev = MINOR(CURRENT->dev); + block = CURRENT->sector; + if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) { + end_request(0); + goto repeat; + } + block += hd[dev].start_sect; + dev /= 5; + __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), + "r" (hd_info[dev].sect)); + __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), + "r" (hd_info[dev].head)); + sec++; + nsect = CURRENT->nr_sectors; + if (reset) { + reset = 0; + recalibrate = 1; + reset_hd(CURRENT_DEV); + return; + } + if (recalibrate) { + recalibrate = 0; + hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0, + WIN_RESTORE,&recal_intr); + return; + } + if (CURRENT->cmd == WRITE) { + hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr); + for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) + /* nothing */ ; + if (!r) { + bad_rw_intr(); + goto repeat; + } + port_write(HD_DATA,CURRENT->buffer,256); + } else if (CURRENT->cmd == READ) { + hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); + } else + panic("unknown hd-command"); +} + +void hd_init(void) +{ + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + set_intr_gate(0x2E,&hd_interrupt); + outb_p(inb_p(0x21)&0xfb,0x21); + outb(inb_p(0xA1)&0xbf,0xA1); +} diff --git a/4/linux/kernel/blk_drv/ll_rw_blk.c b/4/linux/kernel/blk_drv/ll_rw_blk.c new file mode 100644 index 0000000..f57d998 --- /dev/null +++ b/4/linux/kernel/blk_drv/ll_rw_blk.c @@ -0,0 +1,165 @@ +/* + * linux/kernel/blk_dev/ll_rw.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This handles all read/write requests to block devices + */ +#include +#include +#include +#include + +#include "blk.h" + +/* + * The request-struct contains all necessary data + * to load a nr of sectors into memory + */ +struct request request[NR_REQUEST]; + +/* + * used to wait on when there are no free requests + */ +struct task_struct * wait_for_request = NULL; + +/* blk_dev_struct is: + * do_request-address + * next-request + */ +struct blk_dev_struct blk_dev[NR_BLK_DEV] = { + { NULL, NULL }, /* no_dev */ + { NULL, NULL }, /* dev mem */ + { NULL, NULL }, /* dev fd */ + { NULL, NULL }, /* dev hd */ + { NULL, NULL }, /* dev ttyx */ + { NULL, NULL }, /* dev tty */ + { NULL, NULL } /* dev lp */ +}; + +static inline void lock_buffer(struct buffer_head * bh) +{ + cli(); + while (bh->b_lock) + sleep_on(&bh->b_wait); + bh->b_lock=1; + sti(); +} + +static inline void unlock_buffer(struct buffer_head * bh) +{ + if (!bh->b_lock) + printk("ll_rw_block.c: buffer not locked\n\r"); + bh->b_lock = 0; + wake_up(&bh->b_wait); +} + +/* + * add-request adds a request to the linked list. + * It disables interrupts so that it can muck with the + * request-lists in peace. + */ +static void add_request(struct blk_dev_struct * dev, struct request * req) +{ + struct request * tmp; + + req->next = NULL; + cli(); + if (req->bh) + req->bh->b_dirt = 0; + if (!(tmp = dev->current_request)) { + dev->current_request = req; + sti(); + (dev->request_fn)(); + return; + } + for ( ; tmp->next ; tmp=tmp->next) + if ((IN_ORDER(tmp,req) || + !IN_ORDER(tmp,tmp->next)) && + IN_ORDER(req,tmp->next)) + break; + req->next=tmp->next; + tmp->next=req; + sti(); +} + +static void make_request(int major,int rw, struct buffer_head * bh) +{ + struct request * req; + int rw_ahead; + +/* WRITEA/READA is special case - it is not really needed, so if the */ +/* buffer is locked, we just forget about it, else it's a normal read */ + if (rw_ahead = (rw == READA || rw == WRITEA)) { + if (bh->b_lock) + return; + if (rw == READA) + rw = READ; + else + rw = WRITE; + } + if (rw!=READ && rw!=WRITE) + panic("Bad block dev command, must be R/W/RA/WA"); + lock_buffer(bh); + if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { + unlock_buffer(bh); + return; + } +repeat: +/* we don't allow the write-requests to fill up the queue completely: + * we want some room for reads: they take precedence. The last third + * of the requests are only for reads. + */ + if (rw == READ) + req = request+NR_REQUEST; + else + req = request+((NR_REQUEST*2)/3); +/* find an empty request */ + while (--req >= request) + if (req->dev<0) + break; +/* if none found, sleep on new requests: check for rw_ahead */ + if (req < request) { + if (rw_ahead) { + unlock_buffer(bh); + return; + } + sleep_on(&wait_for_request); + goto repeat; + } +/* fill up the request-info, and add it to the queue */ + req->dev = bh->b_dev; + req->cmd = rw; + req->errors=0; + req->sector = bh->b_blocknr<<1; + req->nr_sectors = 2; + req->buffer = bh->b_data; + req->waiting = NULL; + req->bh = bh; + req->next = NULL; + add_request(major+blk_dev,req); +} + +void ll_rw_block(int rw, struct buffer_head * bh) +{ + unsigned int major; + + if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || + !(blk_dev[major].request_fn)) { + printk("Trying to read nonexistent block-device\n\r"); + return; + } + make_request(major,rw,bh); +} + +void blk_dev_init(void) +{ + int i; + + for (i=0 ; i + +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR 1 +#include "blk.h" + +char *rd_start; +int rd_length = 0; + +void do_rd_request(void) +{ + int len; + char *addr; + + INIT_REQUEST; + addr = rd_start + (CURRENT->sector << 9); + len = CURRENT->nr_sectors << 9; + if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) { + end_request(0); + goto repeat; + } + if (CURRENT-> cmd == WRITE) { + (void ) memcpy(addr, + CURRENT->buffer, + len); + } else if (CURRENT->cmd == READ) { + (void) memcpy(CURRENT->buffer, + addr, + len); + } else + panic("unknown ramdisk-command"); + end_request(1); + goto repeat; +} + +/* + * Returns amount of memory which needs to be reserved. + */ +long rd_init(long mem_start, int length) +{ + int i; + char *cp; + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + rd_start = (char *) mem_start; + rd_length = length; + cp = rd_start; + for (i=0; i < length; i++) + *cp++ = '\0'; + return(length); +} + +/* + * If the root device is the ram disk, try to load it. + * In order to do this, the root device is originally set to the + * floppy, and we later change it to be ram disk. + */ +void rd_load(void) +{ + struct buffer_head *bh; + struct super_block s; + int block = 256; /* Start at block 256 */ + int i = 1; + int nblocks; + char *cp; /* Move pointer */ + + if (!rd_length) + return; + printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length, + (int) rd_start); + if (MAJOR(ROOT_DEV) != 2) + return; + bh = breada(ROOT_DEV,block+1,block,block+2,-1); + if (!bh) { + printk("Disk error while looking for ramdisk!\n"); + return; + } + __asm__ volatile ("cld"); /* by wyj */ + *((struct d_super_block *) &s) = *((struct d_super_block *) bh->b_data); + brelse(bh); + if (s.s_magic != SUPER_MAGIC) + /* No ram disk image present, assume normal floppy boot */ + return; + nblocks = s.s_nzones << s.s_log_zone_size; + if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) { + printk("Ram disk image too big! (%d blocks, %d avail)\n", + nblocks, rd_length >> BLOCK_SIZE_BITS); + return; + } + printk("Loading %d bytes into ram disk... 0000k", + nblocks << BLOCK_SIZE_BITS); + cp = rd_start; + while (nblocks) { + if (nblocks > 2) + bh = breada(ROOT_DEV, block, block+1, block+2, -1); + else + bh = bread(ROOT_DEV, block); + if (!bh) { + printk("I/O error on block %d, aborting load\n", + block); + return; + } + (void) memcpy(cp, bh->b_data, BLOCK_SIZE); + brelse(bh); + printk("\010\010\010\010\010%4dk",i); + cp += BLOCK_SIZE; + block++; + nblocks--; + i++; + } + printk("\010\010\010\010\010done \n"); + ROOT_DEV=0x0101; +} diff --git a/4/linux/kernel/chr_drv/Makefile b/4/linux/kernel/chr_drv/Makefile new file mode 100644 index 0000000..9d4d8d1 --- /dev/null +++ b/4/linux/kernel/chr_drv/Makefile @@ -0,0 +1,68 @@ +# +# Makefile for the FREAX-kernel character device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +AR =ar +AS =as +LD =ld +LDFLAGS =-s -x +CC =gcc -march=i386 +CFLAGS =-w -g -fstrength-reduce -fomit-frame-pointer -mcld \ + -finline-functions -nostdinc -fno-stack-protector -I../../include +CPP =gcc -E -nostdinc -I../../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = tty_io.o console.o keyboard.2.o serial.o rs_io.o \ + tty_ioctl.o + +chr_drv.a: $(OBJS) + $(AR) rcs chr_drv.a $(OBJS) + sync + +keyboard.2.s: keyboard.S ../../include/linux/config.h + $(CPP) -traditional keyboard.S -o keyboard.2.s + +clean: + rm -f core *.o *.a tmp_make keyboard.2.s + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +console.s console.o : console.c ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \ + ../../include/linux/tty.h ../../include/termios.h ../../include/asm/io.h \ + ../../include/asm/system.h +serial.s serial.o : serial.c ../../include/linux/tty.h ../../include/termios.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/signal.h ../../include/asm/system.h ../../include/asm/io.h +tty_io.s tty_io.o : tty_io.c ../../include/ctype.h ../../include/errno.h \ + ../../include/signal.h ../../include/sys/types.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/linux/mm.h ../../include/linux/tty.h \ + ../../include/termios.h ../../include/asm/segment.h \ + ../../include/asm/system.h +tty_ioctl.s tty_ioctl.o : tty_ioctl.c ../../include/errno.h ../../include/termios.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/signal.h ../../include/linux/kernel.h \ + ../../include/linux/tty.h ../../include/asm/io.h \ + ../../include/asm/segment.h ../../include/asm/system.h diff --git a/4/linux/kernel/chr_drv/console.c b/4/linux/kernel/chr_drv/console.c new file mode 100644 index 0000000..a12ffbf --- /dev/null +++ b/4/linux/kernel/chr_drv/console.c @@ -0,0 +1,710 @@ +/* + * linux/kernel/console.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * console.c + * + * This module implements the console io functions + * 'void con_init(void)' + * 'void con_write(struct tty_queue * queue)' + * Hopefully this will be a rather complete VT102 implementation. + * + * Beeping thanks to John T Kohl. + */ + +/* + * NOTE!!! We sometimes disable and enable interrupts for a short while + * (to put a word in video IO), but this will work even for keyboard + * interrupts. We know interrupts aren't enabled when getting a keyboard + * interrupt, as we use trap-gates. Hopefully all is well. + */ + +/* + * Code to check for different video-cards mostly by Galen Hunt, + * + */ + +#include +#include +#include +#include + +/* + * These are set up by the setup-routine at boot-time: + */ + +#define ORIG_X (*(unsigned char *)0x90000) +#define ORIG_Y (*(unsigned char *)0x90001) +#define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004) +#define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff) +#define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8) +#define ORIG_VIDEO_LINES (25) +#define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008) +#define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a) +#define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c) + +#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ +#define VIDEO_TYPE_CGA 0x11 /* CGA Display */ +#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ +#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */ + +#define NPAR 16 + +extern void keyboard_interrupt(void); + +static unsigned char video_type; /* Type of display being used */ +static unsigned long video_num_columns; /* Number of text columns */ +static unsigned long video_size_row; /* Bytes per row */ +static unsigned long video_num_lines; /* Number of test lines */ +static unsigned char video_page; /* Initial video page */ +static unsigned long video_mem_start; /* Start of video RAM */ +static unsigned long video_mem_end; /* End of video RAM (sort of) */ +static unsigned short video_port_reg; /* Video register select port */ +static unsigned short video_port_val; /* Video register value port */ +static unsigned short video_erase_char; /* Char+Attrib to erase with */ + +static unsigned long origin; /* Used for EGA/VGA fast scroll */ +static unsigned long scr_end; /* Used for EGA/VGA fast scroll */ +static unsigned long pos; +static unsigned long x,y; +static unsigned long top,bottom; +static unsigned long state=0; +static unsigned long npar,par[NPAR]; +static unsigned long ques=0; +static unsigned char attr=0x07; + +static void sysbeep(void); + +/* + * this is what the terminal answers to a ESC-Z or csi0c + * query (= vt100 response). + */ +#define RESPONSE "\033[?1;2c" + +/* NOTE! gotoxy thinks x==video_num_columns is ok */ +static inline void gotoxy(unsigned int new_x,unsigned int new_y) +{ + if (new_x > video_num_columns || new_y >= video_num_lines) + return; + x=new_x; + y=new_y; + pos=origin + y*video_size_row + (x<<1); +} + +static inline void set_origin(void) +{ + cli(); + outb_p(12, video_port_reg); + outb_p(0xff&((origin-video_mem_start)>>9), video_port_val); + outb_p(13, video_port_reg); + outb_p(0xff&((origin-video_mem_start)>>1), video_port_val); + sti(); +} + +static void scrup(void) +{ + if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM) + { + if (!top && bottom == video_num_lines) { + origin += video_size_row; + pos += video_size_row; + scr_end += video_size_row; + if (scr_end > video_mem_end) { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl video_num_columns,%1\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((video_num_lines-1)*video_num_columns>>1), + "D" (video_mem_start), + "S" (origin) + ); + scr_end -= origin-video_mem_start; + pos -= origin-video_mem_start; + origin = video_mem_start; + } else { + __asm__("cld\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" (video_num_columns), + "D" (scr_end-video_size_row) + ); + } + set_origin(); + } else { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*top), + "S" (origin+video_size_row*(top+1)) + ); + } + } + else /* Not EGA/VGA */ + { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*top), + "S" (origin+video_size_row*(top+1)) + ); + } +} + +static void scrdown(void) +{ + if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM) + { + __asm__("std\n\t" + "rep\n\t" + "movsl\n\t" + "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*bottom-4), + "S" (origin+video_size_row*(bottom-1)-4) + ); + } + else /* Not EGA/VGA */ + { + __asm__("std\n\t" + "rep\n\t" + "movsl\n\t" + "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*bottom-4), + "S" (origin+video_size_row*(bottom-1)-4) + ); + } +} + +static void lf(void) +{ + if (y+1top) { + y--; + pos -= video_size_row; + return; + } + scrdown(); +} + +static void cr(void) +{ + pos -= x<<1; + x=0; +} + +static void del(void) +{ + if (x) { + pos -= 2; + x--; + *(unsigned short *)pos = video_erase_char; + } +} + +static void csi_J(int par) +{ + long count __asm__("cx"); + long start __asm__("di"); + + switch (par) { + case 0: /* erase from cursor to end of display */ + count = (scr_end-pos)>>1; + start = pos; + break; + case 1: /* erase from start to cursor */ + count = (pos-origin)>>1; + start = origin; + break; + case 2: /* erase whole display */ + count = video_num_columns * video_num_lines; + start = origin; + break; + default: + return; + } + __asm__("cld\n\t" + "rep\n\t" + "stosw\n\t" + ::"c" (count), + "D" (start),"a" (video_erase_char) + ); +} + +static void csi_K(int par) +{ + long count __asm__("cx"); + long start __asm__("di"); + + switch (par) { + case 0: /* erase from cursor to end of line */ + if (x>=video_num_columns) + return; + count = video_num_columns-x; + start = pos; + break; + case 1: /* erase from start of line to cursor */ + start = pos - (x<<1); + count = (x>9), video_port_val); + outb_p(15, video_port_reg); + outb_p(0xff&((pos-video_mem_start)>>1), video_port_val); + sti(); +} + +static void respond(struct tty_struct * tty) +{ + char * p = RESPONSE; + + cli(); + while (*p) { + PUTCH(*p,tty->read_q); + p++; + } + sti(); + copy_to_cooked(tty); +} + +static void insert_char(void) +{ + int i=x; + unsigned short tmp, old = video_erase_char; + unsigned short * p = (unsigned short *) pos; + + while (i++=video_num_columns) + return; + i = x; + while (++i < video_num_columns) { + *p = *(p+1); + p++; + } + *p = video_erase_char; +} + +static void delete_line(void) +{ + int oldtop,oldbottom; + + oldtop=top; + oldbottom=bottom; + top=y; + bottom = video_num_lines; + scrup(); + top=oldtop; + bottom=oldbottom; +} + +static void csi_at(unsigned int nr) +{ + if (nr > video_num_columns) + nr = video_num_columns; + else if (!nr) + nr = 1; + while (nr--) + insert_char(); +} + +static void csi_L(unsigned int nr) +{ + if (nr > video_num_lines) + nr = video_num_lines; + else if (!nr) + nr = 1; + while (nr--) + insert_line(); +} + +static void csi_P(unsigned int nr) +{ + if (nr > video_num_columns) + nr = video_num_columns; + else if (!nr) + nr = 1; + while (nr--) + delete_char(); +} + +static void csi_M(unsigned int nr) +{ + if (nr > video_num_lines) + nr = video_num_lines; + else if (!nr) + nr=1; + while (nr--) + delete_line(); +} + +static int saved_x=0; +static int saved_y=0; + +static void save_cur(void) +{ + saved_x=x; + saved_y=y; +} + +static void restore_cur(void) +{ + gotoxy(saved_x, saved_y); +} + +void con_write(struct tty_struct * tty) +{ + int nr; + char c; + + nr = CHARS(tty->write_q); + while (nr--) { + GETCH(tty->write_q,c); + switch(state) { + case 0: + if (c>31 && c<127) { + if (x>=video_num_columns) { + x -= video_num_columns; + pos -= video_size_row; + lf(); + } + __asm__("movb attr,%%ah\n\t" + "movw %%ax,%1\n\t" + ::"a" (c),"m" (*(short *)pos) + ); + pos += 2; + x++; + } else if (c==27) + state=1; + else if (c==10 || c==11 || c==12) + lf(); + else if (c==13) + cr(); + else if (c==ERASE_CHAR(tty)) + del(); + else if (c==8) { + if (x) { + x--; + pos -= 2; + } + } else if (c==9) { + c=8-(x&7); + x += c; + pos += c<<1; + if (x>video_num_columns) { + x -= video_num_columns; + pos -= video_size_row; + lf(); + } + c=9; + } else if (c==7) + sysbeep(); + break; + case 1: + state=0; + if (c=='[') + state=2; + else if (c=='E') + gotoxy(0,y+1); + else if (c=='M') + ri(); + else if (c=='D') + lf(); + else if (c=='Z') + respond(tty); + else if (x=='7') + save_cur(); + else if (x=='8') + restore_cur(); + break; + case 2: + for(npar=0;npar='0' && c<='9') { + par[npar]=10*par[npar]+c-'0'; + break; + } else state=4; + case 4: + state=0; + switch(c) { + case 'G': case '`': + if (par[0]) par[0]--; + gotoxy(par[0],y); + break; + case 'A': + if (!par[0]) par[0]++; + gotoxy(x,y-par[0]); + break; + case 'B': case 'e': + if (!par[0]) par[0]++; + gotoxy(x,y+par[0]); + break; + case 'C': case 'a': + if (!par[0]) par[0]++; + gotoxy(x+par[0],y); + break; + case 'D': + if (!par[0]) par[0]++; + gotoxy(x-par[0],y); + break; + case 'E': + if (!par[0]) par[0]++; + gotoxy(0,y+par[0]); + break; + case 'F': + if (!par[0]) par[0]++; + gotoxy(0,y-par[0]); + break; + case 'd': + if (par[0]) par[0]--; + gotoxy(x,par[0]); + break; + case 'H': case 'f': + if (par[0]) par[0]--; + if (par[1]) par[1]--; + gotoxy(par[1],par[0]); + break; + case 'J': + csi_J(par[0]); + break; + case 'K': + csi_K(par[0]); + break; + case 'L': + csi_L(par[0]); + break; + case 'M': + csi_M(par[0]); + break; + case 'P': + csi_P(par[0]); + break; + case '@': + csi_at(par[0]); + break; + case 'm': + csi_m(); + break; + case 'r': + if (par[0]) par[0]--; + if (!par[1]) par[1] = video_num_lines; + if (par[0] < par[1] && + par[1] <= video_num_lines) { + top=par[0]; + bottom=par[1]; + } + break; + case 's': + save_cur(); + break; + case 'u': + restore_cur(); + break; + } + } + } + set_cursor(); +} + +/* + * void con_init(void); + * + * This routine initalizes console interrupts, and does nothing + * else. If you want the screen to clear, call tty_write with + * the appropriate escape-sequece. + * + * Reads the information preserved by setup.s to determine the current display + * type and sets everything accordingly. + */ +void con_init(void) +{ + register unsigned char a; + char *display_desc = "????"; + char *display_ptr; + + video_num_columns = ORIG_VIDEO_COLS; + video_size_row = video_num_columns * 2; + video_num_lines = ORIG_VIDEO_LINES; + video_page = ORIG_VIDEO_PAGE; + video_erase_char = 0x0720; + + if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */ + { + video_mem_start = 0xb0000; + video_port_reg = 0x3b4; + video_port_val = 0x3b5; + if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) + { + video_type = VIDEO_TYPE_EGAM; + video_mem_end = 0xb8000; + display_desc = "EGAm"; + } + else + { + video_type = VIDEO_TYPE_MDA; + video_mem_end = 0xb2000; + display_desc = "*MDA"; + } + } + else /* If not, it is color. */ + { + video_mem_start = 0xb8000; + video_port_reg = 0x3d4; + video_port_val = 0x3d5; + if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) + { + video_type = VIDEO_TYPE_EGAC; + video_mem_end = 0xbc000; + display_desc = "EGAc"; + } + else + { + video_type = VIDEO_TYPE_CGA; + video_mem_end = 0xba000; + display_desc = "*CGA"; + } + } + + /* Let the user known what kind of display driver we are using */ + + display_ptr = ((char *)video_mem_start) + video_size_row - 8; + while (*display_desc) + { + *display_ptr++ = *display_desc++; + display_ptr++; + } + + /* Initialize the variables used for scrolling (mostly EGA/VGA) */ + + origin = video_mem_start; + scr_end = video_mem_start + video_num_lines * video_size_row; + top = 0; + bottom = video_num_lines; + + gotoxy(ORIG_X,ORIG_Y); + set_trap_gate(0x21,&keyboard_interrupt); + outb_p(inb_p(0x21)&0xfd,0x21); + a=inb_p(0x61); + outb_p(a|0x80,0x61); + outb(a,0x61); +} +/* from bsd-net-2: */ + +void sysbeepstop(void) +{ + /* disable counter 2 */ + outb(inb_p(0x61)&0xFC, 0x61); +} + +int beepcount = 0; + +static void sysbeep(void) +{ + /* enable counter 2 */ + outb_p(inb_p(0x61)|3, 0x61); + /* set command for counter 2, 2 byte write */ + outb_p(0xB6, 0x43); + /* send 0x637 for 750 HZ */ + outb_p(0x37, 0x42); + outb(0x06, 0x42); + /* 1/8 second */ + beepcount = HZ/8; +} diff --git a/4/linux/kernel/chr_drv/keyboard.2.s b/4/linux/kernel/chr_drv/keyboard.2.s new file mode 100644 index 0000000..a348410 --- /dev/null +++ b/4/linux/kernel/chr_drv/keyboard.2.s @@ -0,0 +1,466 @@ +# 1 "keyboard.S" +# 1 "" +# 1 "" +# 1 "keyboard.S" + + + + + + + + + + + + + +# 1 "../../include/linux/config.h" 1 + + + + + + + + + + + + + + + + + + + + + +# 36 "../../include/linux/config.h" + +# 47 "../../include/linux/config.h" + +# 14 "keyboard.S" 2 + +.text +.globl keyboard_interrupt + + + + +size = 1024 + +head = 4 +tail = 8 +proc_list = 12 +buf = 16 + +mode: .byte 0 +leds: .byte 2 +e0: .byte 0 + + + + + + +keyboard_interrupt: + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + push %ds + push %es + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + xor %al,%al + inb $0x60,%al + cmpb $0xe0,%al + je set_e0 + cmpb $0xe1,%al + je set_e1 + call key_table(,%eax,4) + movb $0,e0 +e0_e1: inb $0x61,%al + jmp 1f +1: jmp 1f +1: orb $0x80,%al + jmp 1f +1: jmp 1f +1: outb %al,$0x61 + jmp 1f +1: jmp 1f +1: andb $0x7F,%al + outb %al,$0x61 + movb $0x20,%al + outb %al,$0x20 + pushl $0 + call do_tty_interrupt + addl $4,%esp + pop %es + pop %ds + popl %edx + popl %ecx + popl %ebx + popl %eax + iret +set_e0: movb $1,e0 + jmp e0_e1 +set_e1: movb $2,e0 + jmp e0_e1 + + + + + + +put_queue: + pushl %ecx + pushl %edx + movl table_list,%edx # read-queue for console + movl head(%edx),%ecx +1: movb %al,buf(%edx,%ecx) + incl %ecx + andl $size-1,%ecx + cmpl tail(%edx),%ecx # buffer full - discard everything + je 3f + shrdl $8,%ebx,%eax + je 2f + shrl $8,%ebx + jmp 1b +2: movl %ecx,head(%edx) + movl proc_list(%edx),%ecx + testl %ecx,%ecx + je 3f + movl $0,(%ecx) +3: popl %edx + popl %ecx + ret + +ctrl: movb $0x04,%al + jmp 1f +alt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: orb %al,mode + ret +unctrl: movb $0x04,%al + jmp 1f +unalt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: notb %al + andb %al,mode + ret + +lshift: + orb $0x01,mode + ret +unlshift: + andb $0xfe,mode + ret +rshift: + orb $0x02,mode + ret +unrshift: + andb $0xfd,mode + ret + +caps: testb $0x80,mode + jne 1f + xorb $4,leds + xorb $0x40,mode + orb $0x80,mode +set_leds: + call kb_wait + movb $0xed,%al + outb %al,$0x60 + call kb_wait + movb leds,%al + outb %al,$0x60 + ret +uncaps: andb $0x7f,mode + ret +scroll: + xorb $1,leds + jmp set_leds +num: xorb $2,leds + jmp set_leds + + + + + +cursor: + subb $0x47,%al + jb 1f + cmpb $12,%al + ja 1f + jne cur2 + testb $0x0c,mode + je cur2 + testb $0x30,mode + jne reboot +cur2: cmpb $0x01,e0 + je cur + testb $0x02,leds + je cur + testb $0x03,mode + jne cur + xorl %ebx,%ebx + movb num_table(%eax),%al + jmp put_queue +1: ret + +cur: movb cur_table(%eax),%al + cmpb $'9,%al + ja ok_cur + movb $'~,%ah +ok_cur: shll $16,%eax + movw $0x5b1b,%ax + xorl %ebx,%ebx + jmp put_queue + + + + + +num_table: + .ascii "789 456 1230," + +cur_table: + .ascii "HA5 DGC YB623" + + + + +func: + pushl %eax + pushl %ecx + pushl %edx + call show_stat + popl %edx + popl %ecx + popl %eax + subb $0x3B,%al + jb end_func + cmpb $9,%al + jbe ok_func + subb $18,%al + cmpb $10,%al + jb end_func + cmpb $11,%al + ja end_func +ok_func: + cmpl $4,%ecx + jl end_func + movl func_table(,%eax,4),%eax + xorl %ebx,%ebx + jmp put_queue +end_func: + ret + + + + +func_table: + .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b + .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b + .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b + +# 294 "keyboard.S" + +key_map: + .byte 0,27 + .ascii "1234567890-=" + .byte 127,9 + .ascii "qwertyuiop[]" + .byte 13,0 + .ascii "asdfghjkl;'" + .byte '`,0 + .ascii "\\zxcvbnm,./" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 + .byte '-,0,0,0,'+ + .byte 0,0,0,0,0,0,0 + .byte '< + .fill 10,1,0 + + +shift_map: + .byte 0,27 + .ascii "!@#$%^&*()_+" + .byte 127,9 + .ascii "QWERTYUIOP{}" + .byte 13,0 + .ascii "ASDFGHJKL:\"" + .byte '~,0 + .ascii "|ZXCVBNM<>?" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 + .byte '-,0,0,0,'+ + .byte 0,0,0,0,0,0,0 + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 + .fill 16,1,0 + .byte 0,0,0,0,0 + .byte 0,0,0,0,0,0,0 + .byte '| + .fill 10,1,0 + +# 449 "keyboard.S" + + + + +do_self: + lea alt_map,%ebx + testb $0x20,mode + jne 1f + lea shift_map,%ebx + testb $0x03,mode + jne 1f + lea key_map,%ebx +1: movb (%ebx,%eax),%al + orb %al,%al + je none + testb $0x4c,mode + je 2f + cmpb $'a,%al + jb 2f + cmpb $'},%al + ja 2f + subb $32,%al +2: testb $0x0c,mode + je 3f + cmpb $64,%al + jb 3f + cmpb $64+32,%al + jae 3f + subb $64,%al +3: testb $0x10,mode + je 4f + orb $0x80,%al +4: andl $0xff,%eax + xorl %ebx,%ebx + call put_queue +none: ret + + + + + + +minus: cmpb $1,e0 + jne do_self + movl $'/,%eax + xorl %ebx,%ebx + jmp put_queue + + + + + + +key_table: + .long none,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,ctrl,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,lshift,do_self + .long do_self,do_self,do_self,do_self + .long do_self,do_self,do_self,do_self + .long do_self,minus,rshift,do_self + .long alt,do_self,caps,func + .long func,func,func,func + .long func,func,func,func + .long func,num,scroll,cursor + .long cursor,cursor,do_self,cursor + .long cursor,cursor,do_self,cursor + .long cursor,cursor,cursor,cursor + .long none,none,do_self,func + .long func,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,unctrl,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,unlshift,none + .long none,none,none,none + .long none,none,none,none + .long none,none,unrshift,none + .long unalt,none,uncaps,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + .long none,none,none,none + + + + + +kb_wait: + pushl %eax +1: inb $0x64,%al + testb $0x02,%al + jne 1b + popl %eax + ret + + + + +reboot: + call kb_wait + movw $0x1234,0x472 + movb $0xfc,%al + outb %al,$0x64 +die: jmp die diff --git a/4/linux/kernel/chr_drv/keyboard.S b/4/linux/kernel/chr_drv/keyboard.S new file mode 100644 index 0000000..25210b4 --- /dev/null +++ b/4/linux/kernel/chr_drv/keyboard.S @@ -0,0 +1,588 @@ +/* + * linux/kernel/keyboard.S + * + * (C) 1991 Linus Torvalds + */ + +/* + * Thanks to Alfred Leung for US keyboard patches + * Wolfgang Thiel for German keyboard patches + * Marc Corsini for the French keyboard + */ + +#include + +.text +.globl keyboard_interrupt + +/* + * these are for the keyboard read functions + */ +size = 1024 /* must be a power of two ! And MUST be the same + as in tty_io.c !!!! */ +head = 4 +tail = 8 +proc_list = 12 +buf = 16 + +mode: .byte 0 /* caps, alt, ctrl and shift mode */ +leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */ +e0: .byte 0 + +/* + * con_int is the real interrupt routine that reads the + * keyboard scan-code and converts it into the appropriate + * ascii character(s). + */ +keyboard_interrupt: + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + push %ds + push %es + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + xor %al,%al /* %eax is scan code */ + inb $0x60,%al + cmpb $0xe0,%al + je set_e0 + cmpb $0xe1,%al + je set_e1 + call key_table(,%eax,4) + movb $0,e0 +e0_e1: inb $0x61,%al + jmp 1f +1: jmp 1f +1: orb $0x80,%al + jmp 1f +1: jmp 1f +1: outb %al,$0x61 + jmp 1f +1: jmp 1f +1: andb $0x7F,%al + outb %al,$0x61 + movb $0x20,%al + outb %al,$0x20 + pushl $0 + call do_tty_interrupt + addl $4,%esp + pop %es + pop %ds + popl %edx + popl %ecx + popl %ebx + popl %eax + iret +set_e0: movb $1,e0 + jmp e0_e1 +set_e1: movb $2,e0 + jmp e0_e1 + +/* + * This routine fills the buffer with max 8 bytes, taken from + * %ebx:%eax. (%edx is high). The bytes are written in the + * order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero. + */ +put_queue: + pushl %ecx + pushl %edx + movl table_list,%edx # read-queue for console + movl head(%edx),%ecx +1: movb %al,buf(%edx,%ecx) + incl %ecx + andl $size-1,%ecx + cmpl tail(%edx),%ecx # buffer full - discard everything + je 3f + shrdl $8,%ebx,%eax + je 2f + shrl $8,%ebx + jmp 1b +2: movl %ecx,head(%edx) + movl proc_list(%edx),%ecx + testl %ecx,%ecx + je 3f + movl $0,(%ecx) +3: popl %edx + popl %ecx + ret + +ctrl: movb $0x04,%al + jmp 1f +alt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: orb %al,mode + ret +unctrl: movb $0x04,%al + jmp 1f +unalt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: notb %al + andb %al,mode + ret + +lshift: + orb $0x01,mode + ret +unlshift: + andb $0xfe,mode + ret +rshift: + orb $0x02,mode + ret +unrshift: + andb $0xfd,mode + ret + +caps: testb $0x80,mode + jne 1f + xorb $4,leds + xorb $0x40,mode + orb $0x80,mode +set_leds: + call kb_wait + movb $0xed,%al /* set leds command */ + outb %al,$0x60 + call kb_wait + movb leds,%al + outb %al,$0x60 + ret +uncaps: andb $0x7f,mode + ret +scroll: + xorb $1,leds + jmp set_leds +num: xorb $2,leds + jmp set_leds + +/* + * curosr-key/numeric keypad cursor keys are handled here. + * checking for numeric keypad etc. + */ +cursor: + subb $0x47,%al + jb 1f + cmpb $12,%al + ja 1f + jne cur2 /* check for ctrl-alt-del */ + testb $0x0c,mode + je cur2 + testb $0x30,mode + jne reboot +cur2: cmpb $0x01,e0 /* e0 forces cursor movement */ + je cur + testb $0x02,leds /* not num-lock forces cursor */ + je cur + testb $0x03,mode /* shift forces cursor */ + jne cur + xorl %ebx,%ebx + movb num_table(%eax),%al + jmp put_queue +1: ret + +cur: movb cur_table(%eax),%al + cmpb $'9,%al + ja ok_cur + movb $'~,%ah +ok_cur: shll $16,%eax + movw $0x5b1b,%ax + xorl %ebx,%ebx + jmp put_queue + +#if defined(KBD_FR) +num_table: + .ascii "789 456 1230." +#else +num_table: + .ascii "789 456 1230," +#endif +cur_table: + .ascii "HA5 DGC YB623" + +/* + * this routine handles function keys + */ +func: + pushl %eax + pushl %ecx + pushl %edx + call show_stat + popl %edx + popl %ecx + popl %eax + subb $0x3B,%al + jb end_func + cmpb $9,%al + jbe ok_func + subb $18,%al + cmpb $10,%al + jb end_func + cmpb $11,%al + ja end_func +ok_func: + cmpl $4,%ecx /* check that there is enough room */ + jl end_func + movl func_table(,%eax,4),%eax + xorl %ebx,%ebx + jmp put_queue +end_func: + ret + +/* + * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc. + */ +func_table: + .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b + .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b + .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b + +#if defined(KBD_FINNISH) +key_map: + .byte 0,27 + .ascii "1234567890+'" + .byte 127,9 + .ascii "qwertyuiop}" + .byte 0,13,0 + .ascii "asdfghjkl|{" + .byte 0,0 + .ascii "'zxcvbnm,.-" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + +shift_map: + .byte 0,27 + .ascii "!\"#$%&/()=?`" + .byte 127,9 + .ascii "QWERTYUIOP]^" + .byte 13,0 + .ascii "ASDFGHJKL\\[" + .byte 0,0 + .ascii "*ZXCVBNM;:_" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + +#elif defined(KBD_US) + +key_map: + .byte 0,27 + .ascii "1234567890-=" + .byte 127,9 + .ascii "qwertyuiop[]" + .byte 13,0 + .ascii "asdfghjkl;'" + .byte '`,0 + .ascii "\\zxcvbnm,./" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + + +shift_map: + .byte 0,27 + .ascii "!@#$%^&*()_+" + .byte 127,9 + .ascii "QWERTYUIOP{}" + .byte 13,0 + .ascii "ASDFGHJKL:\"" + .byte '~,0 + .ascii "|ZXCVBNM<>?" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + +#elif defined(KBD_GR) + +key_map: + .byte 0,27 + .ascii "1234567890\\'" + .byte 127,9 + .ascii "qwertzuiop@+" + .byte 13,0 + .ascii "asdfghjkl[]^" + .byte 0,'# + .ascii "yxcvbnm,.-" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + + +shift_map: + .byte 0,27 + .ascii "!\"#$%&/()=?`" + .byte 127,9 + .ascii "QWERTZUIOP\\*" + .byte 13,0 + .ascii "ASDFGHJKL{}~" + .byte 0,'' + .ascii "YXCVBNM;:_" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte '@,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + + +#elif defined(KBD_FR) + +key_map: + .byte 0,27 + .ascii "&{\"'(-}_/@)=" + .byte 127,9 + .ascii "azertyuiop^$" + .byte 13,0 + .ascii "qsdfghjklm|" + .byte '`,0,42 /* coin sup gauche, don't know, [*|mu] */ + .ascii "wxcvbn,;:!" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + +shift_map: + .byte 0,27 + .ascii "1234567890]+" + .byte 127,9 + .ascii "AZERTYUIOP<>" + .byte 13,0 + .ascii "QSDFGHJKLM%" + .byte '~,0,'# + .ascii "WXCVBN?./\\" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0~#{[|`\\^@]}" + .byte 0,0 + .byte '@,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + +#else +#error "KBD-type not defined" +#endif +/* + * do_self handles "normal" keys, ie keys that don't change meaning + * and which have just one character returns. + */ +do_self: + lea alt_map,%ebx + testb $0x20,mode /* alt-gr */ + jne 1f + lea shift_map,%ebx + testb $0x03,mode + jne 1f + lea key_map,%ebx +1: movb (%ebx,%eax),%al + orb %al,%al + je none + testb $0x4c,mode /* ctrl or caps */ + je 2f + cmpb $'a,%al + jb 2f + cmpb $'},%al + ja 2f + subb $32,%al +2: testb $0x0c,mode /* ctrl */ + je 3f + cmpb $64,%al + jb 3f + cmpb $64+32,%al + jae 3f + subb $64,%al +3: testb $0x10,mode /* left alt */ + je 4f + orb $0x80,%al +4: andl $0xff,%eax + xorl %ebx,%ebx + call put_queue +none: ret + +/* + * minus has a routine of it's own, as a 'E0h' before + * the scan code for minus means that the numeric keypad + * slash was pushed. + */ +minus: cmpb $1,e0 + jne do_self + movl $'/,%eax + xorl %ebx,%ebx + jmp put_queue + +/* + * This table decides which routine to call when a scan-code has been + * gotten. Most routines just call do_self, or none, depending if + * they are make or break. + */ +key_table: + .long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */ + .long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */ + .long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */ + .long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */ + .long do_self,do_self,do_self,do_self /* 10-13 q w e r */ + .long do_self,do_self,do_self,do_self /* 14-17 t y u i */ + .long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */ + .long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */ + .long do_self,do_self,do_self,do_self /* 20-23 d f g h */ + .long do_self,do_self,do_self,do_self /* 24-27 j k l | */ + .long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */ + .long do_self,do_self,do_self,do_self /* 2C-2F z x c v */ + .long do_self,do_self,do_self,do_self /* 30-33 b n m , */ + .long do_self,minus,rshift,do_self /* 34-37 . - rshift * */ + .long alt,do_self,caps,func /* 38-3B alt sp caps f1 */ + .long func,func,func,func /* 3C-3F f2 f3 f4 f5 */ + .long func,func,func,func /* 40-43 f6 f7 f8 f9 */ + .long func,num,scroll,cursor /* 44-47 f10 num scr home */ + .long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */ + .long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */ + .long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */ + .long none,none,do_self,func /* 54-57 sysreq ? < f11 */ + .long func,none,none,none /* 58-5B f12 ? ? ? */ + .long none,none,none,none /* 5C-5F ? ? ? ? */ + .long none,none,none,none /* 60-63 ? ? ? ? */ + .long none,none,none,none /* 64-67 ? ? ? ? */ + .long none,none,none,none /* 68-6B ? ? ? ? */ + .long none,none,none,none /* 6C-6F ? ? ? ? */ + .long none,none,none,none /* 70-73 ? ? ? ? */ + .long none,none,none,none /* 74-77 ? ? ? ? */ + .long none,none,none,none /* 78-7B ? ? ? ? */ + .long none,none,none,none /* 7C-7F ? ? ? ? */ + .long none,none,none,none /* 80-83 ? br br br */ + .long none,none,none,none /* 84-87 br br br br */ + .long none,none,none,none /* 88-8B br br br br */ + .long none,none,none,none /* 8C-8F br br br br */ + .long none,none,none,none /* 90-93 br br br br */ + .long none,none,none,none /* 94-97 br br br br */ + .long none,none,none,none /* 98-9B br br br br */ + .long none,unctrl,none,none /* 9C-9F br unctrl br br */ + .long none,none,none,none /* A0-A3 br br br br */ + .long none,none,none,none /* A4-A7 br br br br */ + .long none,none,unlshift,none /* A8-AB br br unlshift br */ + .long none,none,none,none /* AC-AF br br br br */ + .long none,none,none,none /* B0-B3 br br br br */ + .long none,none,unrshift,none /* B4-B7 br br unrshift br */ + .long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */ + .long none,none,none,none /* BC-BF br br br br */ + .long none,none,none,none /* C0-C3 br br br br */ + .long none,none,none,none /* C4-C7 br br br br */ + .long none,none,none,none /* C8-CB br br br br */ + .long none,none,none,none /* CC-CF br br br br */ + .long none,none,none,none /* D0-D3 br br br br */ + .long none,none,none,none /* D4-D7 br br br br */ + .long none,none,none,none /* D8-DB br ? ? ? */ + .long none,none,none,none /* DC-DF ? ? ? ? */ + .long none,none,none,none /* E0-E3 e0 e1 ? ? */ + .long none,none,none,none /* E4-E7 ? ? ? ? */ + .long none,none,none,none /* E8-EB ? ? ? ? */ + .long none,none,none,none /* EC-EF ? ? ? ? */ + .long none,none,none,none /* F0-F3 ? ? ? ? */ + .long none,none,none,none /* F4-F7 ? ? ? ? */ + .long none,none,none,none /* F8-FB ? ? ? ? */ + .long none,none,none,none /* FC-FF ? ? ? ? */ + +/* + * kb_wait waits for the keyboard controller buffer to empty. + * there is no timeout - if the buffer doesn't empty, we hang. + */ +kb_wait: + pushl %eax +1: inb $0x64,%al + testb $0x02,%al + jne 1b + popl %eax + ret +/* + * This routine reboots the machine by asking the keyboard + * controller to pulse the reset-line low. + */ +reboot: + call kb_wait + movw $0x1234,0x472 /* don't do memory check */ + movb $0xfc,%al /* pulse reset and A20 low */ + outb %al,$0x64 +die: jmp die diff --git a/4/linux/kernel/chr_drv/rs_io.s b/4/linux/kernel/chr_drv/rs_io.s new file mode 100644 index 0000000..ba1e55e --- /dev/null +++ b/4/linux/kernel/chr_drv/rs_io.s @@ -0,0 +1,147 @@ +/* + * linux/kernel/rs_io.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * rs_io.s + * + * This module implements the rs232 io interrupts. + */ + +.text +.globl rs1_interrupt,rs2_interrupt + +size = 1024 /* must be power of two ! + and must match the value + in tty_io.c!!! */ + +/* these are the offsets into the read/write buffer structures */ +rs_addr = 0 +head = 4 +tail = 8 +proc_list = 12 +buf = 16 + +startup = 256 /* chars left in write queue when we restart it */ + +/* + * These are the actual interrupt routines. They look where + * the interrupt is coming from, and take appropriate action. + */ +.align 4 +rs1_interrupt: + pushl $table_list+8 + jmp rs_int +.align 4 +rs2_interrupt: + pushl $table_list+16 +rs_int: + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + push %es + push %ds /* as this is an interrupt, we cannot */ + pushl $0x10 /* know that bs is ok. Load it */ + pop %ds + pushl $0x10 + pop %es + movl 24(%esp),%edx + movl (%edx),%edx + movl rs_addr(%edx),%edx + addl $2,%edx /* interrupt ident. reg */ +rep_int: + xorl %eax,%eax + inb %dx,%al + testb $1,%al + jne end + cmpb $6,%al /* this shouldn't happen, but ... */ + ja end + movl 24(%esp),%ecx + pushl %edx + subl $2,%edx + call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */ + popl %edx + jmp rep_int +end: movb $0x20,%al + outb %al,$0x20 /* EOI */ + pop %ds + pop %es + popl %eax + popl %ebx + popl %ecx + popl %edx + addl $4,%esp # jump over _table_list entry + iret + +jmp_table: + .long modem_status,write_char,read_char,line_status + +.align 4 +modem_status: + addl $6,%edx /* clear intr by reading modem status reg */ + inb %dx,%al + ret + +.align 4 +line_status: + addl $5,%edx /* clear intr by reading line status reg. */ + inb %dx,%al + ret + +.align 4 +read_char: + inb %dx,%al + movl %ecx,%edx + subl $table_list,%edx + shrl $3,%edx + movl (%ecx),%ecx # read-queue + movl head(%ecx),%ebx + movb %al,buf(%ecx,%ebx) + incl %ebx + andl $size-1,%ebx + cmpl tail(%ecx),%ebx + je 1f + movl %ebx,head(%ecx) +1: pushl %edx + call do_tty_interrupt + addl $4,%esp + ret + +.align 4 +write_char: + movl 4(%ecx),%ecx # write-queue + movl head(%ecx),%ebx + subl tail(%ecx),%ebx + andl $size-1,%ebx # nr chars in queue + je write_buffer_empty + cmpl $startup,%ebx + ja 1f + movl proc_list(%ecx),%ebx # wake up sleeping process + testl %ebx,%ebx # is there any? + je 1f + movl $0,(%ebx) +1: movl tail(%ecx),%ebx + movb buf(%ecx,%ebx),%al + outb %al,%dx + incl %ebx + andl $size-1,%ebx + movl %ebx,tail(%ecx) + cmpl head(%ecx),%ebx + je write_buffer_empty + ret +.align 4 +write_buffer_empty: + movl proc_list(%ecx),%ebx # wake up sleeping process + testl %ebx,%ebx # is there any? + je 1f + movl $0,(%ebx) +1: incl %edx + inb %dx,%al + jmp 1f +1: jmp 1f +1: andb $0xd,%al /* disable transmit interrupt */ + outb %al,%dx + ret diff --git a/4/linux/kernel/chr_drv/serial.c b/4/linux/kernel/chr_drv/serial.c new file mode 100644 index 0000000..aba25df --- /dev/null +++ b/4/linux/kernel/chr_drv/serial.c @@ -0,0 +1,59 @@ +/* + * linux/kernel/serial.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * serial.c + * + * This module implements the rs232 io functions + * void rs_write(struct tty_struct * queue); + * void rs_init(void); + * and all interrupts pertaining to serial IO. + */ + +#include +#include +#include +#include + +#define WAKEUP_CHARS (TTY_BUF_SIZE/4) + +extern void rs1_interrupt(void); +extern void rs2_interrupt(void); + +static void init(int port) +{ + outb_p(0x80,port+3); /* set DLAB of line control reg */ + outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */ + outb_p(0x00,port+1); /* MS of divisor */ + outb_p(0x03,port+3); /* reset DLAB */ + outb_p(0x0b,port+4); /* set DTR,RTS, OUT_2 */ + outb_p(0x0d,port+1); /* enable all intrs but writes */ + (void)inb(port); /* read data port to reset things (?) */ +} + +void rs_init(void) +{ + set_intr_gate(0x24,rs1_interrupt); + set_intr_gate(0x23,rs2_interrupt); + init(tty_table[1].read_q.data); + init(tty_table[2].read_q.data); + outb(inb_p(0x21)&0xE7,0x21); +} + +/* + * This routine gets called when tty_write has put something into + * the write_queue. It must check wheter the queue is empty, and + * set the interrupt register accordingly + * + * void _rs_write(struct tty_struct * tty); + */ +void rs_write(struct tty_struct * tty) +{ + cli(); + if (!EMPTY(tty->write_q)) + outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1); + sti(); +} diff --git a/4/linux/kernel/chr_drv/tty_io.c b/4/linux/kernel/chr_drv/tty_io.c new file mode 100644 index 0000000..b8da643 --- /dev/null +++ b/4/linux/kernel/chr_drv/tty_io.c @@ -0,0 +1,349 @@ +/* + * linux/kernel/tty_io.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles + * or rs-channels. It also implements echoing, cooked mode etc. + * + * Kill-line thanks to John T Kohl. + */ +#include +#include +#include + +#define ALRMMASK (1<<(SIGALRM-1)) +#define KILLMASK (1<<(SIGKILL-1)) +#define INTMASK (1<<(SIGINT-1)) +#define QUITMASK (1<<(SIGQUIT-1)) +#define TSTPMASK (1<<(SIGTSTP-1)) + +#include +#include +#include +#include + +#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f) +#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f) +#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f) + +#define L_CANON(tty) _L_FLAG((tty),ICANON) +#define L_ISIG(tty) _L_FLAG((tty),ISIG) +#define L_ECHO(tty) _L_FLAG((tty),ECHO) +#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) +#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) +#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) +#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) + +#define I_UCLC(tty) _I_FLAG((tty),IUCLC) +#define I_NLCR(tty) _I_FLAG((tty),INLCR) +#define I_CRNL(tty) _I_FLAG((tty),ICRNL) +#define I_NOCR(tty) _I_FLAG((tty),IGNCR) + +#define O_POST(tty) _O_FLAG((tty),OPOST) +#define O_NLCR(tty) _O_FLAG((tty),ONLCR) +#define O_CRNL(tty) _O_FLAG((tty),OCRNL) +#define O_NLRET(tty) _O_FLAG((tty),ONLRET) +#define O_LCUC(tty) _O_FLAG((tty),OLCUC) + +struct tty_struct tty_table[] = { + { + {ICRNL, /* change incoming CR to NL */ + OPOST|ONLCR, /* change outgoing NL to CRNL */ + 0, + ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, + 0, /* console termio */ + INIT_C_CC}, + 0, /* initial pgrp */ + 0, /* initial stopped */ + con_write, + {0,0,0,0,""}, /* console read-queue */ + {0,0,0,0,""}, /* console write-queue */ + {0,0,0,0,""} /* console secondary queue */ + },{ + {0, /* no translation */ + 0, /* no translation */ + B2400 | CS8, + 0, + 0, + INIT_C_CC}, + 0, + 0, + rs_write, + {0x3f8,0,0,0,""}, /* rs 1 */ + {0x3f8,0,0,0,""}, + {0,0,0,0,""} + },{ + {0, /* no translation */ + 0, /* no translation */ + B2400 | CS8, + 0, + 0, + INIT_C_CC}, + 0, + 0, + rs_write, + {0x2f8,0,0,0,""}, /* rs 2 */ + {0x2f8,0,0,0,""}, + {0,0,0,0,""} + } +}; + +/* + * these are the tables used by the machine code handlers. + * you can implement pseudo-tty's or something by changing + * them. Currently not done. + */ +struct tty_queue * table_list[]={ + &tty_table[0].read_q, &tty_table[0].write_q, + &tty_table[1].read_q, &tty_table[1].write_q, + &tty_table[2].read_q, &tty_table[2].write_q + }; + +void tty_init(void) +{ + rs_init(); + con_init(); +} + +void tty_intr(struct tty_struct * tty, int mask) +{ + int i; + + if (tty->pgrp <= 0) + return; + for (i=0;ipgrp==tty->pgrp) + task[i]->signal |= mask; +} + +static void sleep_if_empty(struct tty_queue * queue) +{ + cli(); + while (!current->signal && EMPTY(*queue)) + interruptible_sleep_on(&queue->proc_list); + sti(); +} + +static void sleep_if_full(struct tty_queue * queue) +{ + if (!FULL(*queue)) + return; + cli(); + while (!current->signal && LEFT(*queue)<128) + interruptible_sleep_on(&queue->proc_list); + sti(); +} + +void wait_for_keypress(void) +{ + sleep_if_empty(&tty_table[0].secondary); +} + +void copy_to_cooked(struct tty_struct * tty) +{ + signed char c; + + while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) { + GETCH(tty->read_q,c); + if (c==13) + if (I_CRNL(tty)) + c=10; + else if (I_NOCR(tty)) + continue; + else ; + else if (c==10 && I_NLCR(tty)) + c=13; + if (I_UCLC(tty)) + c=tolower(c); + if (L_CANON(tty)) { + if (c==KILL_CHAR(tty)) { + /* deal with killing the input line */ + while(!(EMPTY(tty->secondary) || + (c=LAST(tty->secondary))==10 || + c==EOF_CHAR(tty))) { + if (L_ECHO(tty)) { + if (c<32) + PUTCH(127,tty->write_q); + PUTCH(127,tty->write_q); + tty->write(tty); + } + DEC(tty->secondary.head); + } + continue; + } + if (c==ERASE_CHAR(tty)) { + if (EMPTY(tty->secondary) || + (c=LAST(tty->secondary))==10 || + c==EOF_CHAR(tty)) + continue; + if (L_ECHO(tty)) { + if (c<32) + PUTCH(127,tty->write_q); + PUTCH(127,tty->write_q); + tty->write(tty); + } + DEC(tty->secondary.head); + continue; + } + if (c==STOP_CHAR(tty)) { + tty->stopped=1; + continue; + } + if (c==START_CHAR(tty)) { + tty->stopped=0; + continue; + } + } + if (L_ISIG(tty)) { + if (c==INTR_CHAR(tty)) { + tty_intr(tty,INTMASK); + continue; + } + if (c==QUIT_CHAR(tty)) { + tty_intr(tty,QUITMASK); + continue; + } + } + if (c==10 || c==EOF_CHAR(tty)) + tty->secondary.data++; + if (L_ECHO(tty)) { + if (c==10) { + PUTCH(10,tty->write_q); + PUTCH(13,tty->write_q); + } else if (c<32) { + if (L_ECHOCTL(tty)) { + PUTCH('^',tty->write_q); + PUTCH(c+64,tty->write_q); + } + } else + PUTCH(c,tty->write_q); + tty->write(tty); + } + PUTCH(c,tty->secondary); + } + wake_up(&tty->secondary.proc_list); +} + +int tty_read(unsigned channel, char * buf, int nr) +{ + struct tty_struct * tty; + char c, * b=buf; + int minimum,time,flag=0; + long oldalarm; + + if (channel>2 || nr<0) return -1; + tty = &tty_table[channel]; + oldalarm = current->alarm; + time = 10L*tty->termios.c_cc[VTIME]; + minimum = tty->termios.c_cc[VMIN]; + if (time && !minimum) { + minimum=1; + if (flag=(!oldalarm || time+jiffiesalarm = time+jiffies; + } + if (minimum>nr) + minimum=nr; + while (nr>0) { + if (flag && (current->signal & ALRMMASK)) { + current->signal &= ~ALRMMASK; + break; + } + if (current->signal) + break; + if (EMPTY(tty->secondary) || (L_CANON(tty) && + !tty->secondary.data && LEFT(tty->secondary)>20)) { + sleep_if_empty(&tty->secondary); + continue; + } + do { + GETCH(tty->secondary,c); + if (c==EOF_CHAR(tty) || c==10) + tty->secondary.data--; + if (c==EOF_CHAR(tty) && L_CANON(tty)) + return (b-buf); + else { + put_fs_byte(c,b++); + if (!--nr) + break; + } + } while (nr>0 && !EMPTY(tty->secondary)); + if (time && !L_CANON(tty)) + if (flag=(!oldalarm || time+jiffiesalarm = time+jiffies; + else + current->alarm = oldalarm; + if (L_CANON(tty)) { + if (b-buf) + break; + } else if (b-buf >= minimum) + break; + } + current->alarm = oldalarm; + if (current->signal && !(b-buf)) + return -EINTR; + return (b-buf); +} + +int tty_write(unsigned channel, char * buf, int nr) +{ + static cr_flag=0; + struct tty_struct * tty; + char c, *b=buf; + + if (channel>2 || nr<0) return -1; + tty = channel + tty_table; + while (nr>0) { + sleep_if_full(&tty->write_q); + if (current->signal) + break; + while (nr>0 && !FULL(tty->write_q)) { + c=get_fs_byte(b); + if (O_POST(tty)) { + if (c=='\r' && O_CRNL(tty)) + c='\n'; + else if (c=='\n' && O_NLRET(tty)) + c='\r'; + if (c=='\n' && !cr_flag && O_NLCR(tty)) { + cr_flag = 1; + PUTCH(13,tty->write_q); + continue; + } + if (O_LCUC(tty)) + c=toupper(c); + } + b++; nr--; + cr_flag = 0; + PUTCH(c,tty->write_q); + } + tty->write(tty); + if (nr>0) + schedule(); + } + return (b-buf); +} + +/* + * Jeh, sometimes I really like the 386. + * This routine is called from an interrupt, + * and there should be absolutely no problem + * with sleeping even in an interrupt (I hope). + * Of course, if somebody proves me wrong, I'll + * hate intel for all time :-). We'll have to + * be careful and see to reinstating the interrupt + * chips before calling this, though. + * + * I don't think we sleep here under normal circumstances + * anyway, which is good, as the task sleeping might be + * totally innocent. + */ +void do_tty_interrupt(int tty) +{ + copy_to_cooked(tty_table+tty); +} + +void chr_dev_init(void) +{ +} diff --git a/4/linux/kernel/chr_drv/tty_ioctl.c b/4/linux/kernel/chr_drv/tty_ioctl.c new file mode 100644 index 0000000..e4e3745 --- /dev/null +++ b/4/linux/kernel/chr_drv/tty_ioctl.c @@ -0,0 +1,204 @@ +/* + * linux/kernel/chr_drv/tty_ioctl.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +static unsigned short quotient[] = { + 0, 2304, 1536, 1047, 857, + 768, 576, 384, 192, 96, + 64, 48, 24, 12, 6, 3 +}; + +static void change_speed(struct tty_struct * tty) +{ + unsigned short port,quot; + + if (!(port = tty->read_q.data)) + return; + quot = quotient[tty->termios.c_cflag & CBAUD]; + cli(); + outb_p(0x80,port+3); /* set DLAB */ + outb_p(quot & 0xff,port); /* LS of divisor */ + outb_p(quot >> 8,port+1); /* MS of divisor */ + outb(0x03,port+3); /* reset DLAB */ + sti(); +} + +static void flush(struct tty_queue * queue) +{ + cli(); + queue->head = queue->tail; + sti(); +} + +static void wait_until_sent(struct tty_struct * tty) +{ + /* do nothing - not implemented */ +} + +static void send_break(struct tty_struct * tty) +{ + /* do nothing - not implemented */ +} + +static int get_termios(struct tty_struct * tty, struct termios * termios) +{ + int i; + + verify_area(termios, sizeof (*termios)); + for (i=0 ; i< (sizeof (*termios)) ; i++) + put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios ); + return 0; +} + +static int set_termios(struct tty_struct * tty, struct termios * termios) +{ + int i; + + for (i=0 ; i< (sizeof (*termios)) ; i++) + ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios); + change_speed(tty); + return 0; +} + +static int get_termio(struct tty_struct * tty, struct termio * termio) +{ + int i; + struct termio tmp_termio; + + verify_area(termio, sizeof (*termio)); + tmp_termio.c_iflag = tty->termios.c_iflag; + tmp_termio.c_oflag = tty->termios.c_oflag; + tmp_termio.c_cflag = tty->termios.c_cflag; + tmp_termio.c_lflag = tty->termios.c_lflag; + tmp_termio.c_line = tty->termios.c_line; + for(i=0 ; i < NCC ; i++) + tmp_termio.c_cc[i] = tty->termios.c_cc[i]; + for (i=0 ; i< (sizeof (*termio)) ; i++) + put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio ); + return 0; +} + +/* + * This only works as the 386 is low-byt-first + */ +static int set_termio(struct tty_struct * tty, struct termio * termio) +{ + int i; + struct termio tmp_termio; + + for (i=0 ; i< (sizeof (*termio)) ; i++) + ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio); + *(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag; + *(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag; + *(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag; + *(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag; + tty->termios.c_line = tmp_termio.c_line; + for(i=0 ; i < NCC ; i++) + tty->termios.c_cc[i] = tmp_termio.c_cc[i]; + change_speed(tty); + return 0; +} + +int tty_ioctl(int dev, int cmd, int arg) +{ + struct tty_struct * tty; + if (MAJOR(dev) == 5) { + dev=current->tty; + if (dev<0) + panic("tty_ioctl: dev<0"); + } else + dev=MINOR(dev); + tty = dev + tty_table; + switch (cmd) { + case TCGETS: + return get_termios(tty,(struct termios *) arg); + case TCSETSF: + flush(&tty->read_q); /* fallthrough */ + case TCSETSW: + wait_until_sent(tty); /* fallthrough */ + case TCSETS: + return set_termios(tty,(struct termios *) arg); + case TCGETA: + return get_termio(tty,(struct termio *) arg); + case TCSETAF: + flush(&tty->read_q); /* fallthrough */ + case TCSETAW: + wait_until_sent(tty); /* fallthrough */ + case TCSETA: + return set_termio(tty,(struct termio *) arg); + case TCSBRK: + if (!arg) { + wait_until_sent(tty); + send_break(tty); + } + return 0; + case TCXONC: + return -EINVAL; /* not implemented */ + case TCFLSH: + if (arg==0) + flush(&tty->read_q); + else if (arg==1) + flush(&tty->write_q); + else if (arg==2) { + flush(&tty->read_q); + flush(&tty->write_q); + } else + return -EINVAL; + return 0; + case TIOCEXCL: + return -EINVAL; /* not implemented */ + case TIOCNXCL: + return -EINVAL; /* not implemented */ + case TIOCSCTTY: + return -EINVAL; /* set controlling term NI */ + case TIOCGPGRP: + verify_area((void *) arg,4); + put_fs_long(tty->pgrp,(unsigned long *) arg); + return 0; + case TIOCSPGRP: + tty->pgrp=get_fs_long((unsigned long *) arg); + return 0; + case TIOCOUTQ: + verify_area((void *) arg,4); + put_fs_long(CHARS(tty->write_q),(unsigned long *) arg); + return 0; + case TIOCINQ: + verify_area((void *) arg,4); + put_fs_long(CHARS(tty->secondary), + (unsigned long *) arg); + return 0; + case TIOCSTI: + return -EINVAL; /* not implemented */ + case TIOCGWINSZ: + return -EINVAL; /* not implemented */ + case TIOCSWINSZ: + return -EINVAL; /* not implemented */ + case TIOCMGET: + return -EINVAL; /* not implemented */ + case TIOCMBIS: + return -EINVAL; /* not implemented */ + case TIOCMBIC: + return -EINVAL; /* not implemented */ + case TIOCMSET: + return -EINVAL; /* not implemented */ + case TIOCGSOFTCAR: + return -EINVAL; /* not implemented */ + case TIOCSSOFTCAR: + return -EINVAL; /* not implemented */ + default: + return -EINVAL; + } +} diff --git a/4/linux/kernel/exit.c b/4/linux/kernel/exit.c new file mode 100644 index 0000000..2406ebe --- /dev/null +++ b/4/linux/kernel/exit.c @@ -0,0 +1,197 @@ +/* + * linux/kernel/exit.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include + +#include +#include +#include +#include + +int sys_pause(void); +int sys_close(int fd); + +void release(struct task_struct * p) +{ + int i; + + if (!p) + return; + for (i=1 ; i32) + return -EINVAL; + if (priv || (current->euid==p->euid) || suser()) + p->signal |= (1<<(sig-1)); + else + return -EPERM; + return 0; +} + +static void kill_session(void) +{ + struct task_struct **p = NR_TASKS + task; + + while (--p > &FIRST_TASK) { + if (*p && (*p)->session == current->session) + (*p)->signal |= 1<<(SIGHUP-1); + } +} + +/* + * XXX need to check permissions needed to send signals to process + * groups, etc. etc. kill() permissions semantics are tricky! + */ +int sys_kill(int pid,int sig) +{ + struct task_struct **p = NR_TASKS + task; + int err, retval = 0; + + if (!pid) while (--p > &FIRST_TASK) { + if (*p && (*p)->pgrp == current->pid) + if (err=send_sig(sig,*p,1)) + retval = err; + } else if (pid>0) while (--p > &FIRST_TASK) { + if (*p && (*p)->pid == pid) + if (err=send_sig(sig,*p,0)) + retval = err; + } else if (pid == -1) while (--p > &FIRST_TASK) + if (err = send_sig(sig,*p,0)) + retval = err; + else while (--p > &FIRST_TASK) + if (*p && (*p)->pgrp == -pid) + if (err = send_sig(sig,*p,0)) + retval = err; + return retval; +} + +static void tell_father(int pid) +{ + int i; + + if (pid) + for (i=0;ipid != pid) + continue; + task[i]->signal |= (1<<(SIGCHLD-1)); + return; + } +/* if we don't find any fathers, we just release ourselves */ +/* This is not really OK. Must change it to make father 1 */ + printk("BAD BAD - no father found\n\r"); + release(current); +} + +int do_exit(long code) +{ + int i; + + free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); + for (i=0 ; ifather == current->pid) { + task[i]->father = 1; + if (task[i]->state == TASK_ZOMBIE) + /* assumption task[1] is always init */ + (void) send_sig(SIGCHLD, task[1], 1); + } + for (i=0 ; ifilp[i]) + sys_close(i); + iput(current->pwd); + current->pwd=NULL; + iput(current->root); + current->root=NULL; + iput(current->executable); + current->executable=NULL; + if (current->leader && current->tty >= 0) + tty_table[current->tty].pgrp = 0; + if (last_task_used_math == current) + last_task_used_math = NULL; + if (current->leader) + kill_session(); + current->state = TASK_ZOMBIE; + current->exit_code = code; + tell_father(current->father); + schedule(); + return (-1); /* just to suppress warnings */ +} + +int sys_exit(int error_code) +{ + return do_exit((error_code&0xff)<<8); +} + +int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) +{ + int flag, code; + struct task_struct ** p; + + verify_area(stat_addr,4); +repeat: + flag=0; + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (!*p || *p == current) + continue; + if ((*p)->father != current->pid) + continue; + if (pid>0) { + if ((*p)->pid != pid) + continue; + } else if (!pid) { + if ((*p)->pgrp != current->pgrp) + continue; + } else if (pid != -1) { + if ((*p)->pgrp != -pid) + continue; + } + switch ((*p)->state) { + case TASK_STOPPED: + if (!(options & WUNTRACED)) + continue; + put_fs_long(0x7f,stat_addr); + return (*p)->pid; + case TASK_ZOMBIE: + current->cutime += (*p)->utime; + current->cstime += (*p)->stime; + flag = (*p)->pid; + code = (*p)->exit_code; + release(*p); + put_fs_long(code,stat_addr); + return flag; + default: + flag=1; + continue; + } + } + if (flag) { + if (options & WNOHANG) + return 0; + current->state=TASK_INTERRUPTIBLE; + schedule(); + if (!(current->signal &= ~(1<<(SIGCHLD-1)))) + goto repeat; + else + return -EINTR; + } + return -ECHILD; +} + + diff --git a/4/linux/kernel/fork.c b/4/linux/kernel/fork.c new file mode 100644 index 0000000..816fdba --- /dev/null +++ b/4/linux/kernel/fork.c @@ -0,0 +1,148 @@ +/* + * 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 + +#include +#include +#include +#include + +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; + __asm__ volatile ("cld"); /* by wyj */ + *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; ifilp[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 ; ipid == last_pid) goto repeat; + for(i=1 ; i tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: diff --git a/4/linux/kernel/math/math_emulate.c b/4/linux/kernel/math/math_emulate.c new file mode 100644 index 0000000..825e528 --- /dev/null +++ b/4/linux/kernel/math/math_emulate.c @@ -0,0 +1,42 @@ +/* + * linux/kernel/math/math_emulate.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This directory should contain the math-emulation code. + * Currently only results in a signal. + */ + +#include + +#include +#include +#include + +void math_emulate(long edi, long esi, long ebp, long sys_call_ret, + long eax,long ebx,long ecx,long edx, + unsigned short fs,unsigned short es,unsigned short ds, + unsigned long eip,unsigned short cs,unsigned long eflags, + unsigned short ss, unsigned long esp) +{ + unsigned char first, second; + +/* 0x0007 means user code space */ + if (cs != 0x000F) { + printk("math_emulate: %04x:%08x\n\r",cs,eip); + panic("Math emulation needed in kernel"); + } + first = get_fs_byte((char *)((*&eip)++)); + second = get_fs_byte((char *)((*&eip)++)); + printk("%04x:%08x %02x %02x\n\r",cs,eip-2,first,second); + current->signal |= 1<<(SIGFPE-1); +} + +void math_error(void) +{ + __asm__("fnclex"); + if (last_task_used_math) + last_task_used_math->signal |= 1<<(SIGFPE-1); +} diff --git a/4/linux/kernel/mktime.c b/4/linux/kernel/mktime.c new file mode 100644 index 0000000..a6d03ca --- /dev/null +++ b/4/linux/kernel/mktime.c @@ -0,0 +1,58 @@ +/* + * linux/kernel/mktime.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +/* + * This isn't the library routine, it is only used in the kernel. + * as such, we don't care about years<1970 etc, but assume everything + * is ok. Similarly, TZ etc is happily ignored. We just do everything + * as easily as possible. Let's find something public for the library + * routines (although I think minix times is public). + */ +/* + * PS. I hate whoever though up the year 1970 - couldn't they have gotten + * a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy. + */ +#define MINUTE 60 +#define HOUR (60*MINUTE) +#define DAY (24*HOUR) +#define YEAR (365*DAY) + +/* interestingly, we assume leap-years */ +static int month[12] = { + 0, + DAY*(31), + DAY*(31+29), + DAY*(31+29+31), + DAY*(31+29+31+30), + DAY*(31+29+31+30+31), + DAY*(31+29+31+30+31+30), + DAY*(31+29+31+30+31+30+31), + DAY*(31+29+31+30+31+30+31+31), + DAY*(31+29+31+30+31+30+31+31+30), + DAY*(31+29+31+30+31+30+31+31+30+31), + DAY*(31+29+31+30+31+30+31+31+30+31+30) +}; + +long kernel_mktime(struct tm * tm) +{ + long res; + int year; + if (tm->tm_year < 70 ) tm->tm_year += 100; /* gohigh */ + year = tm->tm_year - 70; +/* magic offsets (y+1) needed to get leapyears right.*/ + res = YEAR*year + DAY*((year+1)/4); + res += month[tm->tm_mon]; +/* and (y+2) here. If it wasn't a leap-year, we have to adjust */ + if (tm->tm_mon>1 && ((year+2)%4)) + res -= DAY; + res += DAY*(tm->tm_mday-1); + res += HOUR*tm->tm_hour; + res += MINUTE*tm->tm_min; + res += tm->tm_sec; + return res; +} diff --git a/4/linux/kernel/panic.c b/4/linux/kernel/panic.c new file mode 100644 index 0000000..7d8a06b --- /dev/null +++ b/4/linux/kernel/panic.c @@ -0,0 +1,24 @@ +/* + * linux/kernel/panic.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This function is used through-out the kernel (includeinh mm and fs) + * to indicate a major problem. + */ +#include +#include + +void sys_sync(void); /* it's really int */ + +volatile void panic(const char * s) +{ + printk("Kernel panic: %s\n\r",s); + if (current == task[0]) + printk("In swapper task - not syncing\n\r"); + else + sys_sync(); + for(;;); +} diff --git a/4/linux/kernel/printk.c b/4/linux/kernel/printk.c new file mode 100644 index 0000000..0daa097 --- /dev/null +++ b/4/linux/kernel/printk.c @@ -0,0 +1,41 @@ +/* + * linux/kernel/printk.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * When in kernel-mode, we cannot use printf, as fs is liable to + * point to 'interesting' things. Make a printf with fs-saving, and + * all is well. + */ +#include +#include + +#include + +static char buf[1024]; + +extern int vsprintf(char * buf, const char * fmt, va_list args); + +int printk(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + __asm__("push %%fs\n\t" + "push %%ds\n\t" + "pop %%fs\n\t" + "pushl %0\n\t" + "pushl $buf\n\t" + "pushl $0\n\t" + "call tty_write\n\t" + "addl $8,%%esp\n\t" + "popl %0\n\t" + "pop %%fs" + ::"r" (i):"ax","cx","dx"); + return i; +} diff --git a/4/linux/kernel/sched.c b/4/linux/kernel/sched.c new file mode 100644 index 0000000..15d839b --- /dev/null +++ b/4/linux/kernel/sched.c @@ -0,0 +1,412 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#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>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;ia=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); +} diff --git a/4/linux/kernel/signal.c b/4/linux/kernel/signal.c new file mode 100644 index 0000000..af40fb9 --- /dev/null +++ b/4/linux/kernel/signal.c @@ -0,0 +1,161 @@ +/* + * linux/kernel/signal.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include +#include +#include + +volatile void do_exit(int error_code); + +int sys_sgetmask() +{ + return current->blocked; +} + +int sys_ssetmask(int newmask) +{ + int old=current->blocked; + + current->blocked = newmask & ~(1<<(SIGKILL-1)); + return old; +} + +int sys_sigpending() +{ + return -ENOSYS; +} + +int sys_sigsuspend() +{ + return -ENOSYS; +} + +static inline void save_old(char * from,char * to) +{ + int i; + + verify_area(to, sizeof(struct sigaction)); + for (i=0 ; i< sizeof(struct sigaction) ; i++) { + put_fs_byte(*from,to); + from++; + to++; + } +} + +static inline void get_new(char * from,char * to) +{ + int i; + + for (i=0 ; i< sizeof(struct sigaction) ; i++) + *(to++) = get_fs_byte(from++); +} + +int sys_signal(int signum, long handler, long restorer) +{ + struct sigaction tmp; + + if (signum<1 || signum>32 || signum==SIGKILL) + return -1; + tmp.sa_handler = (void (*)(int)) handler; + tmp.sa_mask = 0; + tmp.sa_flags = SA_ONESHOT | SA_NOMASK; + tmp.sa_restorer = (void (*)(void)) restorer; + handler = (long) current->sigaction[signum-1].sa_handler; + current->sigaction[signum-1] = tmp; + return handler; +} + +unsigned int sys_sleep(unsigned int time) +{ + sys_signal(SIGALRM,SIG_IGN,NULL); + sys_alarm(time); + sys_pause(); + return 0; +} + +int sys_pipe2(int fd[2], int flags) +{ + if (flags == 0) + return sys_pipe((long*)fd); + else + return -1; +} + +long sys_getcwd(char* buf,size_t size) { + return 0; +} + +long sys_mmap(void*start,size_t len, int prot,int flags,int fd,off_t off) { + return 0; +} + +int sys_munmap(void* start,size_t len) { + return 0; +} + +int sys_clone(int (*fn)(void*),void*child_stack,int flags,void*arg){ + return 0; +} + +int sys_sigaction(int signum, const struct sigaction * action, + struct sigaction * oldaction) +{ + struct sigaction tmp; + + if (signum<1 || signum>32 || signum==SIGKILL) + return -1; + tmp = current->sigaction[signum-1]; + get_new((char *) action, + (char *) (signum-1+current->sigaction)); + if (oldaction) + save_old((char *) &tmp,(char *) oldaction); + if (current->sigaction[signum-1].sa_flags & SA_NOMASK) + current->sigaction[signum-1].sa_mask = 0; + else + current->sigaction[signum-1].sa_mask |= (1<<(signum-1)); + return 0; +} + +void do_signal(long signr,long eax, long ebx, long ecx, long edx, + long fs, long es, long ds, + long eip, long cs, long eflags, + unsigned long * esp, long ss) +{ + unsigned long sa_handler; + long old_eip=eip; + struct sigaction * sa = current->sigaction + signr - 1; + int longs; + unsigned long * tmp_esp; + + sa_handler = (unsigned long) sa->sa_handler; + if (sa_handler==1) + return; + if (!sa_handler) { + if (signr==SIGCHLD) + return; + else + do_exit(1<<(signr-1)); + } + if (sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; + *(&eip) = sa_handler; + longs = (sa->sa_flags & SA_NOMASK)?7:8; + *(&esp) -= longs; + verify_area(esp,longs*4); + tmp_esp=esp; + put_fs_long((long) sa->sa_restorer,tmp_esp++); + put_fs_long(signr,tmp_esp++); + if (!(sa->sa_flags & SA_NOMASK)) + put_fs_long(current->blocked,tmp_esp++); + put_fs_long(eax,tmp_esp++); + put_fs_long(ecx,tmp_esp++); + put_fs_long(edx,tmp_esp++); + put_fs_long(eflags,tmp_esp++); + put_fs_long(old_eip,tmp_esp++); + current->blocked |= sa->sa_mask; +} diff --git a/4/linux/kernel/sys.c b/4/linux/kernel/sys.c new file mode 100644 index 0000000..695f287 --- /dev/null +++ b/4/linux/kernel/sys.c @@ -0,0 +1,292 @@ +/* + * linux/kernel/sys.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +#include +#include +#include +#include +#include +#include + +int sys_ftime() +{ + return -ENOSYS; +} + +int sys_break() +{ + return -ENOSYS; +} + +int sys_ptrace() +{ + return -ENOSYS; +} + +int sys_stty() +{ + return -ENOSYS; +} + +int sys_gtty() +{ + return -ENOSYS; +} + +int sys_rename() +{ + return -ENOSYS; +} + +int sys_prof() +{ + return -ENOSYS; +} + +int sys_setregid(int rgid, int egid) +{ + if (rgid>0) { + if ((current->gid == rgid) || + suser()) + current->gid = rgid; + else + return(-EPERM); + } + if (egid>0) { + if ((current->gid == egid) || + (current->egid == egid) || + suser()) { + current->egid = egid; + current->sgid = egid; + } else + return(-EPERM); + } + return 0; +} + +int sys_setgid(int gid) +{ +/* return(sys_setregid(gid, gid)); */ + if (suser()) + current->gid = current->egid = current->sgid = gid; + else if ((gid == current->gid) || (gid == current->sgid)) + current->egid = gid; + else + return -EPERM; + return 0; +} + +int sys_acct() +{ + return -ENOSYS; +} + +int sys_phys() +{ + return -ENOSYS; +} + +int sys_lock() +{ + return -ENOSYS; +} + +int sys_mpx() +{ + return -ENOSYS; +} + +int sys_ulimit() +{ + return -ENOSYS; +} + +int sys_time(long * tloc) +{ + int i; + + i = CURRENT_TIME; + if (tloc) { + verify_area(tloc,4); + put_fs_long(i,(unsigned long *)tloc); + } + return i; +} + +/* + * Unprivileged users may change the real user id to the effective uid + * or vice versa. + */ +int sys_setreuid(int ruid, int euid) +{ + int old_ruid = current->uid; + + if (ruid>0) { + if ((current->euid==ruid) || + (old_ruid == ruid) || + suser()) + current->uid = ruid; + else + return(-EPERM); + } + if (euid>0) { + if ((old_ruid == euid) || + (current->euid == euid) || + suser()) { + current->euid = euid; + current->suid = euid; + } else { + current->uid = old_ruid; + return(-EPERM); + } + } + return 0; +} + +int sys_setuid(int uid) +{ +/* return(sys_setreuid(uid, uid)); */ + if (suser()) + current->uid = current->euid = current->suid = uid; + else if ((uid == current->uid) || (uid == current->suid)) + current->euid = uid; + else + return -EPERM; + return(0); +} + +int sys_stime(long * tptr) +{ + if (!suser()) + return -EPERM; + startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ; + return 0; +} + +int sys_times(struct tms * tbuf) +{ + if (tbuf) { + verify_area(tbuf,sizeof *tbuf); + put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime); + put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime); + put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime); + put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime); + } + return jiffies; +} + +int sys_brk(unsigned long end_data_seg) +{ + if (end_data_seg >= current->end_code && + end_data_seg < current->start_stack - 16384) + current->brk = end_data_seg; + return current->brk; +} + +/* + * This needs some heave checking ... + * I just haven't get the stomach for it. I also don't fully + * understand sessions/pgrp etc. Let somebody who does explain it. + */ +int sys_setpgid(int pid, int pgid) +{ + int i; + + if (!pid) + pid = current->pid; + if (!pgid) + pgid = current->pid; + for (i=0 ; ipid==pid) { + if (task[i]->leader) + return -EPERM; + if (task[i]->session != current->session) + return -EPERM; + task[i]->pgrp = pgid; + return 0; + } + return -ESRCH; +} + +int sys_getpgrp(void) +{ + return current->pgrp; +} + +int sys_setsid(void) +{ + if (current->leader && !suser()) + return -EPERM; + current->leader = 1; + current->session = current->pgrp = current->pid; + current->tty = -1; + return current->pgrp; +} + +int sys_getgroups() +{ + return -ENOSYS; +} + +int sys_setgroups() +{ + return -ENOSYS; +} + +int sys_uname(struct utsname * name) +{ + static struct utsname thisname = { + "linux .0","nodename","release ","version ","machine " + }; + int i; + + if (!name) return -ERROR; + verify_area(name,sizeof *name); + for(i=0;iumask; + + current->umask = mask & 0777; + return (old); +} diff --git a/4/linux/kernel/system_call.s b/4/linux/kernel/system_call.s new file mode 100644 index 0000000..8caf167 --- /dev/null +++ b/4/linux/kernel/system_call.s @@ -0,0 +1,298 @@ +/* + * 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 = 100 /* 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,sys_execve2 +.globl hd_interrupt,floppy_interrupt,parallel_interrupt +.globl device_not_available, coprocessor_error + +.align 4 +bad_sys_call: + movl $-1,%eax + iret +.align 4 +reschedule: + pushl $ret_from_sys_call + jmp schedule +.align 4 +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 + + pushl %eax #by wyj + call print_nr + popl %eax + + 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 4 +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 4 +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 4 +sys_execve: + lea EIP(%esp),%eax + pushl %eax + call do_execve + addl $4,%esp + ret + +.align 4 +sys_execve2: + lea EIP(%esp),%eax + pushl %eax + call do_execve2 + addl $4,%esp + ret + +.align 4 +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 diff --git a/4/linux/kernel/traps.c b/4/linux/kernel/traps.c new file mode 100644 index 0000000..f9bd8f1 --- /dev/null +++ b/4/linux/kernel/traps.c @@ -0,0 +1,208 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/4/linux/kernel/vsprintf.c b/4/linux/kernel/vsprintf.c new file mode 100644 index 0000000..06b910e --- /dev/null +++ b/4/linux/kernel/vsprintf.c @@ -0,0 +1,233 @@ +/* + * linux/kernel/vsprintf.c + * + * (C) 1991 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include +#include + +/* we use this so that we can do without the ctype library */ +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static int skip_atoi(const char **s) +{ + int i=0; + + while (is_digit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */ + +#define do_div(n,base) ({ \ +int __res; \ +__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \ +__res; }) + +static char * number(char * str, int num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[36]; + const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz"; + if (type&LEFT) type &= ~ZEROPAD; + if (base<2 || base>36) + return 0; + c = (type & ZEROPAD) ? '0' : ' ' ; + if (type&SIGN && num<0) { + sign='-'; + num = -num; + } else + sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0); + if (sign) size--; + if (type&SPECIAL) + if (base==16) size -= 2; + else if (base==8) size--; + i=0; + if (num==0) + tmp[i++]='0'; + else while (num!=0) + tmp[i++]=digits[do_div(num,base)]; + if (i>precision) precision=i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type&SPECIAL) + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + if (!(type&LEFT)) + while(size-->0) + *str++ = c; + while(i0) + *str++ = tmp[i]; + while(size-->0) + *str++ = ' '; + return str; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + int i; + char * str; + char *s; + int *ip; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + break; + + case 's': + s = va_arg(args, char *); + len = strlen(s); + if (precision < 0) + precision = len; + else if (len > precision) + len = precision; + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + break; + + case 'o': + str = number(str, va_arg(args, unsigned long), 8, + field_width, precision, flags); + break; + + case 'p': + if (field_width == -1) { + field_width = 8; + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + break; + + case 'x': + flags |= SMALL; + case 'X': + str = number(str, va_arg(args, unsigned long), 16, + field_width, precision, flags); + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + str = number(str, va_arg(args, unsigned long), 10, + field_width, precision, flags); + break; + + case 'n': + ip = va_arg(args, int *); + *ip = (str - buf); + break; + + default: + if (*fmt != '%') + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + break; + } + } + *str = '\0'; + return str-buf; +} diff --git a/4/linux/lib/Makefile b/4/linux/lib/Makefile new file mode 100644 index 0000000..608faf5 --- /dev/null +++ b/4/linux/lib/Makefile @@ -0,0 +1,73 @@ +# +# Makefile for some libs needed in the kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +AR =ar +AS =as +LD =ld +LDFLAGS =-s -x +CC =gcc -march=i386 +CFLAGS =-w -g -fstrength-reduce -fomit-frame-pointer \ + -finline-functions -nostdinc -fno-stack-protector -I../include +CPP =gcc -E -nostdinc -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = ctype.o _exit.o open.o close.o errno.o write.o dup.o setsid.o \ + execve.o wait.o string.o malloc.o + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + sync + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +_exit.s _exit.o : _exit.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +close.s close.o : close.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +ctype.s ctype.o : ctype.c ../include/ctype.h +dup.s dup.o : dup.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +errno.s errno.o : errno.c +execve.s execve.o : execve.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +malloc.s malloc.o : malloc.c ../include/linux/kernel.h ../include/linux/mm.h \ + ../include/asm/system.h +open.s open.o : open.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h ../include/stdarg.h +setsid.s setsid.o : setsid.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +string.s string.o : string.c ../include/string.h +wait.s wait.o : wait.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h ../include/sys/wait.h +write.s write.o : write.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h diff --git a/4/linux/lib/_exit.c b/4/linux/lib/_exit.c new file mode 100644 index 0000000..c0c9d69 --- /dev/null +++ b/4/linux/lib/_exit.c @@ -0,0 +1,13 @@ +/* + * linux/lib/_exit.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +volatile void _exit(int exit_code) +{ + __asm__("int $0x80"::"a" (__NR_exit),"b" (exit_code)); +} diff --git a/4/linux/lib/close.c b/4/linux/lib/close.c new file mode 100644 index 0000000..afd8364 --- /dev/null +++ b/4/linux/lib/close.c @@ -0,0 +1,10 @@ +/* + * linux/lib/close.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +_syscall1(int,close,int,fd) diff --git a/4/linux/lib/ctype.c b/4/linux/lib/ctype.c new file mode 100644 index 0000000..877e629 --- /dev/null +++ b/4/linux/lib/ctype.c @@ -0,0 +1,35 @@ +/* + * linux/lib/ctype.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +char _ctmp; +unsigned char _ctype[] = {0x00, /* EOF */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 160-175 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 176-191 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 192-207 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 208-223 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 224-239 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* 240-255 */ + diff --git a/4/linux/lib/dup.c b/4/linux/lib/dup.c new file mode 100644 index 0000000..dd13414 --- /dev/null +++ b/4/linux/lib/dup.c @@ -0,0 +1,10 @@ +/* + * linux/lib/dup.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +_syscall1(int,dup,int,fd) diff --git a/4/linux/lib/errno.c b/4/linux/lib/errno.c new file mode 100644 index 0000000..50aca2e --- /dev/null +++ b/4/linux/lib/errno.c @@ -0,0 +1,7 @@ +/* + * linux/lib/errno.c + * + * (C) 1991 Linus Torvalds + */ + +int errno; diff --git a/4/linux/lib/execve.c b/4/linux/lib/execve.c new file mode 100644 index 0000000..a89726d --- /dev/null +++ b/4/linux/lib/execve.c @@ -0,0 +1,10 @@ +/* + * linux/lib/execve.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +_syscall3(int,execve,const char *,file,char **,argv,char **,envp) diff --git a/4/linux/lib/malloc.c b/4/linux/lib/malloc.c new file mode 100644 index 0000000..d7c95d5 --- /dev/null +++ b/4/linux/lib/malloc.c @@ -0,0 +1,232 @@ +/* + * malloc.c --- a general purpose kernel memory allocator for Linux. + * + * Written by Theodore Ts'o (tytso@mit.edu), 11/29/91 + * + * This routine is written to be as fast as possible, so that it + * can be called from the interrupt level. + * + * Limitations: maximum size of memory we can allocate using this routine + * is 4k, the size of a page in Linux. + * + * The general game plan is that each page (called a bucket) will only hold + * objects of a given size. When all of the object on a page are released, + * the page can be returned to the general free pool. When malloc() is + * called, it looks for the smallest bucket size which will fulfill its + * request, and allocate a piece of memory from that bucket pool. + * + * Each bucket has as its control block a bucket descriptor which keeps + * track of how many objects are in use on that page, and the free list + * for that page. Like the buckets themselves, bucket descriptors are + * stored on pages requested from get_free_page(). However, unlike buckets, + * pages devoted to bucket descriptor pages are never released back to the + * system. Fortunately, a system should probably only need 1 or 2 bucket + * descriptor pages, since a page can hold 256 bucket descriptors (which + * corresponds to 1 megabyte worth of bucket pages.) If the kernel is using + * that much allocated memory, it's probably doing something wrong. :-) + * + * Note: malloc() and free() both call get_free_page() and free_page() + * in sections of code where interrupts are turned off, to allow + * malloc() and free() to be safely called from an interrupt routine. + * (We will probably need this functionality when networking code, + * particularily things like NFS, is added to Linux.) However, this + * presumes that get_free_page() and free_page() are interrupt-level + * safe, which they may not be once paging is added. If this is the + * case, we will need to modify malloc() to keep a few unused pages + * "pre-allocated" so that it can safely draw upon those pages if + * it is called from an interrupt routine. + * + * Another concern is that get_free_page() should not sleep; if it + * does, the code is carefully ordered so as to avoid any race + * conditions. The catch is that if malloc() is called re-entrantly, + * there is a chance that unecessary pages will be grabbed from the + * system. Except for the pages for the bucket descriptor page, the + * extra pages will eventually get released back to the system, though, + * so it isn't all that bad. + */ + +#include +#include +#include + +struct bucket_desc { /* 16 bytes */ + void *page; + struct bucket_desc *next; + void *freeptr; + unsigned short refcnt; + unsigned short bucket_size; +}; + +struct _bucket_dir { /* 8 bytes */ + int size; + struct bucket_desc *chain; +}; + +/* + * The following is the where we store a pointer to the first bucket + * descriptor for a given size. + * + * If it turns out that the Linux kernel allocates a lot of objects of a + * specific size, then we may want to add that specific size to this list, + * since that will allow the memory to be allocated more efficiently. + * However, since an entire page must be dedicated to each specific size + * on this list, some amount of temperance must be exercised here. + * + * Note that this list *must* be kept in order. + */ +struct _bucket_dir bucket_dir[] = { + { 16, (struct bucket_desc *) 0}, + { 32, (struct bucket_desc *) 0}, + { 64, (struct bucket_desc *) 0}, + { 128, (struct bucket_desc *) 0}, + { 256, (struct bucket_desc *) 0}, + { 512, (struct bucket_desc *) 0}, + { 1024, (struct bucket_desc *) 0}, + { 2048, (struct bucket_desc *) 0}, + { 4096, (struct bucket_desc *) 0}, + { 0, (struct bucket_desc *) 0}}; /* End of list marker */ + +/* + * This contains a linked list of free bucket descriptor blocks + */ +struct bucket_desc *free_bucket_desc = (struct bucket_desc *) 0; + +/* + * This routine initializes a bucket description page. + */ +static inline void init_bucket_desc() +{ + struct bucket_desc *bdesc, *first; + int i; + + first = bdesc = (struct bucket_desc *) get_free_page(); + if (!bdesc) + panic("Out of memory in init_bucket_desc()"); + for (i = PAGE_SIZE/sizeof(struct bucket_desc); i > 1; i--) { + bdesc->next = bdesc+1; + bdesc++; + } + /* + * This is done last, to avoid race conditions in case + * get_free_page() sleeps and this routine gets called again.... + */ + bdesc->next = free_bucket_desc; + free_bucket_desc = first; +} + +void *malloc(unsigned int len) +{ + struct _bucket_dir *bdir; + struct bucket_desc *bdesc; + void *retval; + + /* + * First we search the bucket_dir to find the right bucket change + * for this request. + */ + for (bdir = bucket_dir; bdir->size; bdir++) + if (bdir->size >= len) + break; + if (!bdir->size) { + printk("malloc called with impossibly large argument (%d)\n", + len); + panic("malloc: bad arg"); + } + /* + * Now we search for a bucket descriptor which has free space + */ + cli(); /* Avoid race conditions */ + for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) + if (bdesc->freeptr) + break; + /* + * If we didn't find a bucket with free space, then we'll + * allocate a new one. + */ + if (!bdesc) { + char *cp; + int i; + + if (!free_bucket_desc) + init_bucket_desc(); + bdesc = free_bucket_desc; + free_bucket_desc = bdesc->next; + bdesc->refcnt = 0; + bdesc->bucket_size = bdir->size; + bdesc->page = bdesc->freeptr = (void *) (cp = get_free_page()); + if (!cp) + panic("Out of memory in kernel malloc()"); + /* Set up the chain of free objects */ + for (i=PAGE_SIZE/bdir->size; i > 1; i--) { + *((char **) cp) = cp + bdir->size; + cp += bdir->size; + } + *((char **) cp) = 0; + bdesc->next = bdir->chain; /* OK, link it in! */ + bdir->chain = bdesc; + } + retval = (void *) bdesc->freeptr; + bdesc->freeptr = *((void **) retval); + bdesc->refcnt++; + sti(); /* OK, we're safe again */ + return(retval); +} + +/* + * Here is the free routine. If you know the size of the object that you + * are freeing, then free_s() will use that information to speed up the + * search for the bucket descriptor. + * + * We will #define a macro so that "free(x)" is becomes "free_s(x, 0)" + */ +void free_s(void *obj, int size) +{ + void *page; + struct _bucket_dir *bdir; + struct bucket_desc *bdesc, *prev; + + /* Calculate what page this object lives in */ + page = (void *) ((unsigned long) obj & 0xfffff000); + /* Now search the buckets looking for that page */ + for (bdir = bucket_dir; bdir->size; bdir++) { + prev = 0; + /* If size is zero then this conditional is always false */ + if (bdir->size < size) + continue; + for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) { + if (bdesc->page == page) + goto found; + prev = bdesc; + } + } + panic("Bad address passed to kernel free_s()"); +found: + cli(); /* To avoid race conditions */ + *((void **)obj) = bdesc->freeptr; + bdesc->freeptr = obj; + bdesc->refcnt--; + if (bdesc->refcnt == 0) { + /* + * We need to make sure that prev is still accurate. It + * may not be, if someone rudely interrupted us.... + */ + if ((prev && (prev->next != bdesc)) || + (!prev && (bdir->chain != bdesc))) + for (prev = bdir->chain; prev; prev = prev->next) + if (prev->next == bdesc) + break; + if (prev) + prev->next = bdesc->next; + else { + if (bdir->chain != bdesc) + panic("malloc bucket chains corrupted"); + bdir->chain = bdesc->next; + } + free_page((unsigned long) bdesc->page); + bdesc->next = free_bucket_desc; + free_bucket_desc = bdesc; + } + sti(); + return; +} + diff --git a/4/linux/lib/open.c b/4/linux/lib/open.c new file mode 100644 index 0000000..8c3fc58 --- /dev/null +++ b/4/linux/lib/open.c @@ -0,0 +1,25 @@ +/* + * linux/lib/open.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include +#include + +int open(const char * filename, int flag, ...) +{ + register int res; + va_list arg; + + va_start(arg,flag); + __asm__("int $0x80" + :"=a" (res) + :"0" (__NR_open),"b" (filename),"c" (flag), + "d" (va_arg(arg,int))); + if (res>=0) + return res; + errno = -res; + return -1; +} diff --git a/4/linux/lib/setsid.c b/4/linux/lib/setsid.c new file mode 100644 index 0000000..68516c7 --- /dev/null +++ b/4/linux/lib/setsid.c @@ -0,0 +1,10 @@ +/* + * linux/lib/setsid.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +_syscall0(pid_t,setsid) diff --git a/4/linux/lib/string.c b/4/linux/lib/string.c new file mode 100644 index 0000000..1182e63 --- /dev/null +++ b/4/linux/lib/string.c @@ -0,0 +1,14 @@ +/* + * linux/lib/string.c + * + * (C) 1991 Linus Torvalds + */ + +#ifndef __GNUC__ +#error I want gcc! +#endif + +#define extern +#define inline +#define __LIBRARY__ +#include diff --git a/4/linux/lib/wait.c b/4/linux/lib/wait.c new file mode 100644 index 0000000..2815c16 --- /dev/null +++ b/4/linux/lib/wait.c @@ -0,0 +1,16 @@ +/* + * linux/lib/wait.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include +#include + +_syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) + +pid_t wait(int * wait_stat) +{ + return waitpid(-1,wait_stat,0); +} diff --git a/4/linux/lib/write.c b/4/linux/lib/write.c new file mode 100644 index 0000000..df52e74 --- /dev/null +++ b/4/linux/lib/write.c @@ -0,0 +1,10 @@ +/* + * linux/lib/write.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +_syscall3(int,write,int,fd,const char *,buf,off_t,count) diff --git a/4/linux/mm/Makefile b/4/linux/mm/Makefile new file mode 100644 index 0000000..e0bcd73 --- /dev/null +++ b/4/linux/mm/Makefile @@ -0,0 +1,38 @@ +AR =ar +AS =as +LD =ld +LDFLAGS =-m elf_i386 -Ttext 0 -e startup_32 +CC =gcc -march=i386 +CFLAGS =-w -g -fstrength-reduce -fomit-frame-pointer -mcld \ + -finline-functions -nostdinc -fno-stack-protector -I../include +CPP =gcc -E -nostdinc -I../include + +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< + +OBJS = memory.o page.o + +all: mm.o + +mm.o: $(OBJS) + $(LD) -r -o mm.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +memory.o : memory.c ../include/signal.h ../include/sys/types.h \ + ../include/asm/system.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h diff --git a/4/linux/mm/memory.c b/4/linux/mm/memory.c new file mode 100644 index 0000000..a7b948a --- /dev/null +++ b/4/linux/mm/memory.c @@ -0,0 +1,469 @@ +/* + * 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 + +#include + +#include +#include +#include + +volatile void do_exit(long code); + +static inline volatile void oom(void) +{ + printk("\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)) + +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\t" + "1:" + "cld\n\t" /* by wyj */ + :"=a" (__res) + :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), + "D" (mem_map+PAGING_PAGES-1) + ); +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; + + /*printk("error\n");*/ + 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>= 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 ; ipid > 5) + printk(" --do_no_page: address=%x, pid=%d\n", address, current->pid); +*/ + 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(); +} \ No newline at end of file diff --git a/4/linux/mm/memory.c.orig b/4/linux/mm/memory.c.orig new file mode 100644 index 0000000..a88a5a5 --- /dev/null +++ b/4/linux/mm/memory.c.orig @@ -0,0 +1,466 @@ +/* + * 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 + +#include + +#include +#include +#include + +volatile void do_exit(long code); + +static inline volatile void oom(void) +{ + printk("\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)) + +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\t" + "1:" + "cld\n\t" /* by wyj */ + :"=a" (__res) + :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), + "D" (mem_map+PAGING_PAGES-1) + ); +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; + + printk("error\n"); + 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>= 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 ; istart_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(); +} \ No newline at end of file diff --git a/4/linux/mm/page.s b/4/linux/mm/page.s new file mode 100644 index 0000000..9ebf3f7 --- /dev/null +++ b/4/linux/mm/page.s @@ -0,0 +1,40 @@ +/* + * linux/mm/page.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * page.s contains the low-level page-exception code. + * the real work is done in mm.c + */ + +.globl page_fault + +page_fault: + xchgl %eax,(%esp) + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%edx + mov %dx,%ds + mov %dx,%es + mov %dx,%fs + movl %cr2,%edx + pushl %edx + pushl %eax + testl $1,%eax + jne 1f + call do_no_page + jmp 2f +1: call do_wp_page +2: addl $8,%esp + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret diff --git a/4/linux/new.patch b/4/linux/new.patch new file mode 100644 index 0000000..e270f03 --- /dev/null +++ b/4/linux/new.patch @@ -0,0 +1,11 @@ +--- old/011/new.h 2021-07-05 09:44:14.000000000 +0800 ++++ 011/new.h 2021-07-05 17:03:44.000000000 +0800 +@@ -7,7 +7,7 @@ + long d_ino; + off_t d_off; + unsigned short d_reclen; +- char * d_name; ++ char d_name[14]; + }; + + /* Return value of `mmap' in case of an error. */ diff --git a/4/linux/tools/build b/4/linux/tools/build new file mode 100644 index 0000000000000000000000000000000000000000..0fa6ae7f877d2483ad12f6df98491878b0f38b14 GIT binary patch literal 15144 zcmeHOe{@vUoxg8pGI?V{2uaYOX?T@#lCNna>cIHh8 zmQpZarI9YWp8nWXTYFZwwe@IEyOkE|#*J0!>F(05)$YpKrDtJJ5Z!~;QnpgEpU-{o z&Abe$XZOGTgPYvB_xtmH@Av+A@BVn7TiL%#Q54|>kMIgYaK~e;cQqn)NMC85`N&FK*ZX5l&;H4Oj1zW; zl`a4-7FU(Q?uwEQw-y88WD$bDkiXOglipPZ_m{!T%HX@p;Epo*lYqOQ_o?*995GK+ zA^p`}2c8Xs5`z)?bcxXWZqf5*G?CT|c_S)}k%TF-MlP4hiJTFR3eW(FNHUW*L?&yb zMJ$_3q|F#8xkxH2be+neBb+n!R5+0qTXG500Dc5G6X{SYleX!)xT(Kyd9U6PYNN>> z`>&LC>*DOnC&ndAE930KC!Uuu zt&MAhzb;`~9d9ALPr|f5P9L7wAz{`c&Te!fD`D0q-c9%(3A0x5)r8kcn6-^|tug-Iy1?28It}``mwHP0bT-cz-X{1=$j#XZ%}~ ziTzM4;G@$U_Z%O6r}0%^Gig(Y4^8a-0ZID_O8%G5c=r7LOK)GU9RB&4f->Llb*W?>)GX96Oe+fC5pdm-^MoZ7}hb3gVPQzky{>)}nKu{L39fE-o5E z3Jf{16(aji z2B)jAT{&Qj$1UTBY5X3#byxE?T12BRLCv#c7D74v3I>psw^7TcFl7zDC0*f@F62m( zu4GEryp2)0XIFC;8b=SxL~N~wuO4A>m$JAO&8i0Gy(^lt>{d`sX3E|T+0iqaN&at= z|81B`zA>>(@z(kz;WZL|ZWBhwVTL0jDK8W$)njZo>N-Y|CyE3#zD@A{IS9LpgoYwP zs!A3KO+`WzB{qP-YVh$nLp8qqp>zSAYW3$wrGuSjTnZJe?NPApJZSkI)1qJ89J+gKOvILLd`42Yk@^OxU$8n6I#&{sfY{{#r2t98kUEB;{&LjpELl}v>QmIa z9eT$~Z7~RT+0N?B?d-ACo!8DUP*(?ZeMCDC|Gw1Dhsp1;YwT|4_eeN_Qdp5`?JOz( zRHT&Jxpp5!eo!Qo+PRH{$BTqgJ4;o&iiA=-ucpLi5Zt3!0T?+<#OJ;UX=ijC2Hj~n47=^@*>RfrGK!w5qdf&8 zJK7cqq8d^olokg$5zZiI-nP^p;uQE%jl2v+pI4QYP%ynJAJNF4fdRHJXKdt?pwHBnr|q`b4D&zogW}@%iXk?=+1io7?t&uNsf7$#0#;@ETQ- zz511x&Utp9HWym&1!H)i?OD{!=0vuWUYvUL02xPLRj&PA;otGV-E_Bp*Tl(L``m%g zeD@#T*&3msd2)laAOZ`}Yq=`0Rcll|S%4j*FE?(Qknea4CGRz~mM?YWR`9;LAb*kA zm=6yb*J*==L^7%kW-?~ph?rX5Fbi2Nzcp_fDQ!~@=%_J#m$vB!Es+BMU1C-LE$i0Z zA$l`~WHh}H0(c+R$ftoZ5ivqyeF@MKd97tZbdk0odNp1@MPD*$426?!eyt@HzBiN8 zmRUT@!_jHfU@)jz3|ESc>F{9E(9Dd+cXn+djb7-&^4(sG#N0*;UTT1ZKS7X@#3q+ zr@1WABH?6Gi-i+OEOyGBBpZ}3m?c8FpKO*H9aR+nm<@cNFPOd1nGB> zp2EZHXGrhC!&${EL>6fiQm*S6l>62TWt*y8R_*gW2)qVd6XG`L9Y-o^)KxXrH_xsz zeIw$=OP7A4qj^4_6rvk(F3P|fPKe$bjrs-d88r)N(?)EI7 zRpG6u{0YkBBI_ZYi6? z)SjQz_-My#i0dHhp)3MzKZ4i~TpJN6Z`1>0?uE<*47w05c;JEuE_mRA2QGNvf(I^m z;DQG(c;JEuE_mSop$GQu_NiG!nuevBjMYewjQUg@-idJ>oXkVY^XSWva&g16&}zV3 z2NC}5xv9)|aNdlgI`RDdIO9Wl7%=w)b|dmElk2Fthz#7P5zqTnuBUECE@f!zDWppPBl~|2JU@4YF2K~{4iYlq(hZBcy6!(ddd17Rmz;AZ4Gtnj?f1*z9p`F&8miaNx^ zQ@I@^AfJ;+e?lTF{sEjmUj(tz1!=wtA4|HF#9bMQsl1hvZ&S$_ zc`AIK4@v$i0ncX2J|VMDko`Gk{l3e|q4={%DldiTqrTrhjYR!2sJ_#GieI7r7l^(O z=kZrPO8i;oRrQll>H7oo)#@9ho@2gN<>8-iius1X5h(Ksg+OEA1rihqGzDr%@kl@m z+)PzI2{Z@l2vkU*CBSPi}D*p!2w!nu}aFKEhNoU|U1gaGp+!d%LTdf4H z4{V}kb;_&2bq7KOE>>;;up-b;;1Z=Dz^cH{2sA2m-%WuA0!_;A0jv(_1Qsa&0=E7@ zh~cI$J`0@RcNK6|Y)_h9eGD{W z*_skt3vr2EC@QZ-Uac5e%!D^PD(*W;Zk5Bc;xjc=E-G&a^>%-V`!VYCAo$xj83~mx z@ORLKs#*Z6fAQ1ESF3k{-+$eAk*`%>#;4@A{+pO)_I|!Q_V2HiB;-Sb>{D6zFS>E{dY0H zTBUdVDGsM~Dz~@%Y32vStZ2dzN;}8?A|RBt~mi7&YsF+ zwO67P<$CJm6#;IODUZDc!8zL?Vi6!X=SB&U;Ca|rmk`Pg6!Ds8wYo?VIHT1iqDAPG zRySCP3FXFLL&|HO67|EdU+I=Z)BK%QznvD?;AyQcKPZ$QHl4?8L<7@g*}(5Zyx|yl zadteHE~|Wookb{flm#{QRo1d|&-E6?34%t{GL-bxzY* z570$zkS0~&$y&nSIPZEsuODYe5C`Z!<-?j;_?4I4*t*!|P3A>wA)k{MwuT~+*2I#o zCDygA;842IisxBtA{|K`7AgO z5aGS=4R7EzsqCuyAD1g&nRj?RXpX$8=WwPZ%wau^!xW+O=p)|&uj%>3eTFFT0E<*f zNr~0Q<P?;I^70u*yFM7k5)vbkV*?Z zo5_oCZfKZd5%eLU56d$u%HiNd=$T~H$rsZ3#8BFZYLR$2M*&ov!V%FL&!mjjbRlZC zX7a(bu?3?6_ZEX~q4xG*GCbJYW+6n$)S@z3Q+UFvtk7lsISJA|mNTV;^g^0<^g2r3 zkUn;tA*+}TM;8~}%J!v>Rek*{g}j=lr;QPM0&MM80&RpoWhI@glst5U&-H@-X9f6N zdNd_n!wOq)m@t?N@U+!n)9QoUms+p&(y9efu}FLwhdc5S9H9n>v|ucgN|?b|E}Sxg z*$jMulgMB!oe5&}Mm7htxr`}K1zlc{_0c)bS=SiO@Zf$OZGlUNvYn+1(6N}ZYe7*V zYxOSOsS63RD@mVHSw71V!A7;XG4T=tQi-f=+-p!St*8= zqzq$hBr@q~fDT&DNRr>mWRs={;ZPU9CR;xg0{~KnC%GbI#C6#SdOQkA2eKsea4r|# zYDqZxdm~_j@^C5z%8iR!)2#3={2JjY`NXIfxK0G*> z!(3ofFl-Db&$p2Ob>Mmu&j~&stUJ>H@Dwcu(ipBp4O4^kP($G8>|&wE)e*RzMgcN8$!x5!E!zN+(9 zDPCtJAJ@OnL-B6FP9QJC-H2SfvRtlPmq3VXXs2A_88;(xt;;;uy940Ukaw30P~3+Y zKqMd6%O?0}kE4%u;9Ke_qQkcfe7nGRyUoY7zQgwr^0bxZaQ%M-d{-jx1oAO_9?{_w zToZ#ZPJ$h119=}oboh9$ew;`eVh8dudvJzzI&dPXIwa*7tSry$-%U z*ysd@?`a?$K6%%Dj*$1@%lW?V;)~*HOcZ=)9VVoXz88_l%cX2%c{d-I#T^>io%~Oc z$F)bv$NiD&Kf?D0cxWF@=jGPgKf?DG=xht}$vgkxo3wFAWVvkLV~CDE-myMAgFf1L z!o|n^7oknR$4(x`_g#El;Ohb(%W(pE7|tLPPy2Wsw_BT;?+>8UzBWV#@~x3M>xVQ( zKYo}xVXr0x{)@q(%`M}57EI?HqrmO(T~WsO?FB+Cc8D&%)-t}MSK&V9417y%KBpc# zTd?2Y>VFhrL^qN?K_bK4MOdKEppPi_WhcDefu$Y>?|dMTm2JT>!Xe`-Ml^SU=0dns zgL8V|cPPYIU$7=s;w`;kn&aztA5fCY2pp}$}pTd*~| za9mq3TLt~o-uLs0Itg=}GUK(MJRFY}Y|TKWR5S3Vis@W)C3a$fQAGWhahzbC;b-kYjM>eNTUj4Ca_ zdWv+9m?c6;+gxxDU}rCw^iKnpH}sJzkd7gB{H4HOdjWI*7>U&PRiuu875H~Q33i}8 z?tV&m9JD(hvE&tuntufBw$~Pw3HkpOIj4Lqjm1g8PWvcmpAP^#<&(~DOXNIf4>0O2`vAoW$+QePI;6+;i7BcKjVTa z&x&7fmp2Ey3v;b002KN|hy{S%-yC(8(Qhq-qh)XbFh4^HOMSD%ZosRH^_wTY0N5GN z^gp+<`5d?WGh2KGaLFI@#9sq;Hmk`0Ex^u>7UAarJG)?nzX#X}8Zxf}cJ#5n|6E3Y z7ck#*C{u?wJ?_Cef&LJYoS8QZu^9Fnv>Zpd|vV~PBX z&CO(e9_Plu>m{vaAH4C4x-#B;MvsTnQJBbEm(z$?oN~{~{u=MRZ|mH=_IG%0l-w+E zBV2xEO9;7tV%J`eW%c+LcnRw@8zuJ$ti2*`3+p!z>+9{l*_#aK^XQnm4j((6fX<)U N^|Cbi(V*L${{d;jp)>#h literal 0 HcmV?d00001 diff --git a/4/linux/tools/build.c b/4/linux/tools/build.c new file mode 100644 index 0000000..8b8c7ab --- /dev/null +++ b/4/linux/tools/build.c @@ -0,0 +1,171 @@ +/* + * linux/tools/build.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: max 510 bytes of 8086 machine code, loads the rest + * - setup: max 4 sectors of 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + */ + +#include /* fprintf */ +#include +#include /* contains exit */ +#include /* unistd.h needs this */ +#include +#include +#include /* contains read/write */ +#include + +#define MAJOR(a) (((unsigned)(a))>>8) +#define MINOR(a) ((a)&0xff) + +#define MINIX_HEADER 32 +#define GCC_HEADER 0x80 /* 0x1000 */ /* by wyj */ + +#define SYS_SIZE 0x3000 + +#define DEFAULT_MAJOR_ROOT 0x03 //0x02 /* by wyj */ +#define DEFAULT_MINOR_ROOT 0x00 //0x1d + +/* max nr of sectors of setup: don't change unless you also change + * bootsect etc */ +#define SETUP_SECTS 4 + +#define STRINGIFY(x) #x + +void die(char * str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + +void usage(void) +{ + die("Usage: build bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char ** argv) +{ + int i,c,id; + char buf[1024*4]; /* by wyj */ + char major_root, minor_root; + struct stat sb; + + if ((argc != 4) && (argc != 5)) + usage(); + if (argc == 5) { + if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = MAJOR(sb.st_rdev); + minor_root = MINOR(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + if ((major_root != 2) && (major_root != 3) && + (major_root != 0)) { + fprintf(stderr, "Illegal root device (major = %d)\n", + major_root); + die("Bad root device --- major #"); + } + for (i=0;i0 ; i+=c ) + if (write(1,buf,c)!=c) + die("Write call failed"); + close (id); + if (i > SETUP_SECTS*512) + die("Setup exceeds " STRINGIFY(SETUP_SECTS) + " sectors - rewrite build/boot/setup"); + fprintf(stderr,"Setup is %d bytes.\n",i); + for (c=0 ; c sizeof(buf)) + c = sizeof(buf); + if (write(1,buf,c) != c) + die("Write call failed"); + i += c; + } + + if ((id=open(argv[3],O_RDONLY,0))<0) + die("Unable to open 'system'"); + if (read(id,buf,GCC_HEADER) != GCC_HEADER) + die("Unable to read header of 'system'"); +// if (((long *) buf)[5] != 0) +// die("Non-GCC header of 'system'"); + for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c ) + if (write(1,buf,c)!=c) + die("Write call failed"); + close(id); + fprintf(stderr,"System is %d bytes.\n",i); + if (i > SYS_SIZE*16) + die("System is too big"); + return(0); +} diff --git a/4/linux/tools/system b/4/linux/tools/system new file mode 100644 index 0000000000000000000000000000000000000000..2957835d13182995a60ee2333624e9f8af76c69f GIT binary patch literal 295153 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

TexHC_U!q>N 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 YFFA`gRW-kIQ|3HV?(~|FrXYm>0qv%KcK`qY literal 0 HcmV?d00001