x01.os.24: 来点代码

x01.treeos

这是在 deepin 上编译生成 linux-0.12 的学习代码,下载链接:x01.treeos

0.01-200412: 打个招呼

操作系统引导进入 main.c 的第一件事就是打个招呼: printk("Hello x01.treeos!");, 其简单实现 printk.c 如下:

#include <stdarg.h>
#include <string.h>

#define Origin_x    (*(unsigned char*)0x90000)
#define Origin_y    (*(unsigned char*)0x90001)
#define Video_base  ((unsigned char*)0xb8000)

int video_x, video_y;
void init_video()
{
    video_x = Origin_x;
    video_y = Origin_y;
}

static void print_char(char c, int x, int y, char color)
{
	unsigned char* video = Video_base;
	video[(y*80+x) * 2] = c;
    video[(y*80+x)*2+1] = color;
}

int print_color(const char* s, char color)
{
    int col = 0;
    for (int i=0; i < strlen(s); i++) {
        if (i != 0 && i % 80 == 0) {
            video_y ++;
            col -= i;
        }
        if (s[i] == '\n') {
            video_y ++; 
            col -= i;
            continue;
        }
        print_char(s[i], video_x + col++, video_y, color);
    }
    video_y ++;
}

static char buf[1024];
int printk(const char * fmt, ...)
{
    va_list args;
    int i;
    va_start(args, fmt);
    i = vsprintf(buf, fmt, args);
    va_end(args);

    print_color(buf, 0x07);
}

0.02-200421: 中断调用

发生中断时,ss, esp, eflags, cs, eip 依次入栈,中断返回时出栈。

divide_error:
    pushl $do_divide_error 
no_error_code:
    xchgl %eax, (%esp)
    pushl %ebx 
    pushl %ecx 
    pushl %edx 
    pushl %edi 
    pushl %esi 
    pushl %ebp 
    push %ds 
    push %es 
    push %fs 
    pushl $0 
    lea 44(%esp), %edx 
    pushl %edx 
    movl $0x10, %edx 
    mov %dx, %ds 
    mov %dx, %es 
    mov %dx, %fs 
    call *%eax 
    addl $8, %esp 
    pop %fs 
    pop %es 
    pop %ds 
    popl %ebp 
    popl %esi 
    popl %edi 
    popl %edx 
    popl %ecx 
    popl %ebx 
    popl %eax 
    iret 

0.03-200426: 系统调用

  1. 编制处理函数
  2. 在 unistd.h 中添加功能号和原型定义
  3. 在 linux/sys.h 中添加函数外部声明,并在 sys_call_table 中添加函数名
  4. 在 system_call.S 中将 nr_system_calls 增 1
  5. 在 lib/ 添加系统调用库函数的实现
.align 4
system_call:
    push %ds 
    push %es 
    push %fs 
    pushl %eax 
    pushl %edx 
    pushl %ecx 
    pushl %ebx 
    movl $0x10, %edx 
    mov %dx, %ds 
    mov %dx, %es 
    movl $0x17, %edx 
    mov %dx, %fs 
    cmpl NR_syscalls, %eax 
    jae bad_sys_call
    
    call *sys_call_table(, %eax, 4)
    pushl %eax
2:  movl current, %eax 
    cmpl $0, state(%eax)
    jne reschedule 
    cmpl $0, counter(%eax)
    je reschedule 

ret_from_sys_call:
    movl current, %eax 
    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 
    bsfl %ecx, %ecx 
    je 3f 
    btrl %ecx, %ebx 
    movl %ebx, signal(%eax)
    incl %ecx 
    pushl %ecx 
    call do_signal 
    popl %ecx 
    testl %eax, %eax 
    jne 2b 
3:  popl %eax 
    popl %ebx 
    popl %ecx 
    popl %edx 
    addl $4, %esp 
    pop %fs 
    pop %es 
    pop %ds 
    iret 

0.04-200708: 硬盘

  1. 硬盘 I/O 端口及寄存器
Register primary secondary 读时 写时
Command Block Registers 0x1f0 0x170 Data Data
0x1f1 0x171 Error 写前预补偿
0x1f2 0x172 Sector Count Secotr Count
0x1f3 0x173 LBA Low / 起始扇区 同读
0x1f4 0x174 LBA Mid / HD_LCYL 同读
0x1f5 0x175 LBA High / HD_HCYL 同读
0x1f6 0x176 Device Device
0x1f7 0x177 Status Command
Control Block Registers 0x3f6 0x376 Alternate status Device Control
  1. Device Register
bit value 说明
7 1
6 L 0:CHS, 1:LBA
5 1
4 DRV 0: master, 1:slave
3 HS3 L=1时, LBA 27 bit
2 HS2 L=1时, LBA 26 bit
1 HS1 L=1时, LBA 25 bit
0 HS0 L=1时, LBA 24 bit
  1. Status Register
bit value 说明
7 BSY 为 1 时其他位无效
6 DRDY Dirve Ready
5 DF/SE Device Fault / Stream Error
4 # Command dependent (formerly DSC bit)
3 DRQ Data request (ready to transfer data)
2 - Obsolete
1 - Obsolete
0 ERR Error (an error occurred)
  1. Device Control Register
bit value 说明
7 HOB High Order Byte (defined by 48-bit Address feature set)
6 -
5 -
4 -
3 -
2 SRST Software Rest
1 IEN Interrupt Enable
0 0

0.05-230115: 进程

  • 进程 0 的创建

初始化采取手工的方式,创建 INIT_TASK, 进入用户模式,即通过 iret 指令给 ss, esp, eflags, cs, eip 赋值。

#define move_to_user_mode()		\
__asm__("movl %%esp, %%eax\n\t"	\
	"pushl $0x17\n\t" /*ss*/	\
	"pushl %%eax\n\t" /*esp*/	\
	"pushfl\n\t"	 /*eflags*/	\
	"pushl $0x0f\n\t" /*cs*/	\
	"pushl $1f\n\t"	 /*eip*/    \
	"iret\n"			\
	"1:\tmovl $0x17,%%eax\n\t"	\
	"movw %%ax, %%ds\n\t"	\
	"movw %%ax, %%es\n\t"	\
	"movw %%ax, %%fs\n\t"	\
	"movw %%ax, %%gs"	\
	:::"ax")
  • 进程的运行

进程 0 采取复制的方式,调用 fork() => copy_process() 创建进程 1, 此时 fork() 返回进程号 1,继续 pause() => schedule() => switch_to() 运行进程 1。需要注意,copy_process() 创建进程 1 时 tss.eax=0; tss.eip=eip, 其中 eip 为中断返回的下一指令处, 即fork()if (__res >= 0) return __res; 处。进程切换时自动恢复 tr , 则由进程 1 tss 中恢复的返回值 eax 为 0, eip 指令地址为 fork() 中的 if (__res >= 0) return __res; 处。则 if (!fork()) 为真,进入 init() 创建进程 2: execve("/bin/sh", argv_rc, envp_rc);shell 处理 .rc 文件。然后不断循环,设置 tty , 创建子进程。

Linux 制作

  1. 创建虚拟盘 c.img 使用 bximage 即可
  2. 创建分区 (m: 帮助, n: 创建新分区, a: 添加引导标记, p: 查看, w: 保存)
sudo fdisk c.img
  1. 映射到 /dev/mapper
sudo kpartx -av c.img
ls -l /dev/mapper
  1. 根据映射 loop 格式化
sudo mkfs.ext4 /dev/mapper/loop0p1
  1. 安装 grub
sudo mount /dev/mapper/loop0p1 /mnt
sudo grub-install --boot-directory=/mnt/ /dev/loop0
  1. 复制 vmlinz initrd.img
sudo cp /boot/vmlinuz-4.15.0-30deepin-generic /mnt/boot/vmlinuz
sudo cp /boot/initrd.img-4.15.0-30deepin-generic  /mnt/boot/initrd.img
  1. 创建配置文件 grub.cfg,复制到 /mnt/boot/grub/ 中
default=0
timeout=10
insmod ext2
set root='hd0,msdos1'
menuentry 'x01.treeos' {
    insmod gzio
	insmod part_msdos
	insmod ext2
	set root='hd0,msdos1'
	linux /boot/vmlinuz ro --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1
	initrd /boot/initrd.img 
}
  • Make LiveCD 脚本
# 取消注释即可下载
#wget http://kernel.org/pub/linux/kernel/v4.x/linux-4.10.9.tar.xz
#wget http://busybox.net/downloads/busybox-1.24.2.tar.bz2
#wget http://kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.xz
mkdir isoimage
tar -xvf linux-4.10.9.tar.xz
tar -xvf busybox-1.25.1.tar.bz2
tar -xvf syslinux-6.03.tar.gz
cd busybox-1.25.1
make distclean defconfig
sed -i "s/.*CONFIG_STATIC.*/CONFIG_STATIC=y/" .config
make busybox install
cd _install
rm -f linuxrc
mkdir dev proc sys
echo '#!/bin/sh' > init
echo 'dmesg -n 1' >> init
echo 'mount -t devtmpfs none /dev' >> init
echo 'mount -t proc none /proc' >> init
echo 'mount -t sysfs none /sys' >> init
echo 'setsid cttyhack /bin/sh' >> init
chmod +x init
find . | cpio -R root:root -H newc -o | gzip > ../../isoimage/rootfs.gz
cd ../../linux-4.10.9
make mrproper defconfig bzImage
cp arch/x86/boot/bzImage ../isoimage/kernel.gz
cd ../isoimage
cp ../syslinux-6.03/bios/core/isolinux.bin .
cp ../syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32 .
echo 'default kernel.gz initrd=rootfs.gz' > ./isolinux.cfg
xorriso \
    -as mkisofs \
    -o ../minimal_linux_live.iso \
    -b isolinux.bin \
    -c boot.cat \
    -no-emul-boot \
    -boot-load-size 4 \
    -boot-info-table \
    ./
cd ..

0.06-230226:跟着代码走

文件,内存,IO设备等互为表里,牵连甚多,只能跟着代码走了,好在 CLK(Comment Linux Kernl) 所述甚详,可反复看。

制作根文件,需使用 sls1.0-img 提取码: 35wb, 参照 CLK 进行操作. 由于复制会忽略软链接 cpp 等,为使创建的根文件编译 linux-0.12 成功,需进入 /usr/local/bin 创建软链接 cpp, strip 等,例如: ln -s /usr/local/lib/gcc-cpp cpp. sls1.0-img 已作了精简,如果使用 oldliux 网站的原始根文件 rootimage-0.12-hd, 需注意有的内容不在一个分区中。

posted on 2019-01-14 13:21  x01  阅读(260)  评论(0编辑  收藏  举报

导航