Full Code of VOID001/neu-os for AI

master ba080fb5231c cached
69 files
129.2 KB
47.1k tokens
189 symbols
1 requests
Download .txt
Repository: VOID001/neu-os
Branch: master
Commit: ba080fb5231c
Files: 69
Total size: 129.2 KB

Directory structure:
gitextract_l9h0t38n/

├── .gitignore
├── Makefile
├── Makefile.header
├── bochsrc
├── boot/
│   ├── Makefile
│   ├── binary.s
│   ├── bochsrc
│   ├── bootsect.s
│   ├── head.s
│   ├── ldS.ld
│   └── setup.s
├── docs/
│   └── EXPERIMENT.md
├── include/
│   ├── asm/
│   │   ├── io.h
│   │   ├── segment.h
│   │   └── system.h
│   ├── errno.h
│   ├── linux/
│   │   ├── fs.h
│   │   ├── head.h
│   │   ├── kernel.h
│   │   ├── lib.h
│   │   ├── mm.h
│   │   ├── sched.h
│   │   ├── sys.h
│   │   └── tty.h
│   ├── serial_debug.h
│   ├── signal.h
│   ├── stdarg.h
│   ├── stddef.h
│   ├── sys/
│   │   ├── types.h
│   │   └── wait.h
│   └── unistd.h
├── init/
│   ├── Makefile
│   └── main.c
├── kernel/
│   ├── Makefile
│   ├── asm.s
│   ├── blk_drv/
│   │   ├── Makefile
│   │   ├── blk.h
│   │   └── request_scan_algo.c
│   ├── chr_drv/
│   │   ├── Makefile
│   │   ├── do_keyboard.c
│   │   ├── keyboard.s
│   │   ├── tty_io.c
│   │   ├── tty_io.c.orig
│   │   ├── tty_io.c.rej
│   │   ├── tty_queue.c
│   │   ├── tty_read.c
│   │   └── vga_console.c
│   ├── exit.c
│   ├── fork.c
│   ├── libc_restore.s
│   ├── panic.c
│   ├── printk.c
│   ├── sched.c
│   ├── serial_debug.c
│   ├── signal.c
│   ├── signal_demo.c
│   ├── sys.c
│   ├── system_call.s
│   ├── traps.c
│   ├── vsprintf.c
│   └── vsprintf.c.old
├── ldS.ld
├── lib/
│   ├── Makefile
│   ├── getline.c
│   └── printf.c
└── mm/
    ├── Makefile
    ├── memory.c
    ├── mm_test.c
    └── page.s

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.o
*.a
*.sym
boot/bootsect
env.sh
boot/bootimg
boot/setup
boot/binary
.workdoc/
*.ini
tags
bootimg
system
.ycm_extra_conf.py


================================================
FILE: Makefile
================================================
include Makefile.header
#AS=i686-elf-as
#LD=i686-elf-ld
#OBJCOPY=i686-elf-objcopy
#QEMU=qemu-system-i386
#BOCHS=bochs
#STRIP=i686-elf-strip

.PHONY=clean run all boot/head.o boot/bootsect boot/setup kernel/kernel.o

all: bootimg

OBJS = boot/head.o init/main.o kernel/kernel.o mm/mm.o lib/lib.o
DRIVERS = kernel/chr_drv/chr_drv.a

system: $(OBJS) $(DRIVERS)
	@$(LD) -T ldS.ld $(OBJS) $(DRIVERS) -o system.sym
	@$(STRIP) system.sym -o system.o
	@$(OBJCOPY) -O binary -R .note -R .comment system.o system

kernel/chr_drv/chr_drv.a:
	@make -C kernel/chr_drv/

kernel/kernel.o:
	@make -C kernel

boot/head.o:
	@make head.o -C boot

boot/bootsect:
	@make bootsect -C boot

boot/setup:
	@make setup -C boot

init/main.o:
	@make main.o -C init

lib/lib.o:
	@make lib.o -C lib

mm/mm.o:
	@make -C mm

# Squash the bootimg together
bootimg: boot/setup boot/bootsect system
	@echo -e "\e[1;33mStart building NEU-OS image..."
	@dd if=boot/bootsect of=bootimg bs=512 count=1 2>/dev/null
	@dd if=boot/setup of=bootimg bs=512 count=4 seek=1 2>/dev/null
	@dd if=system of=bootimg bs=512 seek=5 2>/dev/null
	@echo -e "\e[1;0;32mBuild bootimg done"

run: bootimg
	$(QEMU) -boot a -fda bootimg -serial stdio

run_bochs: bootimg
	$(BOCHS) -q

run_debug:
	$(QEMU) -boot a -fda bootimg -S -s

disassemble: system.sym
	objdump -S system.sym | less

clean:
	@rm -f bootsect *.o setup *.sym bootimg a.out binary head  system
	@make clean -C boot
	@make clean -C kernel
	@make clean -C mm
	@make clean -C init
	@make clean -C lib


================================================
FILE: Makefile.header
================================================
AS=as --32
CC=gcc
LD=ld -m elf_i386
OBJCOPY=objcopy
QEMU=qemu-system-i386
BOCHS=bochs
STRIP=strip
_ALLWARN=-Wall -Wextra -Waddress -Wconversion
CFLAGS=-g -m32 -fno-builtin -fno-stack-protector -fomit-frame-pointer -fstrength-reduce $(_ALLWARN)


================================================
FILE: bochsrc
================================================
floppya: 1_44="./bootimg", status=inserted  # Use Floppy Disk A for booting
boot: floppy
display_library: x, options="gui_debug"    # Uncomment this line to enable GUI debugger
#gdbstub: enabled=1, port=1234



================================================
FILE: boot/Makefile
================================================
include ../Makefile.header

LDFLAGS	+= -Ttext 0

.PHONY=clean run all

all: bootsect setup head.o

# Bootsector is RAW Binary
# So is setup program

bootsect: bootsect.s
	@$(AS) -n -g -o bootsect.o bootsect.s
	@$(LD) -T ldS.ld -o bootsect bootsect.o
	@cp -f bootsect bootsect.sym
	@$(OBJCOPY) -j .text -S -O binary bootsect

setup: setup.s
	@$(AS) -n -g -o setup.o setup.s
	@$(LD) -T ldS.ld -o setup setup.o
	@cp -f setup setup.sym
	@$(OBJCOPY) -j .text -S -O binary setup

head.o: head.s
	@$(AS) -n -g -o head.o head.s

	#@cp -f head head.sym
	#@$(OBJCOPY) -R .pdr -R .comment -R.note -S -O binary head
	#@$(LD) $(LDFLAGS) -o head head.o

binary: binary.s
	@$(AS) -n -g -o binary.o binary.s
	@$(LD) $(LDFLAGS) -o binary binary.o
	@cp -f binary binary.sym
	@$(OBJCOPY) -R .pdr -R .comment -R.note -S -O binary binary

#bootimg: setup bootsect head binary
#	@dd if=bootsect of=bootimg bs=512 count=1
#	@dd if=setup of=bootimg bs=512 count=4 seek=1
#	@dd if=binary of=bootimg bs=512 seek=5
#	@echo "Build bootimg done"

run: bootimg
	$(QEMU) -boot a -fda bootimg

run_bochs: bootimg
	$(BOCHS) -q

run_debug:
	$(QEMU) -boot a -fda bootimg -S -s

clean:
	@rm -f bootsect *.o setup *.sym bootimg a.out binary head 


================================================
FILE: boot/binary.s
================================================
# This code is used to test whether you make things right 
# switching to protected mode, do not modify it, it will work
# Just as is

.code32

.global _start, begtext, begbss, begdata

_start:
	# Just have a try to move one char in the memory
	#mov 'O', %al
	#movb %al, 0xb8000
	#movb $0b00000111, %al
	#movb %al, 0xb8001
	#mov $0x0c63, %ax		# Don't mess up the order
	#mov %ax, 0xb8000

put_red_string:
	xor %ecx, %ecx			# Clear the counter
loop:
	mov $red_str, %ebx
	add %cx, %bx
	movb (%ebx), %al
	movb $0x0c, %ah
	mov $0xb8A00, %ebx
	shl %ecx
	add %ecx, %ebx
	shr %ecx
	movw %ax, (%ebx)
	inc %ecx
	cmp $62, %ecx
	jne loop
halt:
	jmp halt

red_str:
	.ascii "You've entered protected mode!                           "
	# len = 0x1e


================================================
FILE: boot/bochsrc
================================================
floppya: 1_44="./bootimg", status=inserted  # Use Floppy Disk A for booting
boot: floppy
display_library: x, options="gui_debug"    # Uncomment this line to enable GUI debugger


================================================
FILE: boot/bootsect.s
================================================
#################################################################
#                                                               #
#  Lesson 3: 将操作系统装载到正确的地址,设置GDT后              #
#  切换到保护模式                                               #
#                                                               #
#                                                               #
#################################################################

# 以下代码将操作系统完整的装载到内存中 Layout 为:
# 0x9000:0000 bootsect
# 0x9000:0200 setup
# 0x1000:0000 system
# 这段代码没有需要学生进行修改的地方
# 有兴趣的同学可以仔细读一下 read_it 子程序

	.code16        # 指定语法为 十六位汇编
	.equ SYSSIZE, 0x3000 # System Size in Clicks(1 click = 16bits)

# rewrite with AT&T syntax by falcon <wuzhangjin@gmail.com> at 081012
# Modified by VOID001<zhangjianqiu13@gmail.com> at 2017 03 05
# loads pretty fast by getting whole sectors at a time whenever possible.

	#.global _start, begtext, begdata, begbss, endtext, enddata, endbss
	.global _start # 程序开始处
	.global begtext, begdata, begbss, endtext, enddata, endbss
	.text
	begtext:
	.data
	begdata:
	.bss
	begbss:
	.text

	.equ SETUPLEN, 0x04			# Setup 程序占用的扇区数
	.equ BOOTSEG, 0x07c0		# 当此扇区被BIOS识别为启动扇区装载到内存中时,装载到0x07c0段处
	.equ INITSEG, 0x9000		# 我们的bootsector代码会被移动到这里
	.equ SETUPSEG, 0x9020		# setup.s的代码会被移动到这里(Bootsector 之后的一个扇区)
	.equ SYSSEG, 0x1000		    # system 程序的装载地址
								# 此时我们处于实模式(REAL MODE)中,对内存的寻址方式为
								# (段地址 << 4 + 偏移量) 可以寻址的线性空间为 20 位
	.equ ROOT_DEV, 0x301			# 指定/dev/fda为系统镜像所在的设备
	.equ ENDSEG, SYSSEG + SYSSIZE

	ljmp    $BOOTSEG, $_start   # 修改cs寄存器为BOOTSEG, 并跳转到_start处执行我们的代码

_start:
	mov $BOOTSEG, %ax			# 这里我们将启动扇区从0x07c0:0000处移动到0x9000:0000
	mov %ax, %ds				# rep mov 的用法如下
								# 源地址ds:si = 0x07c0:0000
								# 目的地址es:di = 0x9000:0000
								# 移动次数 %cx = 256
								# 因为是movsw所以每次移动一个Word(2Byte) 256 * 2 = 512 Byte	即为启动扇区的大小
	mov $INITSEG, %ax
	mov %ax, %es
	mov $256, %cx
	xor %si, %si
	xor %di, %di
	rep movsw					# 进行移动

	ljmp $INITSEG, $go			# 长跳转同时切换CS:IP
go:
	mov %cs,%ax				# 对DS, ES, SS寄存器进行初始化
	mov %ax, %ds
	mov %ax, %es			# 设置好 ES 寄存器,为后续输出字符串准备
	mov %ax, %ss			
	mov $0xFF00, %sp		# 设置好栈

load_setup:
	# 这里我们需要将软盘中的内容加载到内存中,并且跳转到相应地址执行代码
	mov $0x0000, %dx		# 选择磁盘号0,磁头号0进行读取
	mov $0x0002, %cx		# 从二号扇区,0轨道开始读(注意扇区是从1开始编号的)
	mov $INITSEG, %ax		# ES:BX 指向装载目的地址
	mov %ax, %es
	mov $0x0200, %bx		
	mov $02, %ah			# Service 2: Read Disk Sectors
	mov $4, %al				# 读取的扇区数
	int $0x13				# 调用BIOS中断读取
	jnc demo_load_ok		# 没有异常,加载成功
	mov $0x0000, %dx
	mov $0x0000, %ax		# Service 0: Reset the Disk
	int $0x13
	jmp load_setup			# 并一直重试,直到加载成功

demo_load_ok:				# Here will jump to where the demo program is
	#mov $SETUPSEG, %ax
	#mov %ax, %cs			# This is awful!! Do not change CS alone!! It will result in GDB cannot debug the code
							# And, of course the code will not work
	#mov %ax, %ds
	#ljmp $SETUPSEG, $0		# jump to where the demo program exists (Demo code, removed now)

	mov $0x00, %dl
	mov $0x0800, %ax
	int $0x13
	mov $0x00, %ch
	mov %cx, %cs:sectors+0
	mov $INITSEG, %ax
	mov %ax, %es

# 下面的程序用来显示一行信息
print_msg:
	mov	$0x03, %ah			# 在输出我们的信息前读取光标的位置, 会将光标当前所在行,列存储在DX里(DH为行, DL为列)
	xor	%bh, %bh
	int	$0x10
	
	mov	$20, %cx			# Set the output length
	mov	$0x0007, %bx		# page 0, color = 0x07 (from wikipedia https://en.wikipedia.org/wiki/INT_10H)
	mov $msg1, %bp
	mov	$0x1301, %ax		# write string, move cursor
	int	$0x10				# 使用这个中断0x10的时候,输出的内容是从 ES:BP 中取得的,因而要设置好 ES 和 BP


# 接下来将整个系统镜像装载到0x1000:0000开始的内存中
	mov $SYSSEG, %ax
	mov %ax, %es			# ES 作为参数
	call read_it
	call kill_motor

		
	mov %cs:root_dev,%ax
	cmp $0, %ax
	jne root_defined		# ROOT_DEV != 0, Defined root
	mov %cs:sectors+0, %bx    # else check for the root dev
	mov $0x0208, %ax
	cmp $15, %bx
	je	root_defined		# Sector = 15, 1.2Mb Floopy Driver
	mov $0x021c, %ax
	cmp $18, %bx			# Sector = 18 1.44Mb Floppy Driver
	je root_defined

undef_root:					# If no root found, loop forever
	jmp undef_root

root_defined:

	mov %ax, %cs:root_dev+0

# Now everything loaded into memory, we jump to the setup-routine
# which is now located at 0x9020:0000

	ljmp $SETUPSEG, $0


# Here is the read_it routine and kill_motor routine
# read_it 和 kill_motor 是两个子函数,用来快速读取软盘中的内容,以及关闭软驱
# 电机使用,下面是他们的代码

# 首先定义一些变量, 用于读取软盘信息使用

sread:  .word 1 + SETUPLEN		# 当前轨道读取的扇区数
head:   .word 0					# 当前读头
track:	.word 0					# 当前轨道


read_it:
	mov %es, %ax
	test $0x0fff, %ax
die:
	jne die				# If es is not at 64KB(0x1000) Boundary, then stop here
	xor %bx, %bx
rp_read:
	mov %es, %ax
	cmp $ENDSEG, %ax
	jb ok1_read			# If $ENDSEG > %ES, then continue reading, else just return
	ret
ok1_read:

	mov %cs:sectors+0, %ax
	sub sread, %ax
	mov %ax, %cx		# Calculate how much sectors left to read
	shl $9, %cx			# cx = cx * 512B
	add %bx, %cx		# current bytes read in now
	jnc ok2_read		# If not bigger than 64K, continue to ok_2
	je ok2_read
	xor %ax, %ax
	sub %bx, %ax
	shr $9, %ax
ok2_read:
	call read_track
	mov %ax, %cx		# cx = num of sectors read so far
	add sread, %ax

	cmp %cs:sectors+0, %ax
	jne ok3_read
	mov $1, %ax
	sub head, %ax
	jne ok4_read
	incw track
ok4_read:
	mov %ax, head
	xor %ax, %ax
ok3_read:
	mov %ax, sread
	shl $9, %cx
	add %cx, %bx		# HERE!!! I MADE A FAULT HERE!!!
	jnc rp_read			# If shorter than 64KB, then read the data again, else, adjust ES to next 64KB segment, then read again
	mov %es, %ax
	add $0x1000, %ax
	mov %ax, %es		# Change the Segment to next 64KB
	xor %bx, %bx
	jmp rp_read

# Comment for routine 0x13 service 2
# AH = 02
# AL = number of sectors to read	(1-128 dec.)
# CH = track/cylinder number  (0-1023 dec., see below)
# CL = sector number  (1-17 dec.)
# DH = head number  (0-15 dec.)
# DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
# ES:BX = pointer to buffer

read_track:				# This routine do the actual read
	push %ax
	push %bx
	push %cx
	push %dx
	mov track, %dx		# Set the track number $track, disk number 0
	mov sread, %cx
	inc %cx
	mov %dl, %ch
	mov head, %dx
	mov %dl, %dh
	mov $0, %dl
	and $0x0100, %dx
	mov $2, %ah
	int $0x13
	jc bad_rt
	pop %dx
	pop %cx
	pop %bx
	pop %ax
	ret

## Debug the load function
#d_load_setup:
#	# 这里我们需要将软盘中的内容加载到内存中,并且跳转到相应地址执行代码
#	mov $0x0000, %dx		# 选择磁盘号0,磁头号0进行读取
#	mov $0x0006, %cx		# 从二号扇区,0轨道开始读(注意扇区是从1开始编号的)
#	mov $SYSSEG, %ax		# ES:BX 指向装载目的地址
#	mov %ax, %es
#	mov $0x0200, %bx		
#	mov $02, %ah			# Service 2: Read Disk Sectors
#	mov $4, %al				# 读取的扇区数
#	int $0x13				# 调用BIOS中断读取
#ddemo: jnc ddemo		# 没有异常,加载成功
#	mov $0x0000, %dx
#	mov $0x0000, %ax		# Service 0: Reset the Disk
#	int $0x13
#	jmp d_load_setup			# 并一直重试,直到加载成功
## END DEBUG


bad_rt:
	mov $0, %ax
	mov $0, %dx
	int $0x13
	pop %dx
	pop %cx
	pop %bx
	pop %ax
	jmp read_track

kill_motor:
	push %dx
	mov $0x3f2, %dx
	mov $0, %al
	outsb
	pop %dx
	ret


sectors:
	.word 0


msg1:
	.byte 13,10
	.ascii "Hello VOID001!"
	.byte 13,10,13,10

	.= 508                      # 这里是对齐语法,等价于 .org 表示在该处补零,一直补到地址为 508 的地方 (即第一扇区的最后四字节)
                                # 然后在这里的前两个字节填充根文件系统所在的设备号 ROOT_DEV,后两个字节填充 0xaa55 魔术值
                                # BIOS 会识别硬盘中第一扇区以 0xaa55 结尾的为启动扇区,于是 BIOS 会装载代码并且运行

root_dev:
	.word ROOT_DEV
boot_flag:
	.word 0xAA55

.text
	endtext:
.data
	enddata:
.bss
	endbss:


================================================
FILE: boot/head.s
================================================
#######################################################
#    Lesson 5: 设置最初的页表,进行系统初始化		  #
#    这里要注意,页表我们设置在了0x00000000 ,这会覆盖  #
#	 head.s的代码                                     #
#######################################################

.text

.global idt, gdt, pg_dir, tmp_floppy_area
pg_dir:						# 这里说明pg_dir要写在这里,最后会覆盖掉startup code
.global startup_32

startup_32:
	movl $0x10, %eax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	lss stack_start, %esp		# Setup the Stack to stack_start, Change SS and %esp together
	call setup_idt
	call setup_gdt
	mov $0x10, %eax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	lss stack_start, %esp
	xorl %eax, %eax
1:	incl %eax
	movl %eax, 0x000000		#Compare the address 0 with 1MB
	cmpl %eax, 0x100000
	je 1b					# If A20 not enabled, loop forever


# 检查486协处理器是否存在

	movl %cr0, %eax
	andl $0x80000011, %eax
	orl	$2, %eax			# Set MP Bit
	movl %eax, %cr0
	call check_x87
	jmp after_page_tables


check_x87:
	fninit
	fstsw %ax
	cmpb $0, %al
	je 1f
	movl %cr0, %eax
	xorl $6, %eax
	movl %eax, %cr0
	ret
.align 2
1:	.byte 0xDB, 0xE4  # 287 协处理器码
	ret


setup_idt:
	# We fill all the IDT entries with a default entry
	# that will display a message when any interrupt is triggered
	
	lea ignore_int, %edx
	movl $0x00080000, %eax
	movw %dx, %ax
	mov $0x8E00, %dx
	lea idt, %edi

	mov $256, %cx
rp_sidt:					# Fill the IDT Table will default stub entry
	mov %eax, (%edi)
	mov %edx, 4(%edi)
	addl $8, %edi
	dec %cx
	jne rp_sidt
	lidt idt_descr			# Load IDT Register

setup_gdt:
	lgdt gdt_descr
	ret

# Make place for pg directory
.org 0x1000					# Align to 4KB Boundary
pg0:

.org 0x2000
pg1:

.org 0x3000
pg2:

.org 0x4000
pg3:

.org 0x5000

tmp_floppy_area:
	.fill 1024, 1, 0		# fill here with 1024 (1)Bytes of 0

after_page_tables:
	push $0
	push $0
	push $0
	pushl $L6
	pushl $main
	jmp setup_paging
L6:
	jmp L6

int_msg:					# Message to display when interrupt happen
	.asciz "Unknown interrupt\n"

.align 2					# Align to 4Bytes

# This routine is used to print a default message when any interrupt comes

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
	
	// This is used to tell 8259A keyboard interrupts handling done
	// Temporary use, will remove later
//	mov $0x20, %al
//	out %al, $0x20
//	// End 
//	jmp 1f
//1:  jmp 1f
//1:  out %al, $0xA0
	
	popl %eax
	pop %fs
	pop %es
	pop %ds
	popl %edx
	popl %ecx
	popl %eax
	iret

.align 2
setup_paging:
	movl $1024 * 5, %ecx		# We have 5 pages (one page pg_dir + 4 pages)
	xorl %eax, %eax
	xorl %edi, %edi
	cld;rep;stosl				
	
	# Setup Page Directory(Only 4)
	movl $pg0+7, pg_dir			# +7 Means set attribute present bit, r/w user
	movl $pg1+7, pg_dir+4		# -- -- ---
	movl $pg2+7, pg_dir+8		# -- -- ---
	movl $pg3+7, pg_dir+12		# -- -- ---
	
	# Then we fill the rest of the page table
	# We mapped the highest linear address to Phy Address 16MB
	movl $pg3 + 4092, %edi
	movl $0xfff007, %eax			# 7 means present, r/w user attribute

	std
1:	stosl
	subl $0x1000, %eax
	jge 1b

	# Set up the Page Dir register cr3
	xorl %eax, %eax
	movl %eax, %cr3

	# Then enable paging
	movl %cr0, %eax
	orl $0x80000000, %eax			# Set the paging bit
	movl %eax, %cr0					# ENABLE PAGING NOW!
	ret

.align 2
.word 0

idt_descr:
	.word 256*8 - 1			# Length in Bytes - 1
	.long idt				# Base

.align 2
.word 0



gdt_descr:
	.word 256*8 - 1
	.long gdt
	.align 8

idt:  .fill 256, 8, 0		# Forget to set IDT at first QAQ

gdt:
	# Empty Entry (FIRST ENTRY)
	.quad 0x0000000000000000
	# BaseAddress = 0x00000000
	# Limit = 0xfff
	# Granularity = 1 means 4KB Segment limit are 4KB unit
	# TYPE = 0xA Executable Read
	# DPL = 0x00 S = 1 P = 1
	# Code Segment
	.quad 0x00c09a0000000fff
	# BaseAddress = 0x00000000
	# Limit = 0xfff
	# Granularity = 1 means 4KB Segment limit are 4KB unit
	# TYPE = 0x2 Read/Write
	# DPL = 0x00 S = 1 P = 1
	# Data Segment
	.quad 0x00c0920000000fff
	# Temporaray
	.quad 0x0000000000000000
	.fill 252, 8, 0



================================================
FILE: boot/ldS.ld
================================================
SECTIONS {
    .text 0x0000 : {
        *(.text)
    }

    /DISCARD/ : {

    }
}


================================================
FILE: boot/setup.s
================================================
###########################################################
#														  #
#		Lesson 4 设置IDT 对8259A进行编程    			  #
#													      #
###########################################################

.code16

.text

.equ SETUPSEG, 0x9020
.equ INITSEG, 0x9000
.equ SYSSEG, 0x1000
.equ LEN, 54

.global _start, begtext, begdata, begbss, endtext, enddata, endbss

.text
	begtext:
.data
	begdata:
.bss
	begbss:
.text

	
show_text:
	mov $SETUPSEG, %ax
	mov %ax, %es
	mov $0x03, %ah
	xor %bh, %bh
	int $0x10					# these two line read the cursor position
	mov $0x000a, %bx			# Set video parameter
	mov $0x1301, %ax
	mov $LEN, %cx
	mov $msg, %bp
	int $0x10

# 下面的代码调用BIOS中断将硬件的一些状态保存在0x9000:0000开始的内存处
# (注意这里会覆盖bootsect,不过无所谓,因为我们不再需要它了)
	ljmp $SETUPSEG, $_start
_start:

# 保存光标位置
# Comment for routine 10 service 3
# AH = 03
# BH = video page
# on return:
# CH = cursor starting scan line (low order 5 bits)
# CL = cursor ending scan line (low order 5 bits)
# DH = row
# DL = column
	mov $INITSEG, %ax
	mov %ax, %ds
	mov $0x03, %ah
	xor %bh, %bh
	int $0x10
	mov %dx, %ds:0
# 取扩展内存大小的值
# Comment for routine 0x15 service 0x88
# AH = 88h
# on return:
# CF = 80h for PC, PCjr
# = 86h for XT and Model 30
# = other machines, set for error, clear for success
# AX = number of contiguous 1k blocks of memory starting
# at address 1024k (100000h)
	mov $0x88, %ah
	int $0x15
	mov %ax, %ds:2

# 取显卡显示模式
# Comment for routine 10 service 0xf
# AH = 0F
# on return:
# AH = number of screen columns
# AL = mode currently set (see VIDEO MODES)
# BH = current display page
	mov $0x0f, %ah
	int $0x10
	mov %bx, %ds:4
	mov %ax, %ds:6

# 检查显示方式(EGA/VGA)并取参数
# Comment for routine 10 service 0x12
# We use bl 0x10
# BL = 10  return video configuration information
# on return:
# BH = 0 if color mode in effect
# = 1 if mono mode in effect
# BL = 0 if 64k EGA memory
# = 1 if 128k EGA memory
# = 2 if 192k EGA memory
# = 3 if 256k EGA memory
# CH = feature bits
# CL = switch settings
	mov $0x12, %ah
	mov $0x10, %bl
	int $0x10
	mov %ax, %ds:8
	mov %bx, %ds:10
	mov %cx, %ds:12

# 复制硬盘参数表信息
# 比较奇怪的是硬盘参数表存在中断向量里
# 第一个硬盘参数表的首地址在0x41中断向量处,第二个参数的首地址表在0x46中断向量处,紧跟着第一个参数表, 每个参数表长度为0x10 Byte

# 第一块硬盘参数表
	mov $0x0000, %ax
	mov %ax, %ds
	lds %ds:4*0x41, %si
	mov $INITSEG, %ax
	mov %ax, %es
	mov $0x0080, %di
	mov $0x10, %cx
	rep movsb
# 第二块硬盘参数表
	mov $0x0000, %ax
	mov %ax, %ds
	lds %ds:4*0x46, %si
	mov $INITSEG, %ax
	mov %ax, %es
	mov $0x0090, %di
	mov $0x10, %cx
	rep movsb

# 检查第二块硬盘是否存在,如果不存在的话就清空相应的参数表(
# Comment for routine 0x13 service 0x15
# AH = 15h
# DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
# on return:
# AH = 00 drive not present
# = 01 diskette, no change detection present
# = 02 diskette, change detection present
# = 03 fixed disk present
# CX:DX = number of fixed disk sectors; if 3 is returned in AH
# CF = 0 if successful
# = 1 if error
	mov $0x1500, %ax
	mov $0x81, %dl
	int $0x13
	jc no_disk1
	cmp $3, %ah
	je is_disk1
no_disk1:				# 没有第二块硬盘,那么就对第二个硬盘表清零,使用stosb
	mov $INITSEG, %ax
	mov %ax, %es
	mov $0x0090, %di
	mov $0x10, %cx
	mov $0x00, %ax
	rep stosb

is_disk1:

# 下面该切换到保护模式了~(终于要离开这个不安全,文档匮乏,调试费力的16bit实模式了)
# 进行切换保护模式的准备操作
	cli				# 关中断

# 我们先将system从0x1000:0000移动到0x0000:0000处
	mov $0x0000, %ax
	cld					# Direction = 0 move forward
do_move:
	mov %ax, %es
	add $0x1000, %ax
	cmp $0x9000, %ax	# Does we finish the move
	jz end_move
	mov %ax, %ds
	sub %di, %di
	sub %si, %si
	mov $0x8000, %cx	# Move 0x8000 word = 0x10000 Byte (64KB)
	rep movsw
	jmp do_move

# 下面我们加载 GDT, IDT 等
# 在这里补充加载GDT的代码,并在下面补充GDT表的结构

end_move:
	mov $SETUPSEG, %ax
	mov %ax, %ds
	lidt idt_48
	lgdt gdt_48	

# 开启A20地址线,使得可以访问1M以上的内存
	inb $0x92, %al
	orb $0b00000010, %al
	outb %al, $0x92

# 这里我们会对8259A进行编程,很脏的活,不建议大家搞OwO(所以注释都是英文的辣)
	mov $0x11, %al			# Init ICW1, 0x11 is init command

	out %al, $0x20			# 0x20 is 8259A-1 Port
	.word 0x00eb, 0x00eb	# Time Delay jmp $+2, jmp $+2
	out %al, $0xA0			# And init 8259A-2
	.word 0x00eb, 0x00eb
	mov $0x20, %al			# Send Hardware start intterupt number(0x20)
	out %al, $0x21			# From 0x20 - 0x27
	.word 0x00eb, 0x00eb
	mov $0x28, %al
	out %al, $0xA1			# From 0x28 - 0x2F
	.word 0x00eb, 0x00eb
	mov $0x04, %al			# 8259A-1 Set to Master
	out %al, $0x21
	.word 0x00eb, 0x00eb
	mov $0x02, %al			# 8259A-2 Set to Slave
	out %al, $0xA1
	.word 0x00eb, 0x00eb
	mov $0x01, %al			# 8086 Mode
	out %al, $0x21
	.word 0x00eb, 0x00eb
	out %al, $0xA1
	.word 0x00eb, 0x00eb
	mov $0xFF, %al
	out %al, $0x21			# Mask all the interrupts now
	.word 0x00eb, 0x00eb
	out %al, $0xA1			


# 开启保护模式!
	mov %cr0, %eax
	bts $0, %eax		# Turn on Protect Enable (PE) bit
	mov %eax, %cr0

# Jump to protected mode
	.equ sel_cs0, 0x0008
	mov $0x10, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	ljmp $sel_cs0, $0

# 请填写GDTR信息
gdt_48:					# This is the GDT Descriptor
	.word 0x800			# 
	.word 512+gdt, 0x9	# This give the GDT Base address 0x90200

idt_48:
	.word 0
	.word 0, 0

gdt:
	.word	0,0,0,0
	.word	0x07FF
	.word	0x0000
	.word	0x9A00
	.word	0x00C0

	.word	0x07FF
	.word	0x0000
	.word	0x9200
	.word	0x00C0

	

msg:
	.byte 13, 10
	.ascii "You've successfully load the floppy data into RAM"
	.byte 13, 10, 13, 10


================================================
FILE: docs/EXPERIMENT.md
================================================
## 0 环境 SSR

* 搭建环境 ( Virtualbox 跑通 / 自己搭建 )
* 熟悉环境(GCC, GNU Toolchain, Objdump, dd, Makefile, Bochs, QEMU)

## 1 启动
* 启动扇区 (QEMU 运行 Makefile 构建的 bootsector, 并且使用 Objdump 看反汇编)
* [选] 软盘中断 / 硬盘中断
* 硬盘启动 [选]
* 设置 GDT IDT 并查看(通过 Bochs 查看设置是否正确)

## 2 VGA Serial
* 实现 printk (int, int-oct, int-hex, char, string)
* [选] 实现滚屏,清屏,输出彩色字符 ()
* 提供好串口

## 3 Page
* Page Translation  & It's Data Structure PTE PDE GDT
* Bitmap / Buddy 分配 管理
* Page Fault 体验

## 4 Interrupt
* IDT, Trap Gate 等 实现时钟中断
* 实现 die 函数

## 5 Process
* 调度算法 Round Robin, FCFS, 多级队列
* fork 的实现(简单 fork 实现)(不带 COW 的 fork)
* task_struct (进程的组织,链表,数组。)
* execve 的实现 (a.out) [选做]

## 6 System Call & Signal
* 系统调用的处理过程
* 实现一个系统调用
* 信号的机制
* 在 Linux 下自己写一个带信号处理的程序

## 7 Drivers
* 键盘 (译码,读取)
* 终端(队列)(回显,缓冲队列,终端接口)
* 终端的数据结构
* IDE 硬盘 / 软盘驱动 [选]
* 声卡驱动 [选]

## 8 Filesystem (待定)


## 9 Virtual Memory (后续提高)




----
### Draft
1. Roadmap  [DONE]
2. 实验的文档中要有每一个步骤需要的原理/知识的资料提供
3. 实验环境文档要清晰
4. 实验的验证
5. 给小例子,让他完善。(每一个模块都是这样?)
6. 重点
 - [6.1] 用户态内核态切换()
 - [6.2] 进程创建(fork)
 - [6.3] 中断的实现(键盘,时钟)
 - [6.4] 启动过程形象化
 - [6.5] 存储 分页管理,页表
 - [6.6] 系统调用
 - 6.7 中断在系统中的重要性。
 - 6.8 存储 虚拟存储(**暂无**)
 - 6.9 VGA / 键盘
7. 先小例子,让学生实现
8. 提供屏蔽部分底层实现的操作系统资源操作接口(如get_page free_page task_struct)

## 实验设计可以参考的书籍

* 《自己动手做操作系统》 Orange
* Linux 内核设计的艺术
* Linux 0.11 内核完全解读
* 操作系统的设计与实现 陈文智


================================================
FILE: include/asm/io.h
================================================
// 从Port读入数据到al, _v变量的值就是al的值
#define inb(port) ({\
unsigned char _v; \
__asm__ volatile("inb %%dx, %%al":"=a" (_v):"d" (port)); \
_v; \
    })

#define outb(port, value) \
    __asm__ ("outb %%al, %%dx"::"a" (value), "d" (port));

// 带有延时的outb
#define outb_p(port, value) \
    __asm__ volatile("outb %%al, %%dx\n\t" \
            "jmp 1f\n\t" \
            "1: jmp 1f\n\t" \
            "1:"::"a" (value), "d" (port))

#define inb_p(port) ({ \
unsigned char _v; \
__asm__ volatile("inb %%dx, %%al\n\t" \
        "jmp 1f\n\t" \
        "1: jmp 1f\n\t" \
        "1:": "=a" (_v): "d" (port)); \
_v; \
        })


================================================
FILE: include/asm/segment.h
================================================
static inline char get_fs_byte(const char *addr) {
    register char _v;
    __asm__ volatile("movb %%fs:%1, %0":"=r" (_v):"m"(*addr));
    return _v;
}

static inline unsigned short get_fs_word(const char *addr) {
    unsigned short _v;
    __asm__ volatile("movw %%fs:%1, %0":"=r" (_v):"m"(*addr));
    return _v;
}

static inline unsigned long get_fs_long(const char *addr) {
    unsigned long _v;
    __asm__ volatile("movl %%fs:%1, %0":"=r" (_v):"m"(*addr));
    return _v;
}

static inline void put_fs_byte(char val, char *addr) {
    __asm__ volatile("movb %0, %%fs:%1":: "r" (val), "m" (*addr));
}

static inline void put_fs_word(short val, short *addr) {
    __asm__ volatile("movw %0, %%fs:%1":: "r" (val), "m" (*addr));
}

static inline void put_fs_long(unsigned long val, unsigned long *addr) {
    __asm__ volatile("movl %0, %%fs:%1"::  "r" (val), "m" (*addr));
}


================================================
FILE: include/asm/system.h
================================================
/*
 * 定义了用来在C语言中直接调用的汇编宏函数
 *
 */

#define sti()  __asm__ volatile ("sti"::)
#define cli()  __asm__ volatile ("cli"::)
#define nop()  __asm__ volatile ("nop"::)
#define iret() __asm__ volatile ("iret"::)

// 下面这个宏是通用的设置门描述符的宏,参数分别为
// gate_addr: IDT 描述符的地址; type: 门类型
// dpl: 特权级别; funaddr: 要执行的程序的线性地址
// 设置的过程参考注释
#define _set_gate(gate_addr, type, dpl, funaddr) \
    __asm__ volatile (\
            "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))), \
            "a" (0x00080000), \
            "d" ((char*)(funaddr)))

// 陷阱门, Type = 0xF
#define set_trap_gate(n, funaddr) \
    _set_gate(&idt[n], 0xF, 0, funaddr)

// 中断门, Type = 0xE
#define set_intr_gate(n, funaddr) \
    _set_gate(&idt[n], 0xE, 0, funaddr)

#define set_system_gate(n, funaddr) \
    _set_gate(&idt[n], 0xF, 3, funaddr)


#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__ volatile ("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)), ((int) (addr)), "0x89")
#define set_ldt_desc(n, addr) _set_tssldt_desc(((char *) (n)), ((int) (addr)), "0x82")



================================================
FILE: include/errno.h
================================================
#ifndef _ERRNO_H
#define _ERRNO_H

// 错误号定义

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


================================================
FILE: include/linux/fs.h
================================================
#ifndef _FS_H
#define _FS_H

#include <linux/sched.h>

#define READ 0
#define WRITE 1
#define READA 2
#define WRITEA 3

struct buffer_head {
    char * b_data;  /* pointer to data block 1024Byte */
    unsigned long b_blocknr; /* block number */
    unsigned short b_dev; /* device no */
    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-unlock 1-lock */
    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;
};

#define MAJOR(a) (((unsigned)(a))>>8)

#endif


================================================
FILE: include/linux/head.h
================================================
#ifndef _HEAD_H
#define _HEAD_H

typedef struct desc_struct {
    unsigned long a, b;
} desc_table[256];

extern desc_table idt, gdt;
extern unsigned long pg_dir[1024];

#endif


================================================
FILE: include/linux/kernel.h
================================================
#ifndef _KERNEL_H
#define _KERNEL_H
 
void printk(char *fmt, ...);     // Simplest printk function to use
void video_putchar_at(char ch, int x, int y, char attr);
void video_putchar(char ch);
void video_clear();
void video_init();
int video_getx();
int video_gety();
void roll_screen();
void memcpy(char *dest, char *src, int count, int size);
void printnum(int num, int base, int sign);
int get_cursor();
void update_cursor(int row, int col);
void panic(const char *str);
void verify_area(void *addr, unsigned int size);

extern int video_x, video_y;

#endif


================================================
FILE: include/linux/lib.h
================================================
char getchar();
int getline(char *str);
int printf(char *fmt, ...);


================================================
FILE: include/linux/mm.h
================================================
#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);
/* extern */ void calc_mem(void);
void do_no_page(unsigned long error_code, unsigned long address);
int copy_page_tables(unsigned long from, unsigned long to, unsigned long size);
void mm_print_pageinfo(unsigned long addr);

#endif


================================================
FILE: include/linux/sched.h
================================================
#ifndef _SCHED_H
#define _SCHED_H

// 最多有 64 个进程(任务)同时处于系统中
#define NR_TASKS 64
// 时钟频率 100 hz
#define HZ 100

#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]

#include<linux/mm.h>
#include<linux/head.h>
// 目前signal.h 仅仅具有最基本的sigaction结构,之后会实现signal
#include<signal.h>

// 定义任务状态

#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, unsigned long size);
extern int free_page_tables(unsigned long from, unsigned long size);
extern void schedule(void);

typedef int (*fn_ptr)();

struct i387_struct {
    long cwd;       // 控制字(Control)
    long swd;       // 状态字(State)
    long twd;       // 标记字(Tag)
    long fip;       // 协处理器代码指针IP
    long fcs;       // 协处理器代码段寄存器CS
    long foo;       // 内存offset
    long fos;       // 内存段
    long st_space[20];      // 8个10字节的协处理器累加器
};

// 这里是和TSS结构对应的,要注意变量的顺序,具体参考Intel手册
// 还包含了 i387 协处理器的结构
struct tss_struct {
    long back_link;
    long esp0;
    long ss0;
    long esp1;
    long ss1;
    long esp2;
    long ss2;
    long cr3;
    long eip;
    long eflags;
    long eax, ecx, edx, ebx;        // 顺序很重要
    long esp;
    long ebp;
    long esi;
    long edi;
    long es;
    long cs;
    long ss;
    long ds;
    long fs;
    long gs;
    unsigned long ldt;
    unsigned long bitmap;
    struct i387_struct i387;
};

// 进程描述符结构
struct task_struct {
    // --- 硬编码部分,下面的不应该修改 ---
    long state;
    long counter;       // 时间片计数器
    long priority;      // 优先级
    // 信号相关结构,分别为信号集,响应信号的行为,屏蔽的信号集 
    long signal;
    struct sigaction sigaction[32];
    unsigned long blocked;
    // --- 硬编码部分结束 ---
    int exit_code;      // 其父进程会读取该任务的 exit_code 
    unsigned long start_code;
    unsigned long end_code;
    unsigned long end_data;
    unsigned long brk;              // 总长度brk
    unsigned long start_stack;      // 堆栈段地址

    long pid;                       // pid 进程ID
    long father;                    // 父进程ID
    long pgrp;                      // 进程组ID
    long session;                   // 会话(session)ID
    long leader;                    // 会话(session)的首领

    unsigned short uid;             // 用户ID
    unsigned short euid;            // 有效用户ID
    unsigned short suid;            // SetUID
    unsigned short gid;             // 组ID
    unsigned short egid;            // 有效组ID
    unsigned short sgid;            // SetGID

    long alarm;                     // 报警定时值 单位: jiffies
    long utime;                     // 用户态运行时间
    long stime;                     // 内核态运行时间
    long cutime;                    // 子进程用户态运行时间
    long cstime;                    // 子进程内核态运行时间
    long start_time;                // 进程开始运行的时点

    unsigned short used_math;       // 是否使用了协处理器

    int tty;
    // 下面是和文件系统相关的变量,暂时不使用,先注释掉
    // unsigned short umask;
    // struct m_inode *pwd;
    // struct m_inode *executable;
    // unsigned long close_on_exec;
    // struct file * filp[NR_OPEN];
    struct desc_struct ldt[3];      // LDT 第一项为空, 第二项是代码段,第三项是数据段(也是堆栈段)
    struct tss_struct tss;          // 该进程的TSS结构
};

#define INIT_TASK \
/* state info */ {0,15,15, \
/* signals */    0,{{},},0, \
/* exit_code, brk */0,0,0,0,0,0, \
/* pid */    0,-1,0,0,0, \
/* uid */    0,0,0,0,0,0, \
/* alarm, etc... */0,0,0,0,0,0,\
/* math */    0, \
/* tty */    -1, \
/* LDT */    { \
        {0, 0},\
        {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 start_time;

#define CURRENT_TIME (start_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);
extern void show_task_info(struct task_struct *task);

#define FIRST_TSS_ENTRY 4
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
// 下面计算出TSS,LDT在GDT中的偏移量
// _TSS(n)表示第n个TSS,计算方式为,第一个 TSS 的入口为 4 << 3(因为每一个Entry占8Byte, 所以第4个的偏移量为4 << 3)
#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__ volatile("ltr %%ax"::"a" (_TSS(n)))
#define lldt(n) __asm__ volatile("lldt %%ax"::"a" (_LDT(n)))
// 取出当前的任务号,返回为n: 当前任务号(和进程号不一样)
#define str(n) \
    __asm__ volatile("str %%ax\n\t" \
                    "subl %2, %%eax\n\t" \
                    "shrl $4, %%eax\n\t" \
                    : "=a" (n) \
                    : "a" (0), "i" (FIRST_TSS_ENTRY << 3))


// #define PAGE_ALIGN(n) (((n) + 0xfff) & 0xfffff000) 这一行虽然在原有的linux0.11中,不过在整个repo中都没有使用到

#define switch_to(n) {\
    struct {long a, b;} __tmp; \
    __asm__ volatile ("cmpl %%ecx, current\n\t" \
            "je 1f\n\t" \
            "movw %%dx, %1\n\t" \
            "xchgl %%ecx, current\n\t" \
            "ljmp *%0\n\t" /* 长跳跃到新的TSS选择符,表示任务切换 */ \
            "cmpl %%ecx, last_task_used_math\n\t" \
            "jne 1f\n\t" \
            "clts\n\t" \
            "1:" \
            ::"m" (*&__tmp.a), "m" (*&__tmp.b), \
            "d" (_TSS(n)), "c" ((long) task[n])); \
}

#define _set_base(addr, base) \
    __asm__ volatile(/* "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) \
                    /*:"dx"*/ \
            )


#define _set_limit(addr, limit) \
    __asm__ volatile( \
            "movw %%dx, %0\n\t" \
            "rorl $16, %%edx\n\t" \
            "movb %1, %%dh\n\t" \
            "andl $0xf0, %%dh\n\t" \
            "orb %%dh, %%dl\n\t" \
            "mov %%dl %1" \
            :"m" (*(addr)), \
            "m" ((*(addr) + 6)) \
            "d" (limit) \
            )

#define set_base(ldt, base) _set_base(((char *)&(ldt)), (base))
// limit >> 12 是因为当Descriptor中G位置位的时候,Limit单位是4KB
#define set_limit(ldt, limit) _set_limit(((char *)ldt), (limit - 1) >> 12)

static inline unsigned long _get_base(char *addr) {
    unsigned long __base;
    __asm__ volatile("movb %3, %%dh\n\t"
                "movb %2, %%dl\n\t"
                "shll $16, %%edx\n\t"
                "movw %1, %%dx\n\t"
                :"=&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__ volatile("lsll %1, %0\n\tincl %0":"=r"(__limit):"r"(segment)); \
    __limit; \
        })

#endif


================================================
FILE: include/linux/sys.h
================================================
#ifndef _SYS_H
#define _SYS_H

#include<linux/sched.h>

extern int sys_fork();
extern int sys_pause();
extern int stub_syscall();
extern int serial_debugstr(char *str);
extern int sys_kill(int pid, int sig);
extern int sys_sigaction(int signum, struct sigaction *action, struct sigaction *old_action);
extern int sys_sgetmask(void);
extern int sys_ssetmask(int newmask);
// Just for debug use
extern int tty_read(int channel, char *buf, int nr);
extern int _user_tty_write(int channel, char *buf, int nr);
extern int sys_alarm(long seconds);
extern int sys_sleep(long seconds);

// 目前除了少数syscall之外其余的syscall均为stub状态
fn_ptr sys_call_table[] = {
    tty_read, // 0
    stub_syscall,
    sys_fork,
    _user_tty_write, // 3
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    sys_sleep, // 10
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall, // 20
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    sys_pause,
    stub_syscall, // 30
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    sys_kill,
    stub_syscall,
    stub_syscall,
    stub_syscall, // 40
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall, // 50
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall, // 60
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    stub_syscall,
    sys_sigaction,
    sys_sgetmask,
    sys_ssetmask,
    stub_syscall,
    stub_syscall,
    serial_debugstr,
};

#endif


================================================
FILE: include/linux/tty.h
================================================
#ifndef _TTY_H
#define _TTY_H

#include <linux/sched.h>
#define TTY_BUF_SIZE 1024

#define TTY_ECHO 0x0001     // 回显标志

void tty_init();

struct tty_queue {
    char buf[TTY_BUF_SIZE];
    struct task_struct *wait_proc;      // 等待该缓冲区的进程队列
    unsigned long head;
    unsigned long tail;
};

struct tty_struct {
    void (*write)(struct tty_struct *tty);
    int pgrp;
    unsigned int flags;
    struct tty_queue read_q;        // read_q 存储的是键盘 Raw input
    struct tty_queue write_q;       // write_q 存储的是即将输出到屏幕的字符
    struct tty_queue buffer;        // buffer 是用户的输入 buffer, 会被 tty_read 读走
};

extern struct tty_struct tty_table[];
int tty_isempty_q(const struct tty_queue *q);
int tty_isfull_q(const struct tty_queue *q);
char tty_pop_q(struct tty_queue *q);
char tty_queue_head(const struct tty_queue *q);
char tty_queue_tail(const struct tty_queue *q);
int tty_push_q(struct tty_queue *q, char ch);
int tty_push_q_front(struct tty_queue *q, char ch);
void tty_queue_stat(const struct tty_queue *q);
void tty_write(struct tty_struct *tty);
int _user_tty_write(int channel, char *buf, int nr);
void copy_to_buffer(struct tty_struct *tty);
int tty_read(int channel, char *buf, int nr);

#endif


================================================
FILE: include/serial_debug.h
================================================
#ifndef _SERIAL_DEBUG_H
#define _SERIAL_DEBUG_H
extern void s_putchar(char a);
extern void s_puts(char *s);
extern void s_printnum(int num, int base, int sign);
extern void s_printk(char *fmt, ...);

#endif


================================================
FILE: include/signal.h
================================================
#ifndef _SIGNAL_H
#define _SIGNAL_H

#include <sys/types.h>

typedef int sig_atomic_t;
typedef unsigned int sigset_t;

#define _NSIG 32		// 32种信号,所以下面的信号集也是32bit啦
#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 SIGSTKFLT	16
#define SIGCHLD		17
#define SIGCONT		18
#define SIGSTOP		19
#define SIGTSTP		20
#define SIGTTIN		21
#define SIGTTOU		22

// 下面定义 sigaction 需要的结构
#define SA_NOCLDSTOP 1
#define SA_NOMASK (int)0x40000000		// 表示不阻止在某个信号的信号处理程序中再次收到该信号
#define SA_ONESHOT (int)0x80000000		// 只调用一次信号处理句柄

#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2

#define SIG_DFL ((void(*) (int))0)		// 默认信号处理句柄
#define SIG_IGN ((void(*) (int))1)		// 信号处理忽略句柄

void (*signal(int _sig, void(*func)(int)))(int);
int raise(int sig);						// 向自身发信号
int kill(pid_t pid, int sig);			// 向某个进程发信号

typedef unsigned int sigset_t;  // 32bit 信号集,一位表示一个信号,对于linux0.11已经够用了

struct sigaction {
    void (*sa_handler)(int);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};

// 对阻塞信号集的操作
// Not implemented in sigaction
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);

int sigpending(sigset_t *set);
int sigprocmask(int how, sigset_t *set, sigset_t *oldset);
// 改变对于某个信号sig的处理过程
// int sigaction(int sig, struct sigaction *act, struct sigaction *oldact);

#endif


================================================
FILE: include/stdarg.h
================================================
#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 */


================================================
FILE: include/stddef.h
================================================
#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


================================================
FILE: include/sys/types.h
================================================
#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



================================================
FILE: include/sys/wait.h
================================================
#ifndef _SYS_WAIT_H
#define _SYS_WAIT_H

#include <sys/types.h>

#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


================================================
FILE: include/unistd.h
================================================
#ifndef _UNISTD_H
#define _UNISTD_H

#ifdef __LIBRARY__

#define __NR_fork 2
#define __NR_sleep 10
#define __NR_pause 29
#define __NR_kill 37
#define __NR_sigaction 67
#define __NR_sgetmask 68
#define __NR_ssetmask 69
#define __NR_sys_debug 72

// Just for debug
// TODO Change to write, read
#define __NR_user_tty_read 0
#define __NR_user_tty_write 3

#define _syscall0(type, name)  \
type name(void) \
{ \
    long __res; \
    __asm__ volatile("int $0x80\n\t" \
            : "=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\n\t" \
            : "=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\n\t" \
            : "=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\n\t" \
            : "=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

#endif


================================================
FILE: init/Makefile
================================================
include ../Makefile.header

CFLAGS += -I../include
LDFLAGS += -r

all: main.o

.c.o:
	@$(CC) $(CFLAGS) \
		-c -o $*.o $<

clean:
	rm -rf *.o


================================================
FILE: init/main.c
================================================
/*
 * Default Routine to run after head.s
 *
 */

#define __LIBRARY__

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/lib.h>
#include <unistd.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/tty.h>
// Use to debug serial
#include <serial_debug.h>

extern void trap_init(void);
extern void video_init(void);
extern void sched_init(void);
extern void mem_init(unsigned long start_mem, unsigned long end_mem);
extern int user_tty_read(int channel, char *buf, int nr);
void init();

// Here contains sycall routines

static inline int fork(void) __attribute__((always_inline));
static inline int pause(void) __attribute__((always_inline));
static inline int sys_debug(char *str);
static inline _syscall0(int,fork)
static inline _syscall1(int, sys_debug, char *, str)
static inline _syscall1(int, sleep, long, seconds)

static inline int pause(void) {
    long __res;
    __asm__ volatile("int $0x80\n\t"
            :"=a" (__res)
            :"0" (__NR_pause));
    if( __res >= 0)
        return (int) __res;
    return -1;
}

#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:\t movl $0x17, %%eax\n\t" \
            "movw %%ax, %%ds\n\t" \
            "movw %%ax, %%es\n\t" \
            "movw %%ax, %%fs\n\t" \
            "movw %%ax, %%gs" \
            :::"ax");

int memtest_main(void);
void signal_demo_main(void);
void sched_abcd_demo(void);

int main() {
    video_init();
    trap_init();
    sched_init();
    mem_init(0x100000, 0x300000);
    tty_init();
    sti();
    printk("TTY Init done\n", 1);
    printk("root@neu-os# ");

    // 初始化物理页内存, 将 1MB - 16MB 地址空间的内存进行初始化
    // s_printk("Test Serial mem_init = %x\n", mem_init);
    // mmtest_main();
    move_to_user_mode();
    sys_debug("User mode can print string use this syscall\n");
    // now user process can execute!
    // but why cannot schedule!
    if(!fork()) {
        if(!fork()) {
            sched_abcd_demo();
        } else {
            // signal_demo_main();
        }
        while(1);
    }
    // printk("buf = %s\n", buf);
}

void sched_abcd_demo() {
    // Here init process (pid = 1) will
    // print AABB randomly
    char buf[100] = "TTY";
    printf("Welcome to the OS, your are current at %x\n", sched_abcd_demo);
    printf("Execuse me, but who are you? ");
    getline(buf);
    printf("%s, emm good name! Hi %s. :)\n", buf, buf);
    printf("%s@neu-os$");
    printf("This is a multi-thread demo, start in 3s ... 3");
    sleep(1);
    printf(".2");
    sleep(1);
    printf(".1");
    sleep(1);
    printf(".0\n");
    if(!fork()) {
        while(1) {
            printf("A");
        }
    }
    if(!fork()) {
        while(1) {
            printf("B");
        }
    }
    if(!fork()) {
        while(1) {
            printf("C");
        }
    }
    if(!fork()) {
        while(1) {
            printf("D");
        }
    }
    while(1);
}


================================================
FILE: kernel/Makefile
================================================
include ../Makefile.header

OBJS = printk.o panic.o traps.o asm.o sched.o system_call.o sys.o fork.o \
	   serial_debug.o signal.o signal_demo.o exit.o libc_restore.o vsprintf.o

LDFLAGS	+= -r
CFLAGS += -I../include

.PHONY=clean run all

all: kernel.o

kernel.o: $(OBJS)
	$(LD) $(LDFLAGS) -o kernel.o $(OBJS)

.c.o: 
	@$(CC) $(CFLAGS) \
		-c -o $*.o $<

clean:
	- rm -f $(OBJS) kernel.o
	- make -C chr_drv clean


================================================
FILE: kernel/asm.s
================================================
.code32

# Code to implement low-level 80386 interrupt handler
# We'll handle int0 - int16 here, some interrupts will
# push addtional error code onto stack, so we need to implement two common
# handler

# 实现 Intel 80386 硬件相关中断,即中断号 0 - 16 的汇编处理程序
# 大部分函数都会去调用相应的 traps.c 中的 C 语言处理函数,汇编
# 只做最紧要的事情,主要流程在 C 函数里处理

# 下面仅对中断的具体含义进行简单的注释,想要了解详情的同学请查看 Intel IA32 手册 Vol 3A 第六章

.global divide_error, debug, nmi, int3, overflow, bounds, invalid_op
.global double_fault, coprocessor_segment_overrun
.global invalid_TSS, segment_not_present, stack_segment
.global general_protection, coprocessor_error, irq13, reserved
# 这两个是STUB的,之后会在system_call.s中实现
.global corprocessor_error, parallel_interrupt, device_not_available
.global demo_timer_interrupt

# 先处理无 error code 压栈的情况
# 相关中断有: 除零(Fault) 调试debug(Fault) nmi(Trap) 断点指令(Trap)

divide_error:
	pushl $do_divide_error			# 首先压栈要调用的函数
no_error_code:
	xchgl %eax, (%esp)				# 将函数地址交换到 %eax 中, (同时%eax入栈)
									# 将当前寄存器压栈
	pushl %ebx
	pushl %ecx
	pushl %edx
	pushl %edi
	pushl %esi
	pushl %ebp
	push %ds
	push %es
	push %fs
	pushl $0						# error code = 0
	lea 44(%esp), %edx				# 栈的地址由高地址向低地址生长,因而这里是去向栈底寻址
									# 我们刚刚 push 了 11 个参数,现在将指针回退到中断返回地址
									# 这一参数所在的栈指针位置, 并将地址放入 %edx 
	pushl %edx
	movl $0x10, %edx				# 初始化ds, es, fs加载为内核数据段选择符
	mov %dx, %ds
	mov %dx, %es
	mov %dx, %fs
	call *%eax						# 使用间接调用,调用 %eax 内存放的地址处的函数
	addl $8, %esp					# 丢弃最后入栈的两个参数, 让栈指针回到 %fs 入栈处
	pop %fs
	pop %es
	pop %ds
	popl %ebp
	popl %esi
	popl %edi
	popl %edx
	popl %ecx
	popl %ebx
	popl %eax						# 这里把原 eax 中内容恢复
	iret							# 中断返回,包括弹出相应的参数,以及返回原代码执行

# int1 debug 调试中断入口点 类型 Fault
debug:
	pushl $do_int3
	jmp no_error_code

# int2 Non maskable interrupts 入口点 类型 Trap
nmi:
	pushl $do_nmi
	jmp no_error_code

# int3 断点指令引起中断的入口点 类型 Trap
int3:
	pushl $do_int3
	jmp no_error_code

# int4 溢出错误入口点 类型 Trap
overflow:
	pushl $do_overflow
	jmp no_error_code

# int5 边界检查出错 类型 Fault
bounds:
	pushl $do_bounds
	jmp no_error_code

# int6 无效指令 类型 Fault
invalid_op:
	pushl $do_invalid_op
	jmp no_error_code

# int9 协处理器段超出 类型 Abort
coprocessor_segment_overrun:
	pushl $do_coprocessor_segment_overrun
	jmp no_error_code

# int15 其他 Intel 保留中断的入口点
reserved:
	pushl $do_reserved
	jmp no_error_code

# int45 -- irq13
# 协处理器处理完一个操作的时候就会发送 IRQ13 信号,通知CPU操作完成,80387执行计算时
# CPU 会等待其完成,下面通过写协处理端口(0xF0)消除BUSY信号,并重新激活80387的扩展请求
# 引脚 PERREQ

irq13:
	pushl %eax
	xorb %al, %al
	outb %al, $0xF0
	movb $0x20, %al
	outb %al, $0x20
	jmp 1f
1:	jmp 1f
1:	outb %al, $0xA0			# 向8259A 发送 EOI 信号
	popl %eax
	jmp coprocessor_error

# 下面的中断会在压入中断返回地址之后将出错号一同压栈,因此返回时需要弹出出错号

# Double Fault, 类型 Abort 有出错码
# 当CPU在调用一个异常处理程序的时候又检测到另一个异常,而且这两个异常无法被串行处理
# 
double_fault:
	pushl $do_double_fault
error_code:
	xchgl %eax, 4(%esp)		# 将出错号%eax交换,同时%eax入栈
	xchgl %ebx, (%esp)		# 将要调用的C函数的地址与%ebx交换,同时%ebx入栈
	pushl %ecx
	pushl %edx
	pushl %esi
	pushl %edi
	pushl %ebp
	pushl %ds
	pushl %es
	pushl %fs
	pushl %eax
	lea 44(%esp), %eax
	pushl %eax
	movl $0x10, %eax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	call *%ebx				# 间接调用, %ebx 中存放的就是要调用的C函数的地址
	addl $8, %esp
	pop %fs
	pop %es
	pop %ds
	pop %ebp
	popl %edi
	popl %esi
	popl %edx
	popl %ecx
	popl %ebx
	popl %eax
	iret

# int10 无效的任务状态段(TSS) 类型 Fault
invalid_TSS:
	pushl $do_invalid_TSS
	jmp error_code

# int11 段不存在 类型 Fault 
segment_not_present:
	pushl $do_segment_not_present
	jmp error_code

# int12 堆栈段错误 类型 Fault
stack_segment:
	pushl $do_stack_segment
	jmp error_code

# 一般保护性错误 类型 Fault
general_protection:
	pushl $do_general_protection
	jmp error_code

# coprocessor error 先在这里实现一个stub的,之后会实现
coprocessor_error:
	pushl $do_stub
	jmp error_code

parallel_interrupt: # 本版本没有实现,这里只发EOI
	pushl %eax
	movb $0x20, %al
	outb %al, $0x20
	popl %eax
	iret

device_not_available:
	pushl $do_stub
	jmp error_code

# int7 设备不存在 将在 kernel/system_call.s 中实现
# int14 页错误 将在 mm/page.s 中实现
# int16 协处理器错误 将在 kernel/system_call.s 中实现
# int 0x20 时钟中断 将在 kernel/system_call.s 中实现
# int 0x80 系统调用 将在 kernel/system_call.s 中实现


================================================
FILE: kernel/blk_drv/Makefile
================================================


================================================
FILE: kernel/blk_drv/blk.h
================================================
/*
 * blk.h block driver commmon data structure and 
 * macros, function prototypes
 */
#ifndef _BLK_H
#define _BLK_H

#define NR_BLK_DEV 7
#define NR_REQUEST 32

// This is the request queue structure
struct request {
    int dev;
    int cmd;
    int errors;
    unsigned long sector;
    unsigned long nr_sectors;
    char * buffer;
    struct task_struct * waiting;
    struct buffer_head * bh;
    struct request * next;
};

struct blk_dev_struct {
    void (*request_fn)(void);
    struct request * current_request;
};

#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)))


#endif


================================================
FILE: kernel/blk_drv/request_scan_algo.c
================================================
/*
 * block driver Request queue implementation
 * using SCAN algorithm
 */

#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include <linux/fs.h>

#include "blk.h"

struct request req_tbl[NR_REQUEST];
struct task_struct * wait_for_request = NULL;

struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
    {NULL, NULL},
    {NULL, NULL},
    {NULL, NULL},
    {NULL, NULL},
    {NULL, NULL},
    {NULL, NULL},
    {NULL, NULL},
};

static void add_request(struct blk_dev_struct * dev, struct request * req) {
    struct request * tmp;
    req->next = NULL;
    cli();

    // current disable fs/buffer functional
    if(req->bh) {
        req->bh->b_dirt = 0;
    }

    // If current req == NULL means this is the
    // first request
    if (!(tmp = dev->current_request)) {
        dev->current_request = req;
        sti();
        (dev->request_fn)();
        return ;
    }

    for(; tmp->next != NULL; 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 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("unlock_buffer: buffer not locked\n"); 
    }
    bh->b_lock = 0;
    wake_up(&bh->b_wait);
}

static void make_request(int major, int rw, struct buffer_head *bh) {
    struct request * req;
    int rw_ahead;
    short available = 0;
    if ((rw_ahead = (rw == READA || rw == WRITEA))) {
        if (bh->b_lock) {
            return ;
        }
        if (rw == READA) {
            rw = READ;
        }
        
        if (rw == WRITEA) {
            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 ;
    }

    // Do not let WRITE request fill the queue
    while(!available) {
        if (rw == READ) {
            req = req_tbl + NR_REQUEST;
        } else {
            req = req_tbl + ((NR_REQUEST * 2) / 3);
        }

        while(--req >= req_tbl) {
            if (req->dev < 0)   // dev < 0 means a new request
                break;
            available = 1;
        }
        if (available) {
            break;
        }
        // no available found
        if (req < req_tbl) {
            if (rw_ahead) {
                unlock_buffer(bh);
                return;
            }
            sleep_on(&wait_for_request);
        }
    }

    // found an empty request place, insert into
    //
    req->dev = bh->b_dev;
    req->cmd = rw;
    req->errors = 0;
    req->buffer = bh->b_data;
    req->sector = bh->b_blocknr << 1;
    req->nr_sectors = 2;
    req->waiting = NULL;
    req->bh = bh;
    req->next = NULL;
    add_request(major + blk_dev, req);
}

// Low level read/write block
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");
        return ;
    }
    make_request(major, rw, bh);
}

void blk_dev_init(void) {
    int i;
    for(i = 0; i < NR_REQUEST; i++) {
        req_tbl[i].dev = -1;
        req_tbl[i].next = NULL;
    }
}



================================================
FILE: kernel/chr_drv/Makefile
================================================
include ../../Makefile.header

OBJS = do_keyboard.o keyboard.o tty_io.o tty_queue.o vga_console.o \
	   tty_read.o

LDFLAGS	+= -r
CFLAGS += -I../../include

.PHONY=clean run all
 
all: chr_drv.a

chr_drv.a: $(OBJS)
	@$(AR) rcs chr_drv.a $(OBJS)
	@sync

.c.o: 
	@$(CC) $(CFLAGS) \
		-c -o $*.o $<

clean:
	- rm -f $(OBJS) *.a


================================================
FILE: kernel/chr_drv/do_keyboard.c
================================================
#include <linux/kernel.h>
#include <linux/head.h>
#include <asm/system.h>
#include <serial_debug.h>
#include <linux/tty.h>
#include <linux/sched.h>

#define RELEASE_CHAR(a) ((a) & 0x80)
#define CAPS 0x3A
#define LCTRL 0x1D
#define LALT 0x38
#define LSHIFT 0x2A
#define RCTRL 
#define RALT
#define RSHIFT

extern void keyboard_interrupt();

// 点代表非可见字符, 或者是"."本身
const char scancode_table[] =       "..1234567890-=\b.qwertyuiop[]\n.asdfghjkl;\'`.\\zxcvbnm,./.*. .............7894561230......";
const char shift_scancode_table[] = "..!@#$%^&*()_+\b.QWERTYUIOP{}\n.ASDFGHJKL:\"~.|ZXCVBNM<>?.*. .............7894561230......";
// const char caps_scancode_table[] =  "..1234567890-=\b.QWERTYUIOP[]\n.ASDFGHJKL;\'`.\\ZXCVBNM,./.*. .............7894561230......";

// This function is not supposed
// to be here but just for convenience
char toupper(char ch) {
    if (ch >= 'a' && ch <= 'z') {
        ch = (char)(ch - 'a' + 'A');
    }
    return ch;
}

void do_keyboard_interrupt(short scancode) {
    // Define some flags
    static char caps = 0;
    static char lshift = 0; 
    static char rshift = 0;
    static char lctrl = 0;
    static char rctrl = 0;
    static char lalt = 0;
    static char ralt = 0;
    static char cap_out = 0;
    char ch = ' ';

    if (RELEASE_CHAR(scancode)) {
        s_printk("[DEBUG] Keyboard Release 0x%x\n", scancode);

        // Check if Shift / Alt / Ctrl / Released
        if (scancode == (LCTRL | 0x80)) {
            lctrl = 0;
        }
        if (scancode == (LALT | 0x80)) {
            lalt = 0;
        }
        if (scancode == (LSHIFT | 0x80)) {
            lshift = 0;
            s_printk("LSHIFT[%d]\n", lshift);
        }
        return ;
    }
    if (!RELEASE_CHAR(scancode)) {
        if (scancode == LCTRL) {
            lctrl = 1;
            return ;
        }
        if (scancode == LALT) {
            lalt = 1;
            return ;
        }
        if (scancode == LSHIFT) {
            lshift = 1;
            return ;
        }
        if (scancode == CAPS) {
            caps = !caps;
            return ;
        }
        cap_out = caps?!(lshift || rshift):(lshift || rshift);
        if (lshift || rshift) {
            ch = shift_scancode_table[scancode];
            if (cap_out) {
                ch = toupper(ch);
            }
        }
        else if (lctrl || rctrl) {
            // here we need ctrl escape
        }
        else if (lalt || ralt) {
            // here we do nothing :/
        }
        else {
            ch = scancode_table[scancode];
            if(cap_out) {
                ch = toupper(ch);
            }
        }
        s_printk("[DEBUG] Keyboard Press 0x%x[ %d ]\n", scancode, ch);
        // TODO: 使得 tty 和进程对应, 当前都是指向 tty_table[0]
        // push the parsed char into queue
        if(tty_push_q(&tty_table[0].read_q, ch)) {
            s_printk("[DEBUG] read queue full\n");
            // here we need to sleep to wait queue
            // not full
        }
        copy_to_buffer(&tty_table[0]);
#ifdef DEBUG
        tty_queue_stat(&tty_table[0].read_q);
#endif
    }
    // Wakeup the buffer queue
    // 我们只有在收到 \n EOF 的时候才唤醒队列
    // wake_up(&tty_table[0].buffer.wait_proc);
    return ;
}


================================================
FILE: kernel/chr_drv/keyboard.s
================================================
# Very Simple Keyboard Program
# The whole char_drv is rewritten for teaching purpose
# Get keyboard interrupt then call the keyboard interrupt function

.text
.global keyboard_interrupt

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
	push %ax
# 发送 EOI 给8259
	movb $0x20, %al
	outb %al, $0x20
	call do_keyboard_interrupt
	pop  %ax
	pop  %es
	pop  %ds
	popl %edx
	popl %ecx
	popl %ebx
	popl %eax
	iret


================================================
FILE: kernel/chr_drv/tty_io.c
================================================
#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <serial_debug.h>
#include <linux/tty.h>

#define DEBUG

void con_init();
extern void con_write(struct tty_struct *tty);

// 这里我们用 C99 的 dot initializer 初始化
struct tty_struct tty_table[] = {
    {
        .pgrp = 0,
        .write = NULL,
        .flags = 0 | TTY_ECHO,
        .read_q = {
            .head = 0,
            .tail = 0,
        },
        .write_q = {
            .head = 0,
            .tail = 0,
        },
        .buffer = {
            .head = 0,
            .tail = 0
        }
    },
};

// struct tty_queue read_q;
void tty_init(void) {
    tty_table[0].write = con_write;
    con_init();
}

// copy_to_buffer handle the keyboard input
// to tty echo char
void copy_to_buffer(struct tty_struct *tty) {
    char ch;
    struct tty_queue *read_q= &tty->read_q;
    struct tty_queue *buffer= &tty->buffer;
    while(!tty_isempty_q(&tty->read_q)) {
        ch = tty_pop_q(read_q); 
        // TODO Judge if it's a normal character
        switch(ch) {
            case '\b':
                // This is backspace char
                if (!tty_isempty_q(buffer)) {
                    if(tty_queue_tail(buffer) == '\n')  // \n 不能被清除掉
                       continue ;
                    buffer->tail = (buffer->tail - 1) % TTY_BUF_SIZE;
                } else {
                    continue;
                }
                break;
            case -1:
            // We do not process \n here, process it in tty_read
            case '\n':
                s_printk("Enter wake the tty read queue up!\n");
                tty_push_q(buffer, ch);
                wake_up(&tty_table[0].buffer.wait_proc);
                break;
                // EOF
            default:
                if (!tty_isfull_q(buffer)) {
                    tty_push_q(buffer, ch);
                } else {
                    // here we need to sleep until the queue
                    // is not full
                }
                break;
        }

        if (tty->flags | TTY_ECHO) {
            tty_push_q(&tty->write_q, ch);
            tty->write(tty);
        }
    }
    return ;
}

void tty_write(struct tty_struct* tty) {
    tty->write(tty);
    return ;
}

// 队列为空时,进程进入睡眠状态, 可被中断唤醒
// TODO: write sleep_if_empty
void sleep_if_empty(struct tty_queue *q) {
    cli();
    while(!current->signal && tty_isempty_q(q))
        interruptible_sleep_on(&q->wait_proc); 
    sti();
}

static void sleep_if_full(struct tty_queue *q) {
    cli();
    while(!current->signal && tty_isfull_q(q))
        interruptible_sleep_on(&q->wait_proc); 
    sti();
}

// tty_read 函数从 tty->buffer 中读取内容
// 当没有读到足够的字符且 buffer 为空的时候
// sleep
int tty_read(int channel, char *buf, int nr) {
    int len = 0;
    char ch;
    char *p = buf;
    char tmpbuf[TTY_BUF_SIZE];
    int tmpbuf_len = 0;
#ifdef DEBUG
    s_printk("[TTY] buf addr = 0x%x\n", buf);
#endif

    // TODO make channel adjustable
    channel = 0;
    if (channel > 2 || channel < 0 || nr < 0)
        return -1;
    struct tty_struct *tty = tty_table + channel;
    // Sleep until the queue is not empty
    // Only we get an \n, we start to process
    // interruptible_sleep_on(&tty->buffer.wait_proc);
    //
    // outer loop for reading lines
    while (1) {
        // If the queue is empty and we haven't finish reading
        // Then we sleep, only enter can wake up the sleep
        // sleep_if_empty(&tty->buffer);

        // If enter triggered, we start to read then back
        while(1) {
            ch = tty_pop_q(&tty->buffer);
#ifdef DEBUG
            if(tty_isempty_q(&tty->buffer)) {
                s_printk("Empty!");
            }
            tty_queue_stat(&tty->buffer);
#endif
            if (nr <= 0) {
                // get the char then put it back
                tmpbuf[tmpbuf_len++] = ch;
                if (ch == '\n' || ch == -1) {
                    for (int i = tmpbuf_len - 1; i >= 0; i--) {
                        tty_push_q_front(&tty->buffer, tmpbuf[i]);
                    }
                    break;
                    // revert the queue and stop the cycle
                }
                // put the char in tmp queue and continue
                continue;
            }
            // TODO: Change -1 to EOF
            // We also keep the \n
            if (ch == '\n' || ch == -1) {
                // TODO: Figure out why put_fs_byte don't work
                put_fs_byte(ch, p++);
                // *p++ = ch;
                len++;
                nr--;
                break;
            }
            put_fs_byte(ch, p++);
            // *p++ = ch;
            len++;
            nr--;
        }
#ifdef DEBUG
        s_printk("Buf = %s\n", buf);
#endif
        if (nr <= 0) {
            break;
        }
    }

    return len;
}

int _user_tty_write(int channel, char *buf, int nr) {
    int i = 0;
    // TODO make channel adjustable
    channel = 0;
    struct tty_struct *tty = tty_table + channel;
    for (i = 0; i < nr; i++) {
        char c = get_fs_byte(buf + i);
        tty_push_q(&tty->write_q, c);
    }
    tty_write(tty);
    // TODO return len
    return nr;
}


================================================
FILE: kernel/chr_drv/tty_io.c.orig
================================================
#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <serial_debug.h>
#include <linux/tty.h>

#define DEBUG

void con_init();
extern void con_write(struct tty_struct *tty);

// 这里我们用 C99 的 dot initializer 初始化
struct tty_struct tty_table[] = {
    {
        .pgrp = 0,
        .write = NULL,
        .flags = 0 | TTY_ECHO,
        .read_q = {
            .head = 0,
            .tail = 0,
        },
        .write_q = {
            .head = 0,
            .tail = 0,
        },
        .buffer = {
            .head = 0,
            .tail = 0
        }
    },
};

// struct tty_queue read_q;
void tty_init(void) {
    tty_table[0].write = con_write;
    con_init();
}

// copy_to_buffer handle the keyboard input
// to tty echo char
void copy_to_buffer(struct tty_struct *tty) {
    char ch;
    struct tty_queue *read_q= &tty->read_q;
    struct tty_queue *buffer= &tty->buffer;
    while(!tty_isempty_q(&tty->read_q)) {
        ch = tty_pop_q(read_q); 
        // Judge if it's a normal character
        // if (ch < 32) {
        //     // This is control character
        //     continue;
        // }
        switch(ch) {
            case '\b':
                // This is backspace char
                if (!tty_isempty_q(buffer)) {
                    if(tty_queue_tail(buffer) == '\n')  // \n 不能被清除掉
                       continue ;
                    buffer->tail = (buffer->tail - 1) % TTY_BUF_SIZE;
                } else {
                    continue;
                }
                break;
            case -1:
            case '\n':
                s_printk("Wakeup!\n");
                wake_up(&tty_table[0].buffer.wait_proc);
                break;
                // EOF
            default:
                if (!tty_isfull_q(buffer)) {
                    tty_push_q(buffer, ch);
                } else {
                    // here we need to sleep until the queue
                    // is not full
                }
                break;
        }

        if (tty->flags | TTY_ECHO) {
            tty_push_q(&tty->write_q, ch);
            tty->write(tty);
        }
    }
    return ;
}

void tty_write(struct tty_struct* tty) {
    tty->write(tty);
    return ;
}

// 队列为空时,进程进入睡眠状态, 可被中断唤醒
// static void sleep_if_empty(struct tty_queue *q) {
//     cli();
//     while(!current->signal && tty_isempty_q(q))
//         interruptible_sleep_on(&q->wait_proc); 
//     sti();
// }

// static void sleep_if_full(struct tty_queue *q) {
//     cli();
//     while(!current->signal && tty_isfull_q(q))
//         interruptible_sleep_on(&q->wait_proc); 
//     sti();
// }

// tty_read 函数从 tty->buffer 中读取内容
// 当没有读到足够的字符且 buffer 为空的时候
// sleep
// nr = -1 means only stop at \n
int tty_read(int channel, char *buf, int nr) {
    int len = 0;
    char ch;
    char *p = buf;
    // return from the tty_read when recv a \n or EOF
    // Sanity check
#ifdef DEBUG
    s_printk("buf addr = 0x%x\n", buf);
#endif
    if (channel > 2 || channel < 0 || nr < 0)
        return -1;
    struct tty_struct *tty = tty_table + channel;
    // Sleep until the queue is not empty
    // sleep_if_empty(&tty->buffer);
    // Only we get an \n, we start to process
    interruptible_sleep_on(&tty->buffer.wait_proc);

    while (nr > 0) {
        ch = tty_pop_q(&tty->buffer);
        // TODO: Change -1 to EOF
        if (ch == '\n' || ch == -1) {
            // \n Or EOF, we are done
            // Now we can just stop and pop all to buffer
            break;
        }
        /* if (ch == CTRL_INT) return -1; */
        // TODO: Why put_fs_byte don't work here
        // put_fs_byte(ch, p);
        *p++ = ch;
        s_printk("%s\n", buf);
        len++;
        nr--;
    }
    // while (!done) {
    //     sleep_if_empty(&tty->buffer);
    //     for (i = tty->buffer.head; i != tty->buffer.tail; i = (i + 1) % TTY_BUF_SIZE) {
    //         if (tty->buffer.buf[i] == '\n' || tty->buffer.buf[i] == -1) {
    //             done = 1;
    //             break;
    //         }
    //     }
    // }
    return len;
}


================================================
FILE: kernel/chr_drv/tty_io.c.rej
================================================
--- kernel/chr_drv/tty_io.c
+++ kernel/chr_drv/tty_io.c
@@ -141,9 +141,7 @@ int tty_read(int channel, char *buf, int nr) {
             break;
         }
         /* if (ch == CTRL_INT) return -1; */
-        put_fs_byte(ch, p);
-        *p++ = ch;
-        s_printk("%s\n", buf);
+        put_fs_byte(ch, p++);
         len++;
         nr--;
     }


================================================
FILE: kernel/chr_drv/tty_queue.c
================================================
#include <linux/head.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <serial_debug.h>


// 定义对 tty_queue 的操作
// tty_queue 是一个循环队列,操作要注意这一点

int tty_isempty_q(const struct tty_queue *q) {
    if (q->head == q->tail)
        return 1;
    return 0;
}

int tty_isfull_q(const struct tty_queue *q) {
    if ((q->tail + 1) % TTY_BUF_SIZE == q->head)
        return 1;
    return 0;
}

// Here we should check the queue, if emtpy sleep
char tty_pop_q(struct tty_queue *q) {
    sleep_if_empty(q);
    char ch;
    ch = q->buf[q->head];
    q->head = (q->head + 1) % TTY_BUF_SIZE;
    return ch;
}

// 队列满状态下返回 -1
int tty_push_q(struct tty_queue *q, char ch) {
    if (tty_isfull_q(q))
        return -1;
    q->buf[q->tail] = ch;
    q->tail = (q->tail + 1) % TTY_BUF_SIZE;
    return 0;
}

int tty_push_q_front(struct tty_queue *q, char ch) {
    if (tty_isfull_q(q))
        return -1;
    q->head = (q->head - 1) % TTY_BUF_SIZE;
    q->buf[q->head] = ch;
    return 0;
}

char tty_queue_head(const struct tty_queue *q) {
    return q->buf[q->head];
}

char tty_queue_tail(const struct tty_queue *q) {
    return q->buf[q->tail];
}

void tty_queue_stat(const struct tty_queue *q) {
    unsigned int i;
    s_printk("[DEBUG] Queue head = %d, tail = %d\n", q->head, q->tail);
    s_printk("[DEBUG] Queue: [ ");
    for (i = q->head; i != q->tail; i = (i + 1) % TTY_BUF_SIZE) {
        s_printk("%d<-", q->buf[i]);
        if(i == (q->tail - 1) % TTY_BUF_SIZE) {
            s_printk("#");
        }
    }
    s_printk(" ]\n");
}


================================================
FILE: kernel/chr_drv/tty_read.c
================================================
#define __LIBRARY__
#include <unistd.h>
#include <linux/tty.h>

// Define user_tty_read syscall for test
_syscall3(int, user_tty_read, int, channel, char *, buf, int, nr)
_syscall3(int, user_tty_write, int, channel, char *, buf, int, nr)


================================================
FILE: kernel/chr_drv/vga_console.c
================================================
/*
 * console low level implementation
 */

#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <asm/system.h>
#include <asm/io.h>
#include <stdarg.h>
#include <stddef.h>
#include <serial_debug.h>

#define PAGE_SIZE 4096
#define VIDEO_MEM 0xB8000
#define VIDEO_X_SZ 80
#define VIDEO_Y_SZ 25
#define TAB_LEN 8
#define CALC_MEM(x, y) (2*((x) + 80*(y)))

extern void keyboard_interrupt(char scancode);

int video_x, video_y;

long user_stack[PAGE_SIZE>>2];

struct video_info {
    unsigned int retval;        // Return value
    unsigned int colormode;     // Color bits
    unsigned int feature;       // Feature settings
};

extern int video_x;
extern int video_y;

char *video_buffer = (char *)VIDEO_MEM;

void video_init() {
    // struct video_info *info = (struct video_info *)0x9000;

    video_x = 0;
    video_y = 0;
    video_clear();
    update_cursor(video_y, video_x);
}

int video_getx() {
    return video_x;
}

int video_gety() {
    return video_y;
}

void update_cursor(int row, int col) {
    unsigned int pos = ((unsigned int)row * VIDEO_X_SZ) + (unsigned int)col;
    // LOW Cursor port to VGA Index Register
    outb(0x3D4, 0x0F);
    outb(0x3D5, (unsigned char)(pos & 0xFF));
    // High Cursor port to VGA Index Register
    outb(0x3D4, 0x0E);
    outb(0x3D5, (unsigned char)((pos >> 8) & 0xFF));
    return ;
}

int get_cursor() {
    int offset;
    outb(0x3D4,0xF);
    offset=inb(0x3D5)<<8;
    outb(0x3D4,0xE);
    offset+=inb(0x3D5);
    return offset;
}

void video_putchar(char ch) {
    if(ch == '\n') {
        video_x = 0;
        video_y++;
    }
    else if(ch == '\t') {
        while(video_x % TAB_LEN) video_x++;
    }
    else if(ch == '\b') {
        video_x--;
        if (video_x < 0) {
            video_x = VIDEO_X_SZ;
            video_y--;
            if (video_y < 0) video_y = 0;
        }
        // erase char
        video_putchar_at(' ', video_x, video_y, 0x0F);
    }
    else {
        video_putchar_at(ch, video_x, video_y, 0x0F);
        video_x++;

    }
    if(video_x >= VIDEO_X_SZ) {
        video_x = 0;
        video_y++;
    }
    if(video_y >= VIDEO_Y_SZ) {
        roll_screen();
        video_x = 0;
        video_y = VIDEO_Y_SZ - 1;
    }

    update_cursor(video_y, video_x);
    return ;
}

void con_write(struct tty_struct *tty) {
    char ch;
    s_printk("Console write called\n");
    while (!tty_isempty_q(&tty->write_q)) {
        // tty_queue_stat(&tty->write_q);
        ch = tty_pop_q(&tty->write_q);
        video_putchar(ch);
    }
    s_printk("Console write returned\n");
    return ;
}

void video_clear() {
    int i;
    int j;
    video_x = 0;
    video_y = 0;
    for(i = 0; i < VIDEO_X_SZ; i++) {
        for(j = 0; j < VIDEO_Y_SZ; j++) {
           video_putchar_at(' ', i, j, 0x0F);  // DO NOT USE 0x00 HERE, YOU WILL LOSE YOUR LOVELY BLINKING CURSOR(
        }
    }
    return ;
}

void video_putchar_at(char ch, int x, int y, char attr) {
    if(x >= 80)
        x = 80;
    if(y >= 25)
        y = 25;
    *(video_buffer + 2*(x+80*y)) = ch;              // You should write it correct, think carefully
    *(video_buffer + 2*(x+80*y)+1) = attr;          // Previous code : (video_buffer + 2*x + 80*y) (suck)
    return ;
}

void roll_screen() {
    int i;
    // Copy line A + 1 to line A
    for(i = 1; i < VIDEO_Y_SZ; i++) {
        memcpy(video_buffer + (i - 1) * 80 * 2, video_buffer + i * 80 * 2, VIDEO_X_SZ, 2*sizeof(char));
    }
    // Clear the last line
    for(i = 0; i < VIDEO_X_SZ; i++) {
        video_putchar_at(' ', i, VIDEO_Y_SZ - 1, 0x0F);
    }
    return ;
}

void memcpy(char *dest, char *src, int count, int size) {
    int i;
    int j;
    for(i = 0; i < count; i++) {
        for(j = 0; j < size; j++) {
            *(dest + i*size + j) = *(src + i*size + j);
        }
    }
    return ;
}

void con_init(void) {
    register unsigned char a;
    set_trap_gate(0x21, &keyboard_interrupt);
    video_init();
    outb_p(0x21, inb_p(0x21)&0xfd);
    a = inb_p(0x61);
    outb_p(0x61, a | 0x80);
    outb(0x61, a);
}


================================================
FILE: kernel/exit.c
================================================
#include <errno.h>
#include <signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <serial_debug.h>
#include <asm/segment.h>
#include <sys/wait.h>

static inline int send_sig(long sig, struct task_struct* p, int priv);
static void tell_father(int pid);

// sys_kill 为系统调用 kill 的入口点
// TODO: 增加权限判断逻辑 sys.c
int sys_kill(int pid, int sig) {
    struct task_struct **p = task + NR_TASKS;
    int err, retval = 0;
    s_printk("sys_kill entered\n");
    // 目前没有进程组的概念,因而我们只处理 pid > 0 的情况
    if (pid > 0) {
        while(--p > &FIRST_TASK) {
            if (*p && (*p)->pid == pid) {
                // 因为我们没有实现sys.c相关的逻辑,这里暂时给予执行kill的用户最高权限
                if ((err = send_sig(sig, *p, 1))) {
                    s_printk("send_sig error err = %d\n", err);
                    retval = err;
                }
            }
        }
    }
    s_printk("sys_kill leaved, retval = %d\n", retval);
    return retval;
}

// send_sig 用来向进程发送信号
// TODO: 增加权限判断逻辑 sys.c
static inline int send_sig(long sig, struct task_struct* p, int priv) {
    // s_printk("send_sig entered\n");
    // First check params
    if (!p || sig < 1 || sig > 32)
        return -EINVAL;
    if(priv || current->euid == p->euid /*|| suser() */) // 目前我们没有对用户的权限检查
        p->signal |= (1<<(sig - 1));
    else
        return -EPERM;
    return 0;
}

// Use to release the task
void release(struct task_struct *p) {
    int i;
    if(!p) {
        return;
    }
    for(i = 0; i < NR_TASKS; i++) {
        if(task[i] == p) {
            task[i] = NULL;
            free_page((unsigned long) p);
            schedule();
            return;
        }
    }
    panic("Trying to release non-existent task");
}

// Do exit 将进程置成 TASK_ZOMBIE 状态
// 并做收尾工作,不再返回到此内部
int do_exit(long code) {
    int i;

    // 释放当前进程代码段和数据(fs,也是用户堆栈)段的内存页
    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; i < NR_TASKS; i++) {
        if(task[i] && task[i]->father == current->pid) {
            task[i]->father = 1;
            if(task[i]->state == TASK_ZOMBIE)
                (void) send_sig(SIGCHLD, task[1], 1); // TODO: Why sending SIGCHLD to init will make init terminate the child process?
        }
    }
    
    // TODO: 释放 TTY 资源
    if(last_task_used_math == current) {
        last_task_used_math = NULL;
    }

    if(current->leader) {
        // TODO: 释放会话中全部进程
    }

    current->state = TASK_ZOMBIE;
    current->exit_code = code;
    tell_father(current->father);
    schedule();

    // Code will never come here
    // just use to suppress warning
    return -1;
}

// 左移八位是 wait, waitpid 所需, 用低位存 wait() 的状态信息
pid_t sys_exit(int error_code) {
    return do_exit((error_code & 0xff) << 8);
}

// 用来挂起当前进程,直到等待的 pid 退出或者收到要终止该进程的信号
// 如果进程是僵尸进程,则此系统调用立即返回,子进程使用的所有资源释放
pid_t sys_waitpid(pid_t pid, unsigned long stat_addr, int options) {
    int flag, code;
    struct task_struct **p;

    verify_area((void *)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;
        }

        // 检查是否是要等待的 pid
        if (pid > 0) {
            if ((*p)->pid != pid) {
                continue;
            }
        }
        // pid = 0 表示等待进程组全部子进程
        if (!pid) {
            if ((*p)->pgrp != current->pgrp) {
                continue;
            }
        }
        // pid < 0 则等待所有进程组号 = abs(pid) 的进程
        if (pid != -1) {
            if((*p)->pgrp != -pid) {
                continue;
            }
        }
        switch ((*p)->state) {
            case TASK_STOPPED:
                // WUNTRACED 置位,立即返回
                if(!(options & WUNTRACED)) {
                    continue;
                }
                // 写入用户数据段
                put_fs_long(0x7f, (unsigned long *)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((unsigned long)code, (unsigned long *)stat_addr);
                return flag;
            default:
                flag = 1;
                continue;
        }
    }

    // 如果 flag 被置位,则说明我们找到了需要等待的进程,
    // 且进程不是退出或者僵死状态
    if (flag) {
        if (options & WNOHANG) {
            return 0;
        }
        current->state = TASK_INTERRUPTIBLE;
        schedule();
        if(!(current->signal & ~(1<<(SIGCHLD-1)))) {
            goto repeat;
        }
        return -EINTR;
    }
    // 没有找到适合的子进程
    return -ECHILD;
}


// 通知父进程,向父进程发送 SIGCHLD 信号
static void tell_father(int pid) {
    int i;
    for (i = 0; i < NR_TASKS; i++) {
        if (!task[i]) {
            continue;
        }
        if(task[i]->pid != pid) {
            continue;
        }
        task[i]->signal |= (1<<(SIGCHLD-1));
        return ;
    }
    s_printk("[DEBUG] NO FATHER!!!!\n");
    release(current);
}


================================================
FILE: kernel/fork.c
================================================
//#include <errno.h> 当前所有错误号为stub状态
#include <linux/sched.h>
#include <asm/system.h>
#include <linux/mm.h>
//#include <linux/sys.h>
#include <linux/kernel.h>

extern void write_verify(unsigned long address);

long last_pid = 0;

// 对齐内存区域,并且对区域进行验证,区域以4096Byte
// 为单位, 进行write_verify(因为当时的CPU还不支持WP位)
void verify_area(void *addr, unsigned int size) {
    unsigned long start;

    start = (unsigned long)addr;
    size += start & 0xfff;      // size align
    start &= 0xfffff000;        // start align at 4096Byte
    start += get_base(current->ldt[2]);
    while((int)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_code_base != old_data_base) // This should never happen
        panic("Codeseg not fully overlapped with dataseg");
    if(data_limit < code_limit)
        panic("bad data limit");

    new_data_base = new_code_base = (unsigned int)nr * 0x4000000; // 64MB per nr
    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)) {
        printk("free_page_tables: from copy_mem\n");
        free_page_tables(new_data_base, data_limit);
        return -1;
    }
    return 0;
}

// 这里拷贝PCB,以及代码段数据段, 首先我们需要知道,C 语言中参数的获取顺序
// 在函数参数列表中的参数,顺序是这样的,参数列表最后面的参数,对应
// 在汇编中最先压栈的参数(就是离栈顶最远的参数)
// 然后这里的参数涉及到很多,解释一下为什么会有这些参数
// 首先 CPU 进入中断调用,压栈SS, ESP, 标志寄存器 EFLAGS 和返回地址CS:EIP
// 然后是system_call里压栈的寄存器 ds, es, fs, edx, ecx, ebx
// 然后是调用sys_fork函数时,压入的函数返回地址
// 以及sys_fork里压栈的那些参数 gs, esi, edi, ebp, eax(nr)
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;

    /* Only for emit the warning! */
    i = none;
    none = i;
    //i = eflags;
    //eflags = i;
    /* End */
    
    p = (struct task_struct *) get_free_page();
    if(!p)
        return -1;
    task[nr] = p;
    *p = *current; // 这里仅仅复制PCB(task_struct)

    // 初始化 PCB, TSS
    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;
    p->utime = p->stime = 0;
    p->cutime = p->cstime = 0;
    p->start_time = jiffies;
    p->tss.back_link = 0;
    p->tss.eflags = eflags;
    p->tss.esp0 = PAGE_SIZE + (long)p;
    p->tss.ss0 = 0x10;
    p->tss.eip = eip;
    p->tss.eax = 0; // 这是当fork返回的时候,子进程返回0的原因
    p->tss.ecx = ecx;
    p->tss.ebx = ebx;
    p->tss.edx = edx;
    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.ds = ds & 0xffff;
    p->tss.ss = ss & 0xffff;
    p->tss.fs = fs & 0xffff;
    p->tss.gs = gs & 0xffff;
    p->tss.ldt = _LDT(nr);
    p->tss.bitmap = 0x80000000;
    // 如果之前的进程使用了协处理器,这里复位TS标志
    if(last_task_used_math == current)
        __asm__ ("clts; fnsave %0"::"m" (p->tss.i387));
    // 复制进程页表
    if(copy_mem(nr, p)) {
        task[nr] = NULL;
        free_page((unsigned long)p);
        return -1;
    }

    // 设置好TSS和LDT描述符,然后将任务置位
    // 就绪状态,可以被OS调度
    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;

    return last_pid;    // 父进程返回儿子的pid
}

int find_empty_process(void) {
    int i;
    long tmp = last_pid;    // 记录最初起始进程号,用于标记循环结束
    
    while(1) {
        for(i = 0; i < NR_TASKS; i++) {
            if(task[i] && task[i]->pid == last_pid)
                break;
        }
        if(i == NR_TASKS) break;
        if((++last_pid) == tmp)
            break;
        // 判断last_pid 是否超出进程号数据表示范围,如果超出
        // 则置为1
        if(last_pid < 0) last_pid = 1; 
    }
    for(i = 1; i < NR_TASKS; i++)
        if(!task[i])
            return i;
    return -1;
}




================================================
FILE: kernel/libc_restore.s
================================================
.global __sig_restore, __masksig_restore

__sig_restore:		# Use to restore to user routine
	addl $4, %esp
	popl %eax
	popl %ecx
	popl %edx
	popfl
	ret

__masksig_restore:
	addl $4, %esp
# We need a call here to set mask
	addl $4, %esp
	popl %eax
	popl %ecx
	popl %edx
	popfl
	ret


================================================
FILE: kernel/panic.c
================================================
/*
 * Use for trigger a kernel panic
 * When panic, it will print out a message and then 
 * dead, use for alerting a major problem
 */
#define PANIC

#include <linux/kernel.h>

void panic(const char *str) {
    printk("Kernel Panic: %s\n", str);
    for(;;);
}



================================================
FILE: kernel/printk.c
================================================
/*
 * Rewrite printk
 * now printk call for sprintf
 * then call tty_write
 * (user mode will call write(x, x, x)
 */
#include <stdarg.h>
#include <stddef.h>
#include <linux/tty.h>
#include <serial_debug.h>

void vsprintf(char *dest, char *fmt, ...);

void printk(char *fmt, ...) {
    int i = 0;
    char buf[TTY_BUF_SIZE] = "";
    char *ptr = buf;
    va_list ap;
    for(i = 0; i < TTY_BUF_SIZE; i++) {
        buf[i] = 0;
    }
    s_printk("[DEBUG] printk called, fmt = %s", fmt);
    va_start(ap, fmt); 
    vsprintf(buf, fmt, ap);
    va_end(ap);
    while(*ptr) {
        tty_push_q(&tty_table[0].write_q, *ptr);
        tty_queue_stat(&tty_table[0].write_q);
        ptr++;
    }
    tty_write(&tty_table[0]);
}




================================================
FILE: kernel/sched.c
================================================
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/sys.h>
#include <asm/system.h>
#include <asm/io.h>
#include <serial_debug.h>

// #define DEBUG

#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))

extern int timer_interrupt(void);
extern int system_call(void);

union task_union {
    struct task_struct task;
    char stack[PAGE_SIZE];
};

static union task_union init_task = {INIT_TASK,};

long user_stack[PAGE_SIZE >> 2];
long startup_time;
struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
struct task_struct *task[NR_TASKS] = {&(init_task.task), };

struct {
    long *a;
    short b;
} stack_start = {&user_stack[PAGE_SIZE >> 2], 0x10};

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();
    *p = tmp;
    if (tmp)
        tmp->state = TASK_RUNNING;
}

void schedule(void) {
    int i, next, c;
    struct task_struct **p;

    // 先处理信号
    for (p = &LAST_TASK; p > &FIRST_TASK; --p) {
        if(*p) {
            // 如果 进程 p 有 alarm 而且目前时间已经超过 alarm
            // 对进程 p 发送 SIGALARM 信号
            if((*p)->alarm && (*p)->alarm < jiffies) {
                (*p)->signal |= (1<<(SIGALRM-1));
#ifdef DEBUG
                s_printk("[DEBUG] process get SIGALRM signal: 0x%x mask: 0x%x\n blockable= 0x%x", \
                        (*p)->signal, (*p)->blocked, _BLOCKABLE);
#endif
                (*p)->alarm = 0;
            }
            // 如果进程收到了信号,那么就对进程进行唤醒
            if(((unsigned long)((*p)->signal) & (unsigned long)(((unsigned long)(_BLOCKABLE) & (~(*p)->blocked))))  \
                        && (unsigned long)((*p)->state) \
                        == TASK_INTERRUPTIBLE) {
                (*p)->state = TASK_RUNNING;
            }
        }
    }

    while(1) {
        // 初始化,i, p指向任务链表的末尾
        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;
            }
        }
        // 如果循环之后,系统中的任务只有counter > 0的或者没有任何可以
        // 运行的任务,那么就退出循环并进行任务切换
        if(c) break;
        for( p = &LAST_TASK; p > &FIRST_TASK; p--) {
            if(*p) {
                (*p)->counter = ((*p)->counter >> 1) + (*p)->priority;
            }
        }
    }
    // switch_to 接收参数为一个 Task 号
    // show_task_info(task[next]);
    // printk("[%x] Scheduler select task %d\n", jiffies, next);
    // printk("[%x] Scheduler select task\n", next);
#ifdef DEBUG
    s_printk("[DEBUG] [%x] Scheduler select task %d\n", jiffies, next); // THE 'next' VALUE IS NOT CORRECT!
    s_printk("[DEBUG] Scheduler select task %d\n", next);  // THIS CAUSE CRASH
#endif
    // TODO Fix the bug
    // These two lines of code will cause OS Crash
    // When switch to printk it won't
    // Why?
    switch_to(next)
}

void show_task_info(struct task_struct *task) {
    s_printk("Current task Info\n================\n");
    s_printk("pid = %d\n", task->state);
    s_printk("counter = %d\n", task->counter);
    s_printk("start_code = %x\n", task->start_code);
    s_printk("end_code = %x\n", task->end_code);
    s_printk("brk = %x\n", current->ldt[0]);
    s_printk("gid = 0x%x\n", current->gid);
    s_printk("tss.ldt = 0x%x\n", current->tss.ldt);
    // s_printk("tss.eip = 0x%x\n", current->eip);
}

void wake_up(struct task_struct **p) {
    if(p && *p) {
        (*p)->state = TASK_RUNNING;
        *p = NULL;
    }
}

// 假设我们执行任务A的时候调用了这个函数
void interruptible_sleep_on(struct task_struct **p) {
    struct task_struct *tmp;
    
    if(!p)
        return;
    if(current == &(init_task.task)) // 我们不能让 init sleep
        panic("task[0] trying to sleep");
    tmp = *p;
    *p = current;
rep_label: current->state = TASK_INTERRUPTIBLE;
    // 这里会转到其他任务去执行
    schedule(); 
    // 回来的时候说明,调度程序调度到了这里,current = B
    // 我们要检查队列,如果 *p (这里应该是A) 和 当前运行任务不等
    // 如果不是的话
    if(*p && *p != current) {
        (*p)->state = TASK_RUNNING;
        goto rep_label;
    }
    *p = tmp;
    if(tmp)
        tmp->state = TASK_RUNNING;
}

int sys_pause(void) {
    current->state = TASK_INTERRUPTIBLE;
    schedule();
    return 0;
}

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

// 计时器函数 do_timer, 记录程序运行时间,如果处于 CPL = 2 的进程执行时间超过时间片
// 则进行调度,否则继续。

// 下面的这段代码是一个demo,用来验证时钟中断
// 已经设置好并且可用, 目前不支持其他定时器
// floppy操作依旧不支持
int counter = 0;
long volatile jiffies = 0;
void do_timer(long cpl) {
    if (!cpl)
        current->stime++;
    else
        current->utime++;
    if((--current->counter) > 0) return ;
    current->counter = 0;
    if(!cpl) return;
    schedule();
}

// 这是一个临时函数,用于初始化8253计时器
// 并开启时钟中断
void sched_init() {
    int divisor = 1193180/HZ;

    int i;
    struct desc_struct *p;

    // 初始化任务0的TSS, LDT
    set_tss_desc(gdt + FIRST_TSS_ENTRY, &(init_task.task.tss));
    set_ldt_desc(gdt + FIRST_LDT_ENTRY, &(init_task.task.ldt));

    // 初始化其余各项,因为TSS LDT各占一个,所以
    // +2 指向任务1的TSS, LDT(初始任务为0)
    // 把其余各项设置为0
    p = gdt + 2 + FIRST_TSS_ENTRY;
    for(i = 1; i < NR_TASKS; i++) {
        task[i] = NULL;
        p->a = p->b = 0;
        p++;
        p->a = p->b = 0;
        p++;
    }

    // Clear Nested Task(NT) Flag
    __asm__("pushfl; andl $0xffffbfff, (%esp); popfl");
    ltr(0);
    lldt(0);
    
    outb_p(0x43, 0x36);
    outb_p(0x40, divisor & 0xFF);
    outb_p(0x40, divisor >> 8);

    // timer interrupt gate setup: INT 0x20
    set_intr_gate(0x20, &timer_interrupt);
    // Make 8259 accept timer interrupt
    outb(0x21, inb_p(0x21) & ~0x01);

    // 初始化 system_call
    set_system_gate(0x80, &system_call);
}

#undef DEBUG


================================================
FILE: kernel/serial_debug.c
================================================
#include <asm/io.h>
#include <stdarg.h>
#define PORT 0x3f8

int is_transmit_empty() {
    return inb(PORT + 5) & 0x20;
}

void s_putchar(char a) {
    while (is_transmit_empty() == 0);

    outb(PORT,a);
}

void s_puts(char *a) {
    while(*a) {
        s_putchar(*a++);
    }
}

int serial_debugstr(char *str) {
    s_puts(str);
    return 0;
}

void s_printnum(int num, int base, int sign) {
    char digits[] = "0123456789ABCDEF";
    char buf[50] = "";
    int cnt = 0;
    int i;

    if(sign && num < 0) {       // Check for sign or unsign
        s_putchar('-');
        num = -num;
    }

    if(num == 0) {
        s_putchar('0');
        return ;
    }
    while(num) {
        buf[cnt++] = digits[num % base];
        num = num / base;
    }

    for(i = cnt - 1; i >=0; i--) {
        s_putchar(buf[i]);
    }
    return ;
}



void s_printk(char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);

    char c, *s;
    while(*fmt) {
        c = *fmt++;
        if(c != '%') {
            s_putchar(c);
            continue;
        }
        c = *fmt++;
        if(c == '\0')
            break;
        switch(c) {
            case 'd':
                s_printnum(va_arg(ap, int), 10, 1);
                break;
            case 'u':
                s_printnum(va_arg(ap, int), 10, 0);
                break;
            case 'x':
                s_printnum(va_arg(ap, int), 16, 0);
                break;
            case 's':
                s = va_arg(ap, char*);
                while(*s)
                    s_putchar(*s++);
                break;
            case 'c':
                s_putchar(va_arg(ap, char));
                break;
            case '%':
                s_putchar('%');
        }
    }
    return;
}


================================================
FILE: kernel/signal.c
================================================
#include <linux/kernel.h>
#include <signal.h>
#include <sys/types.h>
#include <serial_debug.h>
#include <asm/segment.h>
#include <linux/sched.h>

#define DEBUG

// do_signal 函数调用之前内核栈的参数分布,见文档

void dump_sigaction(struct sigaction *action) {
    s_printk("[DEBUG] Sigaction dump\n");
    s_printk("[DEBUG] addr = 0x%x, sa_mask = 0x%x, sa_handler = 0x%x, sa_restorer = 0x%x\n", 
            action, action->sa_mask, action->sa_handler, action->sa_restorer);
}

void do_signal(long signr, long eax, long ebx, long ecx,
        long edx, long fs, long es, long ds,
        unsigned long eip, long cs, long eflags, unsigned long *esp, long ss) {
#ifdef DEBUG
    s_printk("[DEBUG] Signal = %d\n", signr);
    s_printk("[DEBUG] Context signr = %d, eax = 0x%x, ebx = 0x%x, ecx = 0x%x\n \
            edx = 0x%x, fs = 0x%x, es = 0x%x, ds = 0x%x\n \
            eip = 0x%x, cs = 0x%x, eflags = 0x%x, esp = 0x%x, ss= 0x%x\n",
            signr, eax, ebx, ecx, edx, fs, es, ds, eip, cs, eflags ,esp, ss);
    s_printk("[DEBUG] current pid = %d\n", current->pid);
    dump_sigaction(&current->sigaction[signr - 1]);
#endif
    unsigned long sa_handler;
    unsigned long old_eip = eip;
    struct sigaction *sa = current->sigaction + signr - 1;
    unsigned int longs;
    unsigned long *tmp_esp;

    sa_handler = (unsigned long)sa->sa_handler;
    // IGNORE HANDLER
    if (sa_handler == 1) {
        return ;
    }
    // DEFAULT HANDLER
    if (!sa_handler) {
        return ;
        // if(signr == SIGCHLD)
        //     return;
        // else
        //     sys_debug("Default signal handler");
    }
    // User registered signal handler, then process
    if ((sa->sa_flags & SA_ONESHOT)) {
        sa->sa_handler = NULL;
    }
    // Make EIP in the stack pointed to handler function
    *(&eip) = sa_handler;
    // Check wether we need to push sigmask
    longs = (sa->sa_flags & SA_NOMASK)?7:8;
    // Grow the stack
    *(&esp) -= longs;

    verify_area((void *)esp, longs * 4);
    tmp_esp = (unsigned long *)esp;
    put_fs_long((unsigned long)sa->sa_restorer, tmp_esp++);
    put_fs_long((unsigned long)signr, tmp_esp++);
    if (!(sa->sa_flags & SA_NOMASK))
        put_fs_long((unsigned long)current->blocked, tmp_esp++);
    put_fs_long((unsigned long)eax, tmp_esp++);
    put_fs_long((unsigned long)ecx, tmp_esp++);
    put_fs_long((unsigned long)edx, tmp_esp++);
    put_fs_long((unsigned long)eflags, tmp_esp++);
    put_fs_long((unsigned long)old_eip, tmp_esp++);
    current->blocked |= sa->sa_mask;
}

int sys_sgetmask() {
    return (int)current->blocked;
}

int sys_ssetmask(int newmask) {
    int old;
    old = (int)current->blocked;
    // We cannot mask SIGKILL
    current->blocked = (unsigned long)(newmask & ~(1<<(SIGKILL-1)));
    return old;
}

// Save old first verify the area has enough space
// and correct permission
static inline void save_old(char *from, char *to) {
    unsigned int i;
    verify_area((void *)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) {
    unsigned int i;
    for (i = 0; i < sizeof(struct sigaction); i++) {
        *to = get_fs_byte(from);
        from++;
        to++;
    }
}

// set signal handler in old styled POSIX way
int sys_signal(int signum, long handler, long restorer) {
    struct sigaction tmp;
    // Validate the signal
    if (signum < 1 || signum > 32 || signum == SIGKILL) {
        return -1;
    }
    tmp.sa_handler = (void (*)(int))handler;
    tmp.sa_mask = 0;
    tmp.sa_flags = (int)(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;
}

int sys_sigaction(int signum, const struct sigaction* action, 
        struct sigaction *old_action) {
    struct sigaction tmp;
#ifdef DEBUG
    s_printk("[DEBUG] sys_sigaction entered, signum = %d\n", signum);
#endif
    if (signum < 1 || signum > 32 || signum == SIGKILL)
        return -1;
    tmp = current->sigaction[signum - 1];
#ifdef DEBUG
    s_printk("[DEBUG] sigaction MARK %d\n", __LINE__);
#endif
    get_new((char *)action, (char *)&(current->sigaction[signum - 1]));
    if (old_action)
        save_old((char *) &tmp, (char *)old_action);
    if (current->sigaction[signum - 1].sa_flags & SA_NOMASK)
        current->sigaction[signum - 1].sa_mask = 0;
    else
        current->sigaction[signum - 1].sa_mask |= (sigset_t)(1<<(signum - 1));
#ifdef DEBUG
    s_printk("[DEBUG] current pid = %d\n", current->pid);
    dump_sigaction(&current->sigaction[signum-1]);
#endif
    return 0;
}


#undef DEBUG


================================================
FILE: kernel/signal_demo.c
================================================
#define __LIBRARY__
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <linux/kernel.h>

// First we define kill system call

_syscall2(int, kill, int, pid, int, sig)
static inline _syscall1(int, sys_debug, char *, str)
static inline _syscall3(int, sigaction, int, signum, struct sigaction *, action,
        struct sigaction *, old_action)

extern void __sig_restore();
extern void __masksig_restore();

// A function use to demo signal usage
//

static void demo_handle(int sig) {
    sys_debug("Demo handler activate!\n");
    sig++;
}


void signal_demo_main(void) {
    int ret = 0;
    struct sigaction sa_action;
    sa_action.sa_handler = demo_handle;
    // sa_action.sa_flags |= SA_NOMASK; // TODO: Add mask
    sa_action.sa_mask = 0;
    sa_action.sa_restorer = __sig_restore;
    ret = sigaction(SIGUSR1, &sa_action, NULL);
    if (ret) {
        panic("sigaction set failed");
    }
    kill(1, SIGUSR1);
    return;
}



================================================
FILE: kernel/sys.c
================================================
#include <unistd.h>
#include <linux/head.h>
#include <linux/sched.h>
#include <serial_debug.h>

// #define DEBUG

extern int sys_alarm(long seconds);

int stub_syscall(void) {
    return 0;
}

// Here we implement syscall for sleep
int sys_sleep(long seconds) {
#ifdef DEBUG
    s_printk("[DEBUG] syscall entered, sleeping\n");
#endif
    sys_alarm(seconds);
    current->state = TASK_INTERRUPTIBLE;
    schedule();
    // when sleep wakeup, we clear the alarm
    current->signal = current->signal & (~(1<<(SIGALRM-1)));
    return 0;
}


================================================
FILE: kernel/system_call.s
================================================
# system_call.s 里面实现了系统调用的过程
# 实现了时钟中断和fork 必要的系统调用
# 还没有对信号进行处理

.global timer_interrupt, system_call, sys_fork

# 我们来定义一下栈的布局

EAX = 0x00
EBX = 0x04
ECX = 0x08
EDX = 0x0C
FS = 0x10
ES = 0x14
DS = 0x18
EIP = 0x1C
CS = 0x20
EFLAGS = 0x24
OLDESP = 0x28
OLDSS = 0x2C

nr_system_calls = 72+1 #sys_debug

# offset in task_struct
state = 0
counter = 4
priority = 8
signal = 12
sigaction = 16
blocked = (33*16)


.align 2
bad_sys_call:
	movl $-1, %eax
	iret

.align 2
reschedule:
	pushl $ret_from_syscall
	jmp schedule

.align 2
system_call:
	cmp $nr_system_calls - 1, %eax
	ja bad_sys_call
	push %ds
	push %es
	push %fs
	push %edx
	push %ecx
	push %ebx
	movl $0x10, %edx
	mov %dx, %ds
	mov %dx, %es
	movl $0x17, %edx
	mov %dx, %fs
	call *sys_call_table(, %eax, 4)
	pushl %eax
	movl current,%eax		# 这里对task_struct进行判断,如果current->state != TASK_RUNNING, 则需要进行重新调度
	cmp $0, state(%eax)
	jne reschedule
	cmp $0, counter(%eax)	# 如果时间片用光,也需要重新调度
	je reschedule
ret_from_syscall:
# 这里是信号处理部分
	movl current, %eax		# task 0 不处理信号
	cmpl task, %eax
	je 3f
	cmpw $0x0f, CS(%esp)		# 如果二者不等,那么说明是通过中断跳转过来的,不处理信号
	jne 3f						# 
	cmpw $0x17, OLDSS(%esp)		# 如果二者不等,那么说明堆栈段非用户态堆栈,说明系统调用不是用户态发起的,也不处理
	jne 3f
	movl signal(%eax), %ebx
	movl blocked(%eax), %ecx
	notl %ecx
	andl %ebx, %ecx				# 对屏蔽信号和信号set进行运算,如果发现没有信号可以处理,就跳过
	bsfl %ecx, %ecx
	je 3f
	btrl %ecx, %ebx				# 现在ecx里存有第一个有效信号的位置,我们将此信号复位,用Bit Test Reset,将%ebx
								# 里相应位 reset 掉
	movl %ebx, signal(%eax)
	incl %ecx					# 将 offset 调整为从1开始的(1 - 32 信号表示)
	pushl %ecx					# 将信号压栈
	call do_signal
	popl %eax
# 信号处理完毕
3:	popl %eax
	popl %ebx
	popl %ecx
	popl %edx
	pop %fs
	pop %es
	pop %ds
	iret

.align 2
timer_interrupt:
	push %ds
	push %es
	push %fs
	push %edx
	push %ecx
	push %ebx
	push %eax
	movl $0x10, %eax		# 让DS指向内核数据段
	mov %ax, %ds
	mov %ax, %es
	movl $0x17, %eax
	mov	%ax, %fs
	incl jiffies
	movb $0x20, %al
	outb %al, $0x20
	movl CS(%esp), %eax		# 将 CS 取出,并
	andl $3, %eax			# 计算出 CPL
	pushl %eax
	call do_timer
	addl $4, %esp
	jmp ret_from_syscall

.align 2
sys_fork:
	call find_empty_process
	testl %eax, %eax			# 检查返回值是不是负值, 负值意味着没有获得到pid资源
	js 1f
	push %gs
	pushl %esi
	pushl %edi
	pushl %ebp
	pushl %eax
	call copy_process
	addl $20, %esp
1: ret


================================================
FILE: kernel/traps.c
================================================
/*
 * Code to implement 80386 traps and interrupts
 *
 */

#include <linux/head.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/system.h>

//void page_fault(void);

// mm/page.s
void page_fault(void);

// 在kernel/asm.s中用到的函数原型
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 coprocessor_error(void);
void irq13(void);
void reserved(void);
void parallel_interrupt(void);

// 用来引发一个异常,因为目前我们没有实现
// 信号机制,且为单进程,所以die的处理就是打印完必要的信息
// 停机进入死循环
static void die(char *str, long esp_ptr, long nr) {
    long *esp = (long *)esp_ptr;
    printk("%s: %x\n", str, nr & 0xffff);
    printk("EIP: 0x%x:0x%x\n EFLAGS: 0x%x\n ESP 0x%x:0x%x\n",
            esp[1], esp[0], esp[2], esp[4], esp[3]);
    // Some Process Related code, now stub
    printk("base 0x%x, limit 0x%x\n", get_base(current->ldt[1]), get_limit(0x17));
    
    printk("No Process now, System HALT!   :(\n");
    for(;;);
    return ;
}

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) {
    
    // Now we do not support Task Register
    int tr = 0;
    __asm__ volatile("str %%ax":"=a" (tr):"0"(0));
    
    printk("eax\tebx\tecx\tedx\t\n%x\t%x\t%x\t%x\n",eax, ebx, ecx, edx);
    printk("esi\tedi\tebp\tesp\t\n%x\t%x\t%x\t%x\n",esi, edi, ebp, (long)esp);
    printk("ds\tes\tfs\ttr\n%x\t%x\t%x\t%x\n",ds, es, fs, tr);
    printk("EIP: %x    CS:%x     EFLAGS: %x", esp[0], esp[1], esp[2]);
    printk("errno = %d", error_code);
    return ;
}

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) {
    die("coprocessor error", esp, error_code);
}

void do_reserved(long esp, long error_code) {
    die("reserved(15, 17-47)error", esp, error_code);
}

void do_stub(long esp, long error_code) {
    printk("stub interrupt! %x, %x\n", esp, error_code);
}

void trap_init(void) {
    int i;

    set_trap_gate(0, &divide_error);
    set_trap_gate(1, &debug);
    set_trap_gate(2, &nmi);
    set_system_gate(3, &int3);
    set_system_gate(4, &overflow);
    set_system_gate(5, &bounds);
    set_trap_gate(6, &double_fault);
    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);
    // 开放外部中断 8259
    outb_p(0x21, inb_p(0x21)&0xfb);
    outb(0xA1, inb_p(0xA1)&0xdf);
    set_trap_gate(39, &parallel_interrupt);
}


================================================
FILE: kernel/vsprintf.c
================================================
#include <stdarg.h>
#include <stddef.h>
#include <serial_debug.h>

// Implement sprintf for print formatted string
// Convert it to a string

int _sprintnum(char *dest, int num, int base, int sign);
void vsprintf(char *dest, char *fmt, va_list ap) {
    // va_start(ap, fmt);   va_start 只能使用一次,获得 va_list 之后就不要 start 啦, 不然就会出现参数输出全都不对的错误

    char c, *s;
    char *dest_ptr = dest;

    while(*fmt) {
        c = *fmt++;
        if(c != '%') {
            *(dest_ptr++) = c;
            continue;
        }
        c = *fmt++;
        if(c == '\0')
            break;
        switch(c) {
            case 'd':
                dest_ptr = dest_ptr + _sprintnum(dest_ptr, va_arg(ap, int), 10, 1);
                break;
            case 'u':
                dest_ptr = dest_ptr + _sprintnum(dest_ptr, va_arg(ap, int), 10, 0);
                break;
            case 'x':
                dest_ptr = dest_ptr + _sprintnum(dest_ptr, va_arg(ap, int), 16, 0);
                break;
            case 's':
                s = va_arg(ap, char*);
                while(*s)
                    *dest_ptr++ = (*s++);
                break;
            case 'c':
                *dest_ptr++ = (va_arg(ap, char));
                break;
            case '%':
                *dest_ptr++ = ('%');
        }
    }
    return;
}

int _sprintnum(char *dest, int num, int base, int sign) {
    char digits[] = "0123456789ABCDEF";
    char buf[50] = "";
    int cnt = 0;
    int len = 0;
    int i;

    if(sign && num < 0) {       // Check for sign or unsign
        *dest++ = '-';
        num = -num;
        len++;
    }

    if(num == 0) {
        *dest++= '0';
        len++;
        return len;
    }
    while(num) {
        buf[cnt++] = digits[num % base];
        num = num / base;
    }

    for(i = cnt - 1; i >=0; i--) {
        *dest++ = (buf[i]);
        len++;
    }
    return len;
}


================================================
FILE: kernel/vsprintf.c.old
================================================
/* Copyright (C) 2016 Gan Quan <coin2028@hotmail.com>
 * Copyright (C) 2016 David Gao <davidgao1001@gmail.com>
 *
 * This file is part of AIM.
 *
 * AIM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AIM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdarg.h>

typedef unsigned char       uint8_t;
typedef signed char     int8_t;
typedef unsigned short      uint16_t;
typedef signed short        int16_t;
typedef unsigned int        uint32_t;
typedef signed int      int32_t;
typedef unsigned long long  uint64_t;
typedef signed long long    int64_t;
typedef unsigned long size_t;
typedef signed long ssize_t;
int vsnprintf(char *str, size_t size, const char *fmt, va_list ap);

#define NULL 0


int snprintf(char *str, size_t size, const char *fmt, ...)
{
	int result;
	va_list ap;

	va_start(ap, fmt);
	result = vsnprintf(str, size, fmt, ap);
	va_end(ap); 
	return result;
}

#define FLAG_UNSIGNED	0x001		/* unsigned integer */
#define FLAG_ZEROPAD	0x002		/* padding with zero */
#define FLAG_NEG	0x100		/* negative integer */

int vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
{
#define is_digit(ch)	(((ch) >= '0') && ((ch) <= '9'))
#define digits(x)	(((char)(x) <= 9) ? ('0' + (char)(x)) : ('a' + (char)(x) - 10))

	int64_t val;
	uint64_t uval;
	ssize_t width = 0;
	int base;
	int flag = 0;
	ssize_t pos = 0;
#define set_ch(ch) \
	do { \
		str[pos++] = ch; \
		if (pos == size) { \
			str[size - 1] = '\0'; \
			return size; \
		} \
	} while (0)
#define getint(ap) \
	((longflag == 0) ? va_arg(ap, int) : \
	 (longflag == 1) ? va_arg(ap, long) : va_arg(ap, long long))
#define getuint(ap) \
	((longflag == 0) ? va_arg(ap, unsigned int) : \
	 (longflag == 1) ? va_arg(ap, unsigned long) : \
	 va_arg(ap, unsigned long long))

	int len;
	int longflag = 0;

	int buf_pos;
	char buf[25];
	char *s;

	for ( ; *fmt != '\0'; ) {
		if (*fmt == '%') {
			++fmt;
fmt_loop:		switch (*fmt) {
			case 'l':
				longflag++;
				++fmt;
				goto fmt_loop;
			case 'd':
				base = 10;
				val = getint(ap);
				longflag = 0;
				if (val < 0) {
					flag |= FLAG_NEG;
					uval = -val;
				} else
					uval = val;
				goto print_uint;
			case 'o':
				base = 8;
				goto get_uint;
			case 'x':
				base = 16;
				goto get_uint;
			case 'p':
				/* %p equals %08x on 32 bit and %016x on
				 * 64 bit */
				base = 16;
				flag |= FLAG_ZEROPAD;
				longflag = 1;
#ifdef __LP64__	/* 64 bit */
				width = 16;
#else	/* 32 bit */
				width = 8;
#endif	/* __LP64__ */
				goto get_uint;
			case 'u':
				base = 10;
get_uint:			uval = getuint(ap);
				longflag = 0;
print_uint:			buf_pos = 0;
				while (uval > 0) {
					buf[buf_pos++] = digits(uval % base);
					uval /= base;
				}
				if (buf_pos == 0)
					buf[buf_pos++] = '0';
				if (width == 0) {
					if (flag & FLAG_NEG)
						set_ch('-');
					while ((--buf_pos) >= 0)
						set_ch(buf[buf_pos]);
				} else if ((width > 0) && (flag | FLAG_ZEROPAD)) {
					if (flag & FLAG_NEG)
						set_ch('-');
					while ((--width) >= 0) {
						if (width >= buf_pos)
							set_ch('0');
						else
							set_ch(buf[width]);
					}
				} else if (width > 0) {
					if (flag & FLAG_NEG)
						buf[buf_pos++] = '-';
					while ((--width) >= 0) {
						if (width >= buf_pos)
							set_ch(' ');
						else
							set_ch(buf[width]);
					}
				}
				++fmt;
				break;
			case 'c':
				set_ch((char)va_arg(ap, int));
				++fmt;
				break;
			case 's':
				s = va_arg(ap, char *);
				if (s == NULL)
					s = "(null)";
				for (len = 0; *s != '\0'; ++s, ++len)
					set_ch(*s);
				for (; len < width; ++len)
					set_ch(' ');
				++fmt;
				break;
			case '%':
				set_ch('%');
				++fmt;
				break;
			case '0':
				flag |= FLAG_ZEROPAD;
				++fmt;
				goto fmt_loop;
			case '1': case '2': case '3':
			case '4': case '5': case '6':
			case '7': case '8': case '9':
				for ( ; is_digit(*fmt); ++fmt) {
					width *= 10;
					width += (int)(*fmt) - (int)'0';
				}
				goto fmt_loop;
			default:
				set_ch('%');
				goto print_default;
			}
			flag = 0;
			width = 0;
			continue;
		}
print_default:	set_ch(*fmt);
		++fmt;
	}
	set_ch('\0');
	--pos;		/* exclude the null byte */
#undef FLAG_UNSIGNED
#undef FLAG_ZEROPAD
#undef FLAG_NEG
#undef is_digit
#undef set_ch
	return pos;
}



================================================
FILE: ldS.ld
================================================
SECTIONS {
    ENTRY(startup_32)
    .text 0x0000 : {
        *(.text)
    }

    .rodata . : {
        *(.rodata)
    }

    .data . : {
        *(.data)
    }

    /DISCARD/ : {
        *(.eh_frame)
    }
}


================================================
FILE: lib/Makefile
================================================
include ../Makefile.header

OBJS = getline.o printf.o

LDFLAGS	+= -r
CFLAGS += -I../include

.PHONY=clean run all

all: lib.o

lib.o: $(OBJS)
	$(LD) $(LDFLAGS) -o lib.o $(OBJS)

.c.o: 
	@$(CC) $(CFLAGS) \
		-c -o $*.o $<

clean:
	- rm -f $(OBJS) lib.o


================================================
FILE: lib/getline.c
================================================
#define __LIBRARY__
#define EOF -1
#include <unistd.h>
#include <linux/lib.h>
extern int user_tty_read(int channel, char *buf, int nr);


char getchar() {
    char _buf[2048];
    user_tty_read(0, _buf, 1);
    return _buf[0];
}

int getline(char *str) {
    char _buf[2048];
    char ch;
    char *p = str;
    int len = 0;
    while(ch != '\n' && ch != EOF) {
        user_tty_read(0, _buf, 1);
        ch = _buf[0];
        *p++ = ch;
        len++;
    }
    *(p - 1) = '\0';
    return len - 1;     // remove the \n
}


================================================
FILE: lib/printf.c
================================================
/*
 * User mode printf function
 */
#define __LIBRARY__
#include <stdarg.h>
#include <linux/tty.h>
#include <stddef.h>
#include <unistd.h>
#include <serial_debug.h>
 
extern int user_tty_write(int channel, char *buf, int nr);
void vsprintf(char *dest, char *fmt, va_list ap);

static inline int sys_debug(char *str);

static inline _syscall1(int, sys_debug, char *, str)

int printf(char *fmt, ...) {
    int i = 0;
    char buf[TTY_BUF_SIZE] = "";
    char *ptr = buf;
    va_list ap;
    for(i = 0; i < TTY_BUF_SIZE; i++) {
        buf[i] = 0;
    }
    va_start(ap, fmt); 
    vsprintf(buf, fmt, ap);
    va_end(ap);
    i = 0;
    while(*ptr) {
        i++;
        ptr++;
    }
    return user_tty_write(0, buf, i);
}


================================================
FILE: mm/Makefile
================================================
include ../Makefile.header

LDFLAGS	+= -r
CFLAGS += -I../include

.PHONY=clean run all

.c.o:
	@$(CC) $(CFLAGS) \
		-c -o $*.o $<
.s.o:
	@$(AS) -o $*.o $<

.c.s:
	@$(CC) $(CFLAGS) \
		-S -o $*.s $<

OBJS  = memory.o mm_test.o page.o

all: mm.o

mm.o: $(OBJS)
	@$(LD) $(LDFLAGS) -o mm.o $(OBJS)

clean:
	@rm -f core *.o *.a tmp_make
	@for i in *.c;do rm -f `basename $$i .c`.s;done


================================================
FILE: mm/memory.c
================================================
/*
 * Routine use to handle physical page memory
 * memory.c 涉及到的函数均为物理页内存,以及页表管理函数
 * 并且实现了 Demand Loading 以及 Copy On Write
 */

// 以下是一些在 head.s 里已经规划好的参数,在这里以宏的形式再次给出

#include <linux/kernel.h>
#include <linux/head.h>
#include <serial_debug.h>
#include <linux/mm.h>

#define LOW_MEM 0x100000ul // 0x00000000 - 0x00100000 为物理内存低 1MB 空间, 是系统代码所在
#define PAGING_MEMORY (15*1024*1024) // 剩余 15MB 空闲物理内存用于分页
#define PAGING_PAGES (PAGING_MEMORY >> 12) // 分页后的页数
#define MAP_NR(addr) (((addr) - LOW_MEM) >> 12) // 计算当前物理地址的对应页号
#define USED 100 // 占用状态, 为什么是100呢, 我不是很理解

// 该宏用于复制一页物理内存从 from 到 to
#define copy_page(from, to) \
    __asm__ volatile ("cld; rep; movsl;":"S" (from), "D" (to), "c" (1024))

// 用于使TLB失效, 刷新缓存
#define invalidate() \
    __asm__ volatile("mov %%eax, %%cr3"::"a" (0))

static unsigned long HIGH_MEMORY = 0;
void un_wp_page(unsigned long * table_entry);

// 最基本的, linux0.11 使用内存字节位图来管理物理页的状态, 声明时全部填零
// 每一个字节表示这个物理页面被共享次数
static unsigned char mem_map[ PAGING_PAGES ] = {0,};

// 在当前状态因为没有进程管理, 如果内存超限直接panic
static inline void oom() {
    panic("Out of Memory!! QWQ\n");
}

// 此函数对物理页映像进行初始化(mem_map),将内核使用的部分内存映像初始设置为占用
// 将从start_mem -> end_mem 处的内存初始化为没有被使用 ( 0 - 1MB 空间不在考虑之内)
// >>12 = /4KB(一页的大小)
//
void mem_init(unsigned long start_mem, unsigned long end_mem) {
    unsigned long i;
    HIGH_MEMORY = end_mem;      // 物理内存最高处为 end_mem
    for(i = 0; i < PAGING_PAGES; i++)
        mem_map[i] = USED;      // 除了start_mem ~ end_mem 的区域物理页都应该为被占用状态

    i = (unsigned long)MAP_NR(start_mem);
    end_mem -= start_mem;
    end_mem >>= 12;              // 计算多少页需要设置为free
    while(end_mem-->0) {
        mem_map[i++] = 0;
    }
    return ;
}

// 仅仅用来显示当前 memory 用量的一个小函数
void calc_mem(void) {
    int i, j, k, free = 0;
    long *pg_tbl;

    for(i = 0; i < PAGING_PAGES; i++)
        if(!mem_map[i]) free++;
    printk("%d pages free (of %d in total)\n", free, PAGING_PAGES);
    
    // 遍历除了页表页目录的其余页表项, 如果页面有效, 则统计有效页面数量
    for(i = 2; i < 1024; i++) {
        if(pg_dir[i] & 1) {     // 先检查 Dir 是否存在
            pg_tbl = (long *)(0xfffff000 & pg_dir[i]);  // 计算 pg_tbl 的地址
            for(j = k = 0; j < 1024; j++) {
                if(pg_tbl[j] & 1) {     // 检查 Entry 是否存在
                    k++;
                }
            }
            printk("PageDir[%d] uses %d pages\n", i, k);
        } 
    }
    return ;
}

// 此函数用于获取第一个(按照顺序来说是最后一个)空闲的内存物理页,
// 这个函数从mem_map的末尾开始遍历,直到遇到某个物理页映像为没有被占用状态,这时计算出该页的物理地址
// 初始化该页内容为0, 并返回页起始地址
// 如果不存在可以用的页则返回 0
unsigned long get_free_page(void) {
   register unsigned long __res asm("ax");

   __asm__ volatile ("std; repne; scasb\n\t"
                "jne 1f\n\t"
                "movb $1, 1(%%edi)\n\t"
                "sall $12, %%ecx\n\t"
                "addl %2, %%ecx\n\t"
                "movl %%ecx, %%edx\n\t"
                "movl $1024, %%ecx\n\t"
                "leal 4092(%%edx), %%edi\n\t"
                "rep; stosl;\n\t"
                "movl %%edx, %%eax\n"
                "1: cld"
                : "=a" (__res)
                : "0" (0), "i" (LOW_MEM), "c" (PAGING_PAGES), "D" (mem_map + PAGING_PAGES - 1));    // 从尾端开始检查是否有可用的物理页
   return __res;
}

// 释放一页物理页
// 就是将mem_map中相应的byte置0,以及做一些必要的error_check
// 包括是否访问了内核内存,还有是否超出了物理内存(16MB)边界
void free_page(unsigned long addr) {
    if(addr < LOW_MEM) return ;      // 决不允许操作物理内存低端 
    if(addr >= HIGH_MEMORY) return ; // 也不能超过可用内存高端

    addr = MAP_NR(addr);        // 计算出需要的页号
    if(mem_map[addr]--) return;     // 如果该页为被使用状态那么减少引用计数并返回
    mem_map[addr] = 0;
    panic("Trying to free free page!");  // 不应该出现这种情况 因而引发panic
}

// 释放页表, 以及相应对应的物理页释放,并释放该页目录项占用的物理页
int free_page_tables(unsigned long from, unsigned long size) {
    unsigned long *pg_tbl;
    unsigned long *pg_dir, nr;

    // 检查是否为 4MB 内存边界(该函数仅仅处理连续的4MB内存块)
    if(from & 0x3fffff)
        panic("free_page_tables called with wrong alignment");
    // 如果要释放低 4MB 内存,也会引发错误(也就是from == 0)
    if(!from)
        panic("try to free up swapper memory space");
    size = (size + 0x3fffff) >> 22;             // 计算占的目录项数
    // 计算目录项地址
    pg_dir = (unsigned long *)((from >> 20) & 0xffc);
    // 下面开始释放, 首先释放页表项, 后释放页目录项
    // 注意释放的是连续的目录/页表项, 因而 pg_dir 自增即可
    for(; size-->0; pg_dir++) {
         if(!(*pg_dir & 1)) // 说明该目录项没有被使用
             continue;
         pg_tbl = (unsigned long *)(*pg_dir & 0xfffff000);      // 从页目录项中取出页表项地址
         for(nr = 0; nr < 1024; nr++) { // 开始释放页表项
             if(*pg_tbl & 1)        // 此页存在(P = 1)
                 free_page(0xfffff000 & *pg_tbl);   // 释放此页
             *pg_tbl = 0;
             pg_tbl++;
         }
         // 然后释放掉这个页目录所占的页
         free_page(0xfffff000 & *pg_dir);
         *pg_dir = 0;
    }
    invalidate();
    return 0;
}

// 用来将一页内存放入页表(和页目录), 即将相应的页表,页目录项填充,
// 如果要put的物理页在内存中还是unset状态(mem_map[xxx] = 0),那么先去获取一个可用的物理页
// 此函数假定了 pg_dir = 0 (hardcode)
// 成功时返回该页的物理地址, 失败/OOM 时返回0
// 参数 page 为页面的物理地址 address 为线性地址
unsigned long put_page(unsigned long page, unsigned long address) {
    unsigned long *pg_tbl, tmp;
    
    if(page < LOW_MEM || page >= (unsigned long)HIGH_MEMORY)
        printk("Trying to put page %x at %x\n", page, address);
    // mem_map 中此页为unset状态
    if(mem_map[MAP_NR(page)] != 1)
        printk("mem_map disagrees with %x at %x\n", page, address);
    // 计算该线性地址对应的页目录地址
    pg_tbl = (unsigned long *) ((address >> 20) & 0xffc);
    // 如果页目录存在则直接取出 pg_tbl
    // printk("Params: pg_tbl = %x, entry = %x\n", pg_tbl, (address >> 12) & 0x3ff);
    if(*pg_tbl & 1) {
        // printk("Page table now available\n");
        pg_tbl = (unsigned long *) (*pg_tbl & 0xfffff000);
    }
    // 否则申请物理页放置页目录
    else {
        if(!(tmp = get_free_page())) {
            printk("NO FREE PAGE!");
            return 0;
        }
        *pg_tbl = tmp | 7;
        // printk("Tmp = %x\n", tmp);
        // printk("Page Table = %x\n", *pg_tbl);
        pg_tbl = (unsigned long *) tmp;
    }
    // printk("Put Page Success\n");
    pg_tbl[(address >> 12) & 0x3ff] = page | 7;
    //invalidate();
    return page;
}

// 为线性地址 address 申请一个空物理页
// 失败则返回异常
void get_empty_page(unsigned long address) {
    unsigned long tmp;
    if(!(tmp = get_free_page()) || !put_page(tmp, address)) {
        free_page(tmp);
        oom();
    }
    return ;
}

// 写页面验证, 即验证目前要操作的页面(线性地址)是否合法以及可以被写入
// 如果页面存在但是不可写入, 则会尝试解除页面写入保护 (un_wp_page())
void write_verify(unsigned long address) {
    unsigned long page;
    // 检查页目录项是否存在
    if(!( (page = *((unsigned long *)((address >> 20) & 0xffc))) & 1)) {
        return ;
    }
    // 取页表首地址
    page = page & 0xfffff000;
    page += ((address >> 10) & 0xffc);
    if((*(unsigned long *)page & 3) == 1) { // 页表P = 1, R/W = 0
        un_wp_page((unsigned long *)page);
    }
    return ;
}

// 解除页面的写入保护
// 解除write protect,具体两个情况, 如果这个页面在 1MB 以上空间
// 并且存在,那么就直接在FLAG上添加 W FLAG 并且刷新TLB, 否则的话申请一个新的页面
// 并复制老页面的内容到新页面(Copy On Write)
//
void un_wp_page(unsigned long * table_entry) {
    s_printk("[DEBUG] un_wp_page(0x%x)\n", table_entry);
    unsigned long old_page, new_page;
    old_page = *table_entry & 0xfffff000;
    s_printk("[DEBUG] old_page = 0x%x\n", old_page);
    // 页面存在且位于1MB以上
    if(old_page >= LOW_MEM && mem_map[MAP_NR(old_page)] == 1) {
        s_printk("[DEBUG] Above 1MB\n");
        *table_entry |= 2;
        invalidate();
        return ;
    }
    // 无法分配新的页面
    if(!(new_page = (unsigned long)get_free_page())) // FUCK, I FORGET TO ADD "()" to the function, stuck for nearly two weeks
        oom();
    // 页面被共享, 因为要进行 COW 之后该页面就是独立的了,所以引用计数 -1
    if(old_page >= LOW_MEM)
        mem_map[MAP_NR(old_page)]--;
    *table_entry = new_page | 7;
    invalidate();
    return ;
}

// 缺页异常会调用此函数
void do_wp_page(unsigned long error_code, unsigned long address) {
    s_printk("[DEBUG] Page Fault(Write) at [%x], errono %d\n", address, error_code);
    error_code = error_code; // 纯粹为了消除警告
    // Checked the address caculate correct
    // IN 0x1000 OUT: 0x1004
    un_wp_page((unsigned long *)
            (((address>>10)&0xffc) + (0xfffff000 & 
                *((unsigned long *)((address >> 20) & 0xffc)))));
    mm_print_pageinfo(address);
}

// 缺页异常会调用此函数
// 缺页异常会调用的主要处理函数, (有进程管理相关代码, 暂时略),  如果当前进程executable是空的
// 或者地址已超出进程数据,则尝试取一个空页面并返回, 如果失败
// 则尝试共享页面,如果还失败,则尝试取一个新的页面(不在内存中的),再失败则报错OOM
// 参数 error_code, 错误号; address 线性地址
void do_no_page(unsigned long error_code, unsigned long address) {
    //unsigned long tmp;
    unsigned long page;

    s_printk("[DEBUG] Page Fault at [%x], errono %d\n", address, error_code);
    address &= 0xfffff000;
    if(!(page = get_free_page()))
        oom();
    if(put_page(page, address)) {
        mm_print_pageinfo(address);
        return ;
    }
    free_page(page);
    oom();
}

// 下面的两个函数是多进程需要使用到的,copy_page_tables 和其姊妹函数
// free_page_tables

// copy_page_table 用来拷贝线性地址连续一页的空间到另一个线性地址,
// 做法为仅仅拷贝其页表项内容,不拷贝其物理内存内容
// 同时当from = 0时表示从内核拷贝,这时我们不需要拷贝 4MB ,仅仅拷贝 640 KB
int copy_page_tables(unsigned long from, unsigned long to, unsigned long size) {
    s_printk("[DEBUG] copy_page_tables(0x%x, 0x%x, 0x%x)\n", from, to, size);
    unsigned long *from_page_table;
    unsigned long *to_page_table;
    unsigned long this_page;
    unsigned long *from_dir, *to_dir;
    unsigned long nr;

    // 检查内存边界,必须是4MB的整数倍,否则 panic 
    if((from & 0x3fffff) || (to & 0x3fffff)) {
        panic("copy_page_tables called with wrong alignment");
    }
    // 获取线性地址对应的页目录项
    from_dir = 0 + (unsigned long *)((from >> 20) & 0xffc);
    to_dir = 0 + (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 exists");
        // from_dir 不存在,就跳过这页页表的复制
        if(!(1 & *from_dir))
            continue;
        from_page_table = (unsigned long *)(0xfffff000 & *from_dir); // I forget to fetch the from_page_table!! QAQ
        // s_printk("from_page_table: addr=0x%x\n", (unsigned long)(0xfffff000 & (unsigned long)*from_page_table));
        if(!(to_page_table = (unsigned long *) get_free_page()))
            return -1;
        // s_printk("to_page_table = 0x%x\n", (unsigned long)(0xfffff000 & (unsigned long)*to_page_table));
        *to_dir = ((unsigned long)to_page_table | 7);
        // 判断是不是复制内核页,如果是则只copy 640KB(0xA0=160个页面)
        nr = (from == 0)?0xA0:1024;
        for(; nr-->0; from_page_table++, to_page_table++) {
            this_page = *from_page_table;
            // s_printk("this_page_before: addr=0x%x\n", (unsigned long)(0xfffff000 & (unsigned long)this_page));
            if(!(1 & this_page))
                continue;
            // 这里将复制的页设置为只读
            this_page &= (unsigned long)~2;
            // s_printk("this_page_after: addr=0x%x\n", (unsigned long)(0xfffff000 & (unsigned long)this_page));
            *to_page_table = this_page;
            // 如果复制的页面地址为高于LOW_MEM,说明不是从内核空间复制
            // 同时把原页面也设置为只读,这样二者共享了物理页面,如果向任何一个页面写入都会
            // 引发页保护异常(Page Fault),触发写时复制(Copy On Write COW)
            if(this_page > LOW_MEM) {
                *from_page_table = this_page;
                mem_map[MAP_NR(this_page)]++;
            }
        }
    }
    invalidate();
    return 0;
}


================================================
FILE: mm/mm_test.c
================================================
/*
 * File to show mm functional and test mm
 *
 */

#include <linux/kernel.h>
#include <linux/mm.h>
#include <serial_debug.h>

#define invalidate() \
    __asm__ volatile("mov %%eax, %%cr3"::"a" (0))

unsigned long put_page(unsigned long page, unsigned long address);

void testoom() {
    for(int i = 0; i < 20 * 1024 * 1024; i += 4096)
        do_no_page(0, (unsigned long)i);
    // This should not return!
    return ;
}

// This function is currently buggy
// Cannot use
// void test_share_page() {
//     // Copy a page then try to write to it
//     copy_page_tables(0x0, 0x4000000, 0xA0000);
//     mm_print_pageinfo(0x4000000);
//     // write to the page at 0x4000000
//     char *w = 0x4000000;
//     *w = 'p';
//     mm_print_pageinfo(0x4000000);
//     while (1);
// }

void test_put_page() {
    char *b = (char *)0x100000;
    calc_mem();
    //put_page(0x200000, 0x100000);
    calc_mem();
    *b = 'k';
    while(1);
    return ;
}

// Helper function to convert linear address to PTE
// return a pointer to a page table entry on success
// return NULL(0) on failed
unsigned long *linear_to_pte(unsigned long addr) {
    // get the Page Directory Entry first
    // This variable will be used to refer to pte later
    // (for saving memory XD)
    unsigned long *pde = (unsigned long *)((addr >> 20) & 0xffc);
    // Page dir not exist
    // Or the address is not inside the page table address range(<=4KB)
    if(!(*pde & 1) || (unsigned long)pde > 0x1000) {
        return 0;
    }
    // Now it is page table address :P
    pde = (unsigned long *)(*pde & 0xfffff000);
    // Page table address + page_table index = PTE
    //
    // Remember: x >> 12 & 0x3ff != x >> 10 & 0xffc (后者比前者二进制末尾多了两个0)
    // s_printk("pde = %d\n", (unsigned long)pde + 4);
    // s_printk("pte.offset = %d\n", (unsigned long)pde + (((addr >> 12) & 0x3ff) << 2));
    return (unsigned long *)((unsigned long)(pde) + (((addr >> 12) & 0x3ff) << 2));
}

void disable_linear(unsigned long addr) {
    // Your code here
    // Hint: Modify the correct page table entry
    // then you can make the page Not Present

    // Maybe you need a helper function for get
    // the page table entry for a specific address

    unsigned long *pte = linear_to_pte(addr);
    // Disable it
    *pte = 0;
    // Then RELOAD the TLB
    invalidate();
    return ;
}

void mm_read_only(unsigned long addr) {
    unsigned long *pte = linear_to_pte(addr);
    printk("Before: 0x%x\n", *pte);
    *pte = *pte & 0xfffffffd;
    printk("After: 0x%x\n", *pte);
    invalidate();
    return ;
}

void mm_print_pageinfo(unsigned long addr) {
    unsigned long *pte = linear_to_pte(addr);
    s_printk("[DEBUG] Linear addr: 0x%x, PTE addr = 0x%x. Flags[ ", addr, pte);
    if(*pte & 0x1) s_printk("P ");
    if(*pte & 0x2) s_printk("R/W ");
    else s_printk("RO ");
    if(*pte & 0x4) s_printk("U/S ");
    else s_printk("S ");
    s_printk("]\n");
    s_printk("[DEBUG] Phyaddr = %x\n", (*pte & 0xfffff000));
}


int mmtest_main(void) {
    // test_share_page();
    // return 0;
    s_printk("0x%x\n", linear_to_pte(0x1000));
    printk("Running Memory function tests\n");
    printk("1. Make Linear Address 0xdad233 unavailable\n");

    disable_linear(0xdad233);

    // Now don't modify the code above
    // make the linear address AT 0xdad233 accessible
    // and store 0x23333333 at 3MB physical memory
    // (Hint: add a table entry map to physical address 3MB)
    //
    printk("2. Put page(0x300000) at linear address 0xdad233\n");
    put_page(0x300000, 0xdad233);

    unsigned long *x = (unsigned long *)0xdad233;
    *x = 0x23333333;
    printk("X = %x\n", *x);

    // Then make the linear address 0xdad233 Read Only

    printk("3. Make 0xdad233 READ ONLY\n");
    mm_read_only(0xdad233);
    x = (unsigned long *)0xdad233;
    // DO not modify this code
    // Here will make WP bit enabled (temporarily)
    asm volatile("mov %%cr0, %%eax\n\t"
            "orl $0x00010000, %%eax\n\t"
            "mov %%eax, %%cr0\n\t"
            ::);
    // Please write a GCC Extended assembly here
    // to modify the content at linear address 0xdad233
    // (This will trigger a page write protect fault), uncomment lines below to test
    //asm volatile("mov %%eax, (%%edi)"
    //        ::"D" (x), "a" (0x233111));

    printk("4. Print the page info of 0xdad233 in human readable mode\n");
    mm_print_pageinfo(0xdad233);

    while(1);
    // Please implement
}


================================================
FILE: mm/page.s
================================================
.code32

.global page_fault

page_fault:
	xchgl %eax, (%esp)		# 错误码入eax, 同时eax压栈
	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
Download .txt
gitextract_l9h0t38n/

├── .gitignore
├── Makefile
├── Makefile.header
├── bochsrc
├── boot/
│   ├── Makefile
│   ├── binary.s
│   ├── bochsrc
│   ├── bootsect.s
│   ├── head.s
│   ├── ldS.ld
│   └── setup.s
├── docs/
│   └── EXPERIMENT.md
├── include/
│   ├── asm/
│   │   ├── io.h
│   │   ├── segment.h
│   │   └── system.h
│   ├── errno.h
│   ├── linux/
│   │   ├── fs.h
│   │   ├── head.h
│   │   ├── kernel.h
│   │   ├── lib.h
│   │   ├── mm.h
│   │   ├── sched.h
│   │   ├── sys.h
│   │   └── tty.h
│   ├── serial_debug.h
│   ├── signal.h
│   ├── stdarg.h
│   ├── stddef.h
│   ├── sys/
│   │   ├── types.h
│   │   └── wait.h
│   └── unistd.h
├── init/
│   ├── Makefile
│   └── main.c
├── kernel/
│   ├── Makefile
│   ├── asm.s
│   ├── blk_drv/
│   │   ├── Makefile
│   │   ├── blk.h
│   │   └── request_scan_algo.c
│   ├── chr_drv/
│   │   ├── Makefile
│   │   ├── do_keyboard.c
│   │   ├── keyboard.s
│   │   ├── tty_io.c
│   │   ├── tty_io.c.orig
│   │   ├── tty_io.c.rej
│   │   ├── tty_queue.c
│   │   ├── tty_read.c
│   │   └── vga_console.c
│   ├── exit.c
│   ├── fork.c
│   ├── libc_restore.s
│   ├── panic.c
│   ├── printk.c
│   ├── sched.c
│   ├── serial_debug.c
│   ├── signal.c
│   ├── signal_demo.c
│   ├── sys.c
│   ├── system_call.s
│   ├── traps.c
│   ├── vsprintf.c
│   └── vsprintf.c.old
├── ldS.ld
├── lib/
│   ├── Makefile
│   ├── getline.c
│   └── printf.c
└── mm/
    ├── Makefile
    ├── memory.c
    ├── mm_test.c
    └── page.s
Download .txt
SYMBOL INDEX (189 symbols across 30 files)

FILE: include/asm/segment.h
  function get_fs_byte (line 1) | static inline char get_fs_byte(const char *addr) {
  function get_fs_word (line 7) | static inline unsigned short get_fs_word(const char *addr) {
  function get_fs_long (line 13) | static inline unsigned long get_fs_long(const char *addr) {
  function put_fs_byte (line 19) | static inline void put_fs_byte(char val, char *addr) {
  function put_fs_word (line 23) | static inline void put_fs_word(short val, short *addr) {
  function put_fs_long (line 27) | static inline void put_fs_long(unsigned long val, unsigned long *addr) {

FILE: include/linux/fs.h
  type buffer_head (line 11) | struct buffer_head {

FILE: include/linux/head.h
  type desc_struct (line 4) | struct desc_struct {

FILE: include/linux/sched.h
  type i387_struct (line 35) | struct i387_struct {
  type tss_struct (line 48) | struct tss_struct {
  type task_struct (line 76) | struct task_struct {
  type task_struct (line 149) | struct task_struct
  type task_struct (line 150) | struct task_struct
  type task_struct (line 157) | struct task_struct
  type task_struct (line 158) | struct task_struct
  type task_struct (line 159) | struct task_struct
  type task_struct (line 160) | struct task_struct
  function _get_base (line 228) | static inline unsigned long _get_base(char *addr) {

FILE: include/linux/sys.h
  type sigaction (line 11) | struct sigaction
  type sigaction (line 11) | struct sigaction

FILE: include/linux/tty.h
  type tty_queue (line 11) | struct tty_queue {
  type tty_struct (line 18) | struct tty_struct {
  type tty_struct (line 27) | struct tty_struct
  type tty_queue (line 28) | struct tty_queue
  type tty_queue (line 29) | struct tty_queue
  type tty_queue (line 30) | struct tty_queue
  type tty_queue (line 31) | struct tty_queue
  type tty_queue (line 32) | struct tty_queue
  type tty_queue (line 33) | struct tty_queue
  type tty_queue (line 34) | struct tty_queue
  type tty_queue (line 35) | struct tty_queue
  type tty_struct (line 36) | struct tty_struct
  type tty_struct (line 38) | struct tty_struct

FILE: include/signal.h
  type sig_atomic_t (line 6) | typedef int sig_atomic_t;
  type sigset_t (line 7) | typedef unsigned int sigset_t;
  type sigset_t (line 53) | typedef unsigned int sigset_t;
  type sigaction (line 55) | struct sigaction {

FILE: include/sys/types.h
  type time_t (line 11) | typedef long time_t;
  type pid_t (line 23) | typedef int pid_t;
  type uid_t (line 24) | typedef unsigned short uid_t;
  type gid_t (line 25) | typedef unsigned char gid_t;
  type dev_t (line 26) | typedef unsigned short dev_t;
  type ino_t (line 27) | typedef unsigned short ino_t;
  type mode_t (line 28) | typedef unsigned short mode_t;
  type umode_t (line 29) | typedef unsigned short umode_t;
  type nlink_t (line 30) | typedef unsigned char nlink_t;
  type daddr_t (line 31) | typedef int daddr_t;
  type off_t (line 32) | typedef long off_t;
  type u_char (line 33) | typedef unsigned char u_char;
  type ushort (line 34) | typedef unsigned short ushort;
  type div_t (line 36) | typedef struct { int quot,rem; } div_t;
  type ldiv_t (line 37) | typedef struct { long quot,rem; } ldiv_t;
  type ustat (line 39) | struct ustat {

FILE: init/main.c
  function _syscall1 (line 30) | static inline _syscall0(int,fork)
  function main (line 63) | int main() {
  function sched_abcd_demo (line 91) | void sched_abcd_demo() {

FILE: kernel/blk_drv/blk.h
  type request (line 12) | struct request {
  type blk_dev_struct (line 24) | struct blk_dev_struct {

FILE: kernel/blk_drv/request_scan_algo.c
  type request (line 13) | struct request
  type task_struct (line 14) | struct task_struct
  type blk_dev_struct (line 16) | struct blk_dev_struct
  function add_request (line 26) | static void add_request(struct blk_dev_struct * dev, struct request * re...
  function lock_buffer (line 56) | static inline void lock_buffer(struct buffer_head *bh) {
  function unlock_buffer (line 65) | static inline void unlock_buffer(struct buffer_head *bh) {
  function make_request (line 73) | static void make_request(int major, int rw, struct buffer_head *bh) {
  function ll_rw_block (line 139) | void ll_rw_block(int rw, struct buffer_head *bh) {
  function blk_dev_init (line 150) | void blk_dev_init(void) {

FILE: kernel/chr_drv/do_keyboard.c
  function toupper (line 26) | char toupper(char ch) {
  function do_keyboard_interrupt (line 33) | void do_keyboard_interrupt(short scancode) {

FILE: kernel/chr_drv/tty_io.c
  type tty_struct (line 13) | struct tty_struct
  type tty_struct (line 16) | struct tty_struct
  function tty_init (line 37) | void tty_init(void) {
  function copy_to_buffer (line 44) | void copy_to_buffer(struct tty_struct *tty) {
  function tty_write (line 88) | void tty_write(struct tty_struct* tty) {
  function sleep_if_empty (line 95) | void sleep_if_empty(struct tty_queue *q) {
  function sleep_if_full (line 102) | static void sleep_if_full(struct tty_queue *q) {
  function tty_read (line 112) | int tty_read(int channel, char *buf, int nr) {
  function _user_tty_write (line 185) | int _user_tty_write(int channel, char *buf, int nr) {

FILE: kernel/chr_drv/tty_queue.c
  function tty_isempty_q (line 10) | int tty_isempty_q(const struct tty_queue *q) {
  function tty_isfull_q (line 16) | int tty_isfull_q(const struct tty_queue *q) {
  function tty_pop_q (line 23) | char tty_pop_q(struct tty_queue *q) {
  function tty_push_q (line 32) | int tty_push_q(struct tty_queue *q, char ch) {
  function tty_push_q_front (line 40) | int tty_push_q_front(struct tty_queue *q, char ch) {
  function tty_queue_head (line 48) | char tty_queue_head(const struct tty_queue *q) {
  function tty_queue_tail (line 52) | char tty_queue_tail(const struct tty_queue *q) {
  function tty_queue_stat (line 56) | void tty_queue_stat(const struct tty_queue *q) {

FILE: kernel/chr_drv/vga_console.c
  type video_info (line 28) | struct video_info {
  function video_init (line 39) | void video_init() {
  function video_getx (line 48) | int video_getx() {
  function video_gety (line 52) | int video_gety() {
  function update_cursor (line 56) | void update_cursor(int row, int col) {
  function get_cursor (line 67) | int get_cursor() {
  function video_putchar (line 76) | void video_putchar(char ch) {
  function con_write (line 113) | void con_write(struct tty_struct *tty) {
  function video_clear (line 125) | void video_clear() {
  function video_putchar_at (line 138) | void video_putchar_at(char ch, int x, int y, char attr) {
  function roll_screen (line 148) | void roll_screen() {
  function memcpy (line 161) | void memcpy(char *dest, char *src, int count, int size) {
  function con_init (line 172) | void con_init(void) {

FILE: kernel/exit.c
  type task_struct (line 10) | struct task_struct
  function sys_kill (line 15) | int sys_kill(int pid, int sig) {
  function send_sig (line 37) | static inline int send_sig(long sig, struct task_struct* p, int priv) {
  function release (line 50) | void release(struct task_struct *p) {
  function do_exit (line 68) | int do_exit(long code) {
  function pid_t (line 102) | pid_t sys_exit(int error_code) {
  function pid_t (line 108) | pid_t sys_waitpid(pid_t pid, unsigned long stat_addr, int options) {
  function tell_father (line 185) | static void tell_father(int pid) {

FILE: kernel/fork.c
  function verify_area (line 14) | void verify_area(void *addr, unsigned int size) {
  function copy_mem (line 28) | int copy_mem(int nr, struct task_struct *p) {
  function copy_process (line 61) | int copy_process(int nr, long ebp, long edi, long esi,
  function find_empty_process (line 131) | int find_empty_process(void) {

FILE: kernel/panic.c
  function panic (line 10) | void panic(const char *str) {

FILE: kernel/printk.c
  function printk (line 14) | void printk(char *fmt, ...) {

FILE: kernel/sched.c
  type task_struct (line 18) | struct task_struct
  type task_struct (line 26) | struct task_struct
  type task_struct (line 27) | struct task_struct
  type task_struct (line 28) | struct task_struct
  function sleep_on (line 35) | void sleep_on(struct task_struct **p) {
  function schedule (line 51) | void schedule(void) {
  function show_task_info (line 116) | void show_task_info(struct task_struct *task) {
  function wake_up (line 128) | void wake_up(struct task_struct **p) {
  function interruptible_sleep_on (line 136) | void interruptible_sleep_on(struct task_struct **p) {
  function sys_pause (line 160) | int sys_pause(void) {
  function sys_alarm (line 166) | int sys_alarm(long seconds) {
  function do_timer (line 183) | void do_timer(long cpl) {
  function sched_init (line 196) | void sched_init() {

FILE: kernel/serial_debug.c
  function is_transmit_empty (line 5) | int is_transmit_empty() {
  function s_putchar (line 9) | void s_putchar(char a) {
  function s_puts (line 15) | void s_puts(char *a) {
  function serial_debugstr (line 21) | int serial_debugstr(char *str) {
  function s_printnum (line 26) | void s_printnum(int num, int base, int sign) {
  function s_printk (line 54) | void s_printk(char *fmt, ...) {

FILE: kernel/signal.c
  function dump_sigaction (line 12) | void dump_sigaction(struct sigaction *action) {
  function do_signal (line 18) | void do_signal(long signr, long eax, long ebx, long ecx,
  function sys_sgetmask (line 74) | int sys_sgetmask() {
  function sys_ssetmask (line 78) | int sys_ssetmask(int newmask) {
  function save_old (line 88) | static inline void save_old(char *from, char *to) {
  function get_new (line 98) | static inline void get_new(char *from, char *to) {
  function sys_signal (line 108) | int sys_signal(int signum, long handler, long restorer) {
  function sys_sigaction (line 123) | int sys_sigaction(int signum, const struct sigaction* action,

FILE: kernel/signal_demo.c
  type sigaction (line 12) | struct sigaction
  type sigaction (line 13) | struct sigaction
  function demo_handle (line 21) | static void demo_handle(int sig) {
  function signal_demo_main (line 27) | void signal_demo_main(void) {

FILE: kernel/sys.c
  function stub_syscall (line 10) | int stub_syscall(void) {
  function sys_sleep (line 15) | int sys_sleep(long seconds) {

FILE: kernel/traps.c
  function die (line 40) | static void die(char *str, long esp_ptr, long nr) {
  function do_double_fault (line 53) | void do_double_fault(long esp, long error_code) {
  function do_general_protection (line 57) | void do_general_protection(long esp, long error_code) {
  function do_divide_error (line 61) | void do_divide_error(long esp, long error_code) {
  function do_int3 (line 65) | void do_int3(long *esp, long error_code,
  function do_nmi (line 82) | void do_nmi(long esp, long error_code) {
  function do_debug (line 86) | void do_debug(long esp, long error_code) {
  function do_overflow (line 90) | void do_overflow(long esp, long error_code) {
  function do_bounds (line 94) | void do_bounds(long esp, long error_code) {
  function do_invalid_op (line 98) | void do_invalid_op(long esp, long error_code) {
  function do_device_not_available (line 102) | void do_device_not_available(long esp, long error_code) {
  function do_coprocessor_segment_overrun (line 106) | void do_coprocessor_segment_overrun(long esp, long error_code) {
  function do_invalid_TSS (line 110) | void do_invalid_TSS(long esp, long error_code) {
  function do_segment_not_present (line 114) | void do_segment_not_present(long esp, long error_code) {
  function do_stack_segment (line 118) | void do_stack_segment(long esp, long error_code) {
  function do_coprocessor_error (line 122) | void do_coprocessor_error(long esp, long error_code) {
  function do_reserved (line 126) | void do_reserved(long esp, long error_code) {
  function do_stub (line 130) | void do_stub(long esp, long error_code) {
  function trap_init (line 134) | void trap_init(void) {

FILE: kernel/vsprintf.c
  function vsprintf (line 9) | void vsprintf(char *dest, char *fmt, va_list ap) {
  function _sprintnum (line 49) | int _sprintnum(char *dest, int num, int base, int sign) {

FILE: lib/getline.c
  function getchar (line 8) | char getchar() {
  function getline (line 14) | int getline(char *str) {

FILE: lib/printf.c
  function printf (line 16) | static inline _syscall1(int, sys_debug, char *, str)

FILE: mm/memory.c
  function oom (line 36) | static inline void oom() {
  function mem_init (line 44) | void mem_init(unsigned long start_mem, unsigned long end_mem) {
  function calc_mem (line 60) | void calc_mem(void) {
  function get_free_page (line 87) | unsigned long get_free_page(void) {
  function free_page (line 109) | void free_page(unsigned long addr) {
  function free_page_tables (line 120) | int free_page_tables(unsigned long from, unsigned long size) {
  function put_page (line 158) | unsigned long put_page(unsigned long page, unsigned long address) {
  function get_empty_page (line 193) | void get_empty_page(unsigned long address) {
  function write_verify (line 204) | void write_verify(unsigned long address) {
  function un_wp_page (line 224) | void un_wp_page(unsigned long * table_entry) {
  function do_wp_page (line 248) | void do_wp_page(unsigned long error_code, unsigned long address) {
  function do_no_page (line 264) | void do_no_page(unsigned long error_code, unsigned long address) {
  function copy_page_tables (line 286) | int copy_page_tables(unsigned long from, unsigned long to, unsigned long...

FILE: mm/mm_test.c
  function testoom (line 15) | void testoom() {
  function test_put_page (line 35) | void test_put_page() {
  function disable_linear (line 68) | void disable_linear(unsigned long addr) {
  function mm_read_only (line 84) | void mm_read_only(unsigned long addr) {
  function mm_print_pageinfo (line 93) | void mm_print_pageinfo(unsigned long addr) {
  function mmtest_main (line 106) | int mmtest_main(void) {
Condensed preview — 69 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (157K chars).
[
  {
    "path": ".gitignore",
    "chars": 126,
    "preview": "*.o\n*.a\n*.sym\nboot/bootsect\nenv.sh\nboot/bootimg\nboot/setup\nboot/binary\n.workdoc/\n*.ini\ntags\nbootimg\nsystem\n.ycm_extra_co"
  },
  {
    "path": "Makefile",
    "chars": 1505,
    "preview": "include Makefile.header\n#AS=i686-elf-as\n#LD=i686-elf-ld\n#OBJCOPY=i686-elf-objcopy\n#QEMU=qemu-system-i386\n#BOCHS=bochs\n#S"
  },
  {
    "path": "Makefile.header",
    "chars": 244,
    "preview": "AS=as --32\nCC=gcc\nLD=ld -m elf_i386\nOBJCOPY=objcopy\nQEMU=qemu-system-i386\nBOCHS=bochs\nSTRIP=strip\n_ALLWARN=-Wall -Wextra"
  },
  {
    "path": "bochsrc",
    "chars": 209,
    "preview": "floppya: 1_44=\"./bootimg\", status=inserted  # Use Floppy Disk A for booting\nboot: floppy\ndisplay_library: x, options=\"gu"
  },
  {
    "path": "boot/Makefile",
    "chars": 1210,
    "preview": "include ../Makefile.header\n\nLDFLAGS\t+= -Ttext 0\n\n.PHONY=clean run all\n\nall: bootsect setup head.o\n\n# Bootsector is RAW B"
  },
  {
    "path": "boot/binary.s",
    "chars": 735,
    "preview": "# This code is used to test whether you make things right \n# switching to protected mode, do not modify it, it will work"
  },
  {
    "path": "boot/bochsrc",
    "chars": 177,
    "preview": "floppya: 1_44=\"./bootimg\", status=inserted  # Use Floppy Disk A for booting\nboot: floppy\ndisplay_library: x, options=\"gu"
  },
  {
    "path": "boot/bootsect.s",
    "chars": 7146,
    "preview": "#################################################################\n#                                                     "
  },
  {
    "path": "boot/head.s",
    "chars": 4033,
    "preview": "#######################################################\n#    Lesson 5: 设置最初的页表,进行系统初始化\t\t  #\n#    这里要注意,页表我们设置在了0x0000000"
  },
  {
    "path": "boot/ldS.ld",
    "chars": 83,
    "preview": "SECTIONS {\n    .text 0x0000 : {\n        *(.text)\n    }\n\n    /DISCARD/ : {\n\n    }\n}\n"
  },
  {
    "path": "boot/setup.s",
    "chars": 5140,
    "preview": "###########################################################\n#\t\t\t\t\t\t\t\t\t\t\t\t\t\t  #\n#\t\tLesson 4 设置IDT 对8259A进行编程    \t\t\t  #\n#\t"
  },
  {
    "path": "docs/EXPERIMENT.md",
    "chars": 1318,
    "preview": "## 0 环境 SSR\n\n* 搭建环境 ( Virtualbox 跑通 / 自己搭建 )\n* 熟悉环境(GCC, GNU Toolchain, Objdump, dd, Makefile, Bochs, QEMU)\n\n## 1 启动\n* 启"
  },
  {
    "path": "include/asm/io.h",
    "chars": 612,
    "preview": "// 从Port读入数据到al, _v变量的值就是al的值\n#define inb(port) ({\\\nunsigned char _v; \\\n__asm__ volatile(\"inb %%dx, %%al\":\"=a\" (_v):\"d\" "
  },
  {
    "path": "include/asm/segment.h",
    "chars": 877,
    "preview": "static inline char get_fs_byte(const char *addr) {\n    register char _v;\n    __asm__ volatile(\"movb %%fs:%1, %0\":\"=r\" (_"
  },
  {
    "path": "include/asm/system.h",
    "chars": 1982,
    "preview": "/*\n * 定义了用来在C语言中直接调用的汇编宏函数\n *\n */\n\n#define sti()  __asm__ volatile (\"sti\"::)\n#define cli()  __asm__ volatile (\"cli\"::)\n#"
  },
  {
    "path": "include/errno.h",
    "chars": 827,
    "preview": "#ifndef _ERRNO_H\n#define _ERRNO_H\n\n// 错误号定义\n\nextern int errno;\n\n#define ERROR\t\t99\n#define EPERM\t\t 1\n#define ENOENT\t\t 2\n#"
  },
  {
    "path": "include/linux/fs.h",
    "chars": 700,
    "preview": "#ifndef _FS_H\n#define _FS_H\n\n#include <linux/sched.h>\n\n#define READ 0\n#define WRITE 1\n#define READA 2\n#define WRITEA 3\n\n"
  },
  {
    "path": "include/linux/head.h",
    "chars": 177,
    "preview": "#ifndef _HEAD_H\n#define _HEAD_H\n\ntypedef struct desc_struct {\n    unsigned long a, b;\n} desc_table[256];\n\nextern desc_ta"
  },
  {
    "path": "include/linux/kernel.h",
    "chars": 560,
    "preview": "#ifndef _KERNEL_H\n#define _KERNEL_H\n \nvoid printk(char *fmt, ...);     // Simplest printk function to use\nvoid video_put"
  },
  {
    "path": "include/linux/lib.h",
    "chars": 68,
    "preview": "char getchar();\nint getline(char *str);\nint printf(char *fmt, ...);\n"
  },
  {
    "path": "include/linux/mm.h",
    "chars": 462,
    "preview": "#ifndef _MM_H\n#define _MM_H\n\n#define PAGE_SIZE 4096\n\n/* extern */ unsigned long get_free_page(void);\n/* extern */ unsign"
  },
  {
    "path": "include/linux/sched.h",
    "chars": 7201,
    "preview": "#ifndef _SCHED_H\n#define _SCHED_H\n\n// 最多有 64 个进程(任务)同时处于系统中\n#define NR_TASKS 64\n// 时钟频率 100 hz\n#define HZ 100\n\n#define F"
  },
  {
    "path": "include/linux/sys.h",
    "chars": 2004,
    "preview": "#ifndef _SYS_H\n#define _SYS_H\n\n#include<linux/sched.h>\n\nextern int sys_fork();\nextern int sys_pause();\nextern int stub_s"
  },
  {
    "path": "include/linux/tty.h",
    "chars": 1198,
    "preview": "#ifndef _TTY_H\n#define _TTY_H\n\n#include <linux/sched.h>\n#define TTY_BUF_SIZE 1024\n\n#define TTY_ECHO 0x0001     // 回显标志\n\n"
  },
  {
    "path": "include/serial_debug.h",
    "chars": 207,
    "preview": "#ifndef _SERIAL_DEBUG_H\n#define _SERIAL_DEBUG_H\nextern void s_putchar(char a);\nextern void s_puts(char *s);\nextern void "
  },
  {
    "path": "include/signal.h",
    "chars": 1709,
    "preview": "#ifndef _SIGNAL_H\n#define _SIGNAL_H\n\n#include <sys/types.h>\n\ntypedef int sig_atomic_t;\ntypedef unsigned int sigset_t;\n\n#"
  },
  {
    "path": "include/stdarg.h",
    "chars": 780,
    "preview": "#ifndef _STDARG_H\n#define _STDARG_H\n\ntypedef char *va_list;\n\n/* Amount of space required in an argument list for an arg "
  },
  {
    "path": "include/stddef.h",
    "chars": 286,
    "preview": "#ifndef _STDDEF_H\n#define _STDDEF_H\n\n#ifndef _PTRDIFF_T\n#define _PTRDIFF_T\ntypedef long ptrdiff_t;\n#endif\n\n#ifndef _SIZE"
  },
  {
    "path": "include/sys/types.h",
    "chars": 806,
    "preview": "#ifndef _SYS_TYPES_H\n#define _SYS_TYPES_H\n\n#ifndef _SIZE_T\n#define _SIZE_T\ntypedef unsigned int size_t;\n#endif\n\n#ifndef "
  },
  {
    "path": "include/sys/wait.h",
    "chars": 560,
    "preview": "#ifndef _SYS_WAIT_H\n#define _SYS_WAIT_H\n\n#include <sys/types.h>\n\n#define _LOW(v)\t\t( (v) & 0377)\n#define _HIGH(v)\t( ((v) "
  },
  {
    "path": "include/unistd.h",
    "chars": 1671,
    "preview": "#ifndef _UNISTD_H\n#define _UNISTD_H\n\n#ifdef __LIBRARY__\n\n#define __NR_fork 2\n#define __NR_sleep 10\n#define __NR_pause 29"
  },
  {
    "path": "init/Makefile",
    "chars": 141,
    "preview": "include ../Makefile.header\n\nCFLAGS += -I../include\nLDFLAGS += -r\n\nall: main.o\n\n.c.o:\n\t@$(CC) $(CFLAGS) \\\n\t\t-c -o $*.o $<"
  },
  {
    "path": "init/main.c",
    "chars": 3085,
    "preview": "/*\n * Default Routine to run after head.s\n *\n */\n\n#define __LIBRARY__\n\n#include <linux/kernel.h>\n#include <linux/sched.h"
  },
  {
    "path": "kernel/Makefile",
    "chars": 413,
    "preview": "include ../Makefile.header\n\nOBJS = printk.o panic.o traps.o asm.o sched.o system_call.o sys.o fork.o \\\n\t   serial_debug."
  },
  {
    "path": "kernel/asm.s",
    "chars": 3963,
    "preview": ".code32\n\n# Code to implement low-level 80386 interrupt handler\n# We'll handle int0 - int16 here, some interrupts will\n# "
  },
  {
    "path": "kernel/blk_drv/Makefile",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "kernel/blk_drv/blk.h",
    "chars": 719,
    "preview": "/*\n * blk.h block driver commmon data structure and \n * macros, function prototypes\n */\n#ifndef _BLK_H\n#define _BLK_H\n\n#"
  },
  {
    "path": "kernel/blk_drv/request_scan_algo.c",
    "chars": 3593,
    "preview": "/*\n * block driver Request queue implementation\n * using SCAN algorithm\n */\n\n#include <linux/sched.h>\n#include <linux/ke"
  },
  {
    "path": "kernel/chr_drv/Makefile",
    "chars": 325,
    "preview": "include ../../Makefile.header\n\nOBJS = do_keyboard.o keyboard.o tty_io.o tty_queue.o vga_console.o \\\n\t   tty_read.o\n\nLDFL"
  },
  {
    "path": "kernel/chr_drv/do_keyboard.c",
    "chars": 3208,
    "preview": "#include <linux/kernel.h>\n#include <linux/head.h>\n#include <asm/system.h>\n#include <serial_debug.h>\n#include <linux/tty."
  },
  {
    "path": "kernel/chr_drv/keyboard.s",
    "chars": 527,
    "preview": "# Very Simple Keyboard Program\n# The whole char_drv is rewritten for teaching purpose\n# Get keyboard interrupt then call"
  },
  {
    "path": "kernel/chr_drv/tty_io.c",
    "chars": 5248,
    "preview": "#include <linux/kernel.h>\n#include <linux/head.h>\n#include <linux/sched.h>\n#include <asm/system.h>\n#include <asm/segment"
  },
  {
    "path": "kernel/chr_drv/tty_io.c.orig",
    "chars": 4134,
    "preview": "#include <linux/kernel.h>\n#include <linux/head.h>\n#include <linux/sched.h>\n#include <asm/system.h>\n#include <asm/segment"
  },
  {
    "path": "kernel/chr_drv/tty_io.c.rej",
    "chars": 350,
    "preview": "--- kernel/chr_drv/tty_io.c\n+++ kernel/chr_drv/tty_io.c\n@@ -141,9 +141,7 @@ int tty_read(int channel, char *buf, int nr)"
  },
  {
    "path": "kernel/chr_drv/tty_queue.c",
    "chars": 1535,
    "preview": "#include <linux/head.h>\n#include <linux/sched.h>\n#include <linux/tty.h>\n#include <serial_debug.h>\n\n\n// 定义对 tty_queue 的操作"
  },
  {
    "path": "kernel/chr_drv/tty_read.c",
    "chars": 238,
    "preview": "#define __LIBRARY__\n#include <unistd.h>\n#include <linux/tty.h>\n\n// Define user_tty_read syscall for test\n_syscall3(int, "
  },
  {
    "path": "kernel/chr_drv/vga_console.c",
    "chars": 4085,
    "preview": "/*\n * console low level implementation\n */\n\n#include <linux/kernel.h>\n#include <linux/head.h>\n#include <linux/mm.h>\n#inc"
  },
  {
    "path": "kernel/exit.c",
    "chars": 5181,
    "preview": "#include <errno.h>\n#include <signal.h>\n#include <linux/sched.h>\n#include <linux/kernel.h>\n#include <linux/tty.h>\n#includ"
  },
  {
    "path": "kernel/fork.c",
    "chars": 4294,
    "preview": "//#include <errno.h> 当前所有错误号为stub状态\n#include <linux/sched.h>\n#include <asm/system.h>\n#include <linux/mm.h>\n//#include <l"
  },
  {
    "path": "kernel/libc_restore.s",
    "chars": 280,
    "preview": ".global __sig_restore, __masksig_restore\n\n__sig_restore:\t\t# Use to restore to user routine\n\taddl $4, %esp\n\tpopl %eax\n\tpo"
  },
  {
    "path": "kernel/panic.c",
    "chars": 263,
    "preview": "/*\n * Use for trigger a kernel panic\n * When panic, it will print out a message and then \n * dead, use for alerting a ma"
  },
  {
    "path": "kernel/printk.c",
    "chars": 724,
    "preview": "/*\n * Rewrite printk\n * now printk call for sprintf\n * then call tty_write\n * (user mode will call write(x, x, x)\n */\n#i"
  },
  {
    "path": "kernel/sched.c",
    "chars": 6088,
    "preview": "#include <linux/sched.h>\n#include <linux/kernel.h>\n#include <linux/mm.h>\n#include <linux/sys.h>\n#include <asm/system.h>\n"
  },
  {
    "path": "kernel/serial_debug.c",
    "chars": 1740,
    "preview": "#include <asm/io.h>\n#include <stdarg.h>\n#define PORT 0x3f8\n\nint is_transmit_empty() {\n    return inb(PORT + 5) & 0x20;\n}"
  },
  {
    "path": "kernel/signal.c",
    "chars": 4745,
    "preview": "#include <linux/kernel.h>\n#include <signal.h>\n#include <sys/types.h>\n#include <serial_debug.h>\n#include <asm/segment.h>\n"
  },
  {
    "path": "kernel/signal_demo.c",
    "chars": 970,
    "preview": "#define __LIBRARY__\n#include <signal.h>\n#include <unistd.h>\n#include <errno.h>\n#include <signal.h>\n#include <linux/kerne"
  },
  {
    "path": "kernel/sys.c",
    "chars": 538,
    "preview": "#include <unistd.h>\n#include <linux/head.h>\n#include <linux/sched.h>\n#include <serial_debug.h>\n\n// #define DEBUG\n\nextern"
  },
  {
    "path": "kernel/system_call.s",
    "chars": 2201,
    "preview": "# system_call.s 里面实现了系统调用的过程\n# 实现了时钟中断和fork 必要的系统调用\n# 还没有对信号进行处理\n\n.global timer_interrupt, system_call, sys_fork\n\n# 我们来定"
  },
  {
    "path": "kernel/traps.c",
    "chars": 4346,
    "preview": "/*\n * Code to implement 80386 traps and interrupts\n *\n */\n\n#include <linux/head.h>\n#include <linux/sched.h>\n#include <li"
  },
  {
    "path": "kernel/vsprintf.c",
    "chars": 1879,
    "preview": "#include <stdarg.h>\n#include <stddef.h>\n#include <serial_debug.h>\n\n// Implement sprintf for print formatted string\n// Co"
  },
  {
    "path": "kernel/vsprintf.c.old",
    "chars": 4764,
    "preview": "/* Copyright (C) 2016 Gan Quan <coin2028@hotmail.com>\n * Copyright (C) 2016 David Gao <davidgao1001@gmail.com>\n *\n * Thi"
  },
  {
    "path": "ldS.ld",
    "chars": 209,
    "preview": "SECTIONS {\n    ENTRY(startup_32)\n    .text 0x0000 : {\n        *(.text)\n    }\n\n    .rodata . : {\n        *(.rodata)\n    }"
  },
  {
    "path": "lib/Makefile",
    "chars": 252,
    "preview": "include ../Makefile.header\n\nOBJS = getline.o printf.o\n\nLDFLAGS\t+= -r\nCFLAGS += -I../include\n\n.PHONY=clean run all\n\nall: "
  },
  {
    "path": "lib/getline.c",
    "chars": 523,
    "preview": "#define __LIBRARY__\n#define EOF -1\n#include <unistd.h>\n#include <linux/lib.h>\nextern int user_tty_read(int channel, char"
  },
  {
    "path": "lib/printf.c",
    "chars": 723,
    "preview": "/*\n * User mode printf function\n */\n#define __LIBRARY__\n#include <stdarg.h>\n#include <linux/tty.h>\n#include <stddef.h>\n#"
  },
  {
    "path": "mm/Makefile",
    "chars": 381,
    "preview": "include ../Makefile.header\n\nLDFLAGS\t+= -r\nCFLAGS += -I../include\n\n.PHONY=clean run all\n\n.c.o:\n\t@$(CC) $(CFLAGS) \\\n\t\t-c -"
  },
  {
    "path": "mm/memory.c",
    "chars": 11164,
    "preview": "/*\n * Routine use to handle physical page memory\n * memory.c 涉及到的函数均为物理页内存,以及页表管理函数\n * 并且实现了 Demand Loading 以及 Copy On W"
  },
  {
    "path": "mm/mm_test.c",
    "chars": 4456,
    "preview": "/*\n * File to show mm functional and test mm\n *\n */\n\n#include <linux/kernel.h>\n#include <linux/mm.h>\n#include <serial_de"
  },
  {
    "path": "mm/page.s",
    "chars": 387,
    "preview": ".code32\n\n.global page_fault\n\npage_fault:\n\txchgl %eax, (%esp)\t\t# 错误码入eax, 同时eax压栈\n\tpushl %ecx\n\tpushl %edx\n\tpush %ds\n\tpush"
  }
]

About this extraction

This page contains the full source code of the VOID001/neu-os GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 69 files (129.2 KB), approximately 47.1k tokens, and a symbol index with 189 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!