IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 自己动手写操作系统系列第1篇,从开机加电到切换保护模式 -> 正文阅读

[系统运维]自己动手写操作系统系列第1篇,从开机加电到切换保护模式

本系列为小组作业,参考了很多教程,包括ucore、30天自制操作系统、linux内核设计的艺术等内容、以及最重要的是小组里的几位大佬,本篇文章只是记录自己在学习过程中每一步的脚印,并包含了很多相关知识的补充。本系列大约会有五篇文章,可以完全从零基础编写一个十分简单的操作系统内核。


对应labOS版本v1.0

安装qemu

qemu用来模拟i386的硬件cpu环境

sudo apt install qemu-system-i386

安装好之后就可以用qemu-system-i386命令来模拟硬件环境来运行img镜像文件

补充apt install直接通过安装包安装的命令文件默认文件位置在/usr/bin,而通过下载源码文件手动编译生成的命令文件默认位置在/usr/local/bin

安装nasm

nasm是为可移植性与模块化而设计的一个80x86的汇编器

sudo apt install nasm

补充nasm用法

从编写hello.asm到nasm汇编成hello.o文件到ld链接成hello可执行文件

编写hello.asm文件

section .data
hello:  db 'hello world!', 10
len:   equ 13   ; hello world!\n
section .text
global _start
_start:
mov eax, 4   ; sys_write
mov ebx, 1   ; stdout
mov ecx, hello   
mov edx, len
int 80h
mov eax, 1
mov ebx, 0
int 80h
nasm -f elf64 hello.asm
ld hello.o -o hello
./hello

补充Makefile语法

  • 命令前面加一个$@时,使用make编译时不会显示此条命令,而如果使用make "V="还是会显示出来词条命令。如果命令前面没有$@时,即使只用make编译也会显示出命令信息。

  • $@ 表示目标文件
    $^ 表示所有的依赖文件
    $<目标依赖列表中的第一个依赖
    $?所有目标依赖中被修改过的文件

    这些符号表示的都是在命令当前代码块第一行用冒号等声明出来的各种类型的文件。

  • dd时使用conv=notrunc不截断输出文件

  • seek=1时从文件开头跳过1个快后再开始复制,效果会受conv=notrunc选项的影响。

  • makefile当生成一个目标文件后就停止了,如果要生成多个目标文件,需要在最开始添加一个标签,比如all

补充汇编语法

记录当前代码地址, 记录当前代码地址, 记录当前代码地址, 记录节的开始地址, 记录节的开始地址, 记录节的开始地址,-$$表示当前位置距离开始的相对距离

times重复汇编,在times后跟着的表达式会被重复汇编指定次数,语法示例times 510-($-$$) db 0

到此环境搭建完成,先创建一个目录labOS,以后都在这个目录里干活了。

编写引导扇区文件boot.s

补充:代码说明

  • BITS指令指定nasm产生的代码是被设计运行在16位模式的处理器上还是运行在32位模式的处理器上。
  • org指令是告诉汇编程序,在开始执行的时候,将某段机器语言装载到内存中的哪个地址,按道理说bios会主动加载引导扇区的内容到0x7c00,但是如果这里不加org 0x7c00就会报错。
  • BIOS是一组固化到计算机内主板上一个ROM芯片上的程序,因此我们写操作系统都是从bios之后的程序开始写,而bios最后的工作就是将磁盘第一扇区加载到0x7c00处,再从7c00处开始执行,因此我们的工作从第一扇区开始,此后我们编写的程序就完全可以不按照正常的内核的工作模式来写(正常的工作模式是bootsect加载setup,然后setup覆盖bootsec等等),而是按照我们自己规定的方式工作。同时需要注意的是,如果我们不覆盖bios在内存低地址初始化好的bios中断,那就可以用他的中断来做一些事情了。
  • 另外,bios加载引导扇区到0x7c00时会检测扇区最后是否以0x55\0xaa结尾,这里我们最后要设置一下。
  • 还有,我们在“随意”编写引导扇区代码时,可以直接将0x7c00作为sp,因为此时0x7c00一下一段空间里都没有东西,所以可以随便压栈操作。
; boot.s

BITS 16
; this is designed to be the first sector, it's code must be lower than 512B
; cs:ip=0000:7c00
org 0x7c00

; init segment registers
xor ax, ax
mov ds, ax
mov ss, ax
mov es, ax
mov sp, 0x7c00

; print some message, use bios's intrupt vector fucntion
printStr:
    mov ax, 0x1301
    mov bx, 0x0002
    mov cx, len
    mov dx, 0x0101
    mov bp, msg
    int 0x10

msg: db "oh~ hello labOS!", 10, 0
len: equ 18

; padding with 0 to 510B
times 510-($-$$) db 0
; padding with 0x55aa, it represent gurb sector
dw 0xaa55

编写Makefile文件

V := @
OSIMG := labOS.img

all: $(OSIMG)

boot.o: boot.s
	$(V)nasm $< -o $@

$(OSIMG): boot.o
	$(V)dd if=/dev/zero of=$@ count=100
	$(V)dd if=$< of=$@ conv=notrunc

clean:
	$(V)rm *.img
	$(V)rm *.o

补充:代码说明

创建镜像的过程参考ucore,先创建一个100B大小文件块,在往里面复制引导等等文件。

编译并运行

make
qemu-system-i386 -drive file=labOS.img,format=raw,media=disk -monitor stdio

在这里插入图片描述

到此说明引导扇区制作的没有错误,接下来继续制作实模式转保护模式

补充:代码说明

  • 因为boot.s这个文件大小不能超过512B,而且这里假设这个文件只能写上面那一点数据,所以我们需要把接下来的一些扇区的东西放到另一个文件里,然后让boot.s中的代码将这些扇区数据读入内存中,这里规定让他读到内存的0x8000处。

  • 当打开A20进入保护模式后,寻址方式立刻发生改变,因此在打开指令后需要用jump设置一下cs:ip,因为其中包含了rpl等一些信息,同时寻址方式的改变导致了bios的中断寻址无法正常寻址。

  • jmp dword 0x8:protect中的0x8就是保护模式下代码段选择子

  • GDT中第一个条目不被使用,这个条目占了8个字节,索引总个数为sizeof(gdt)/8-1。

  • 而段描述符中有4bit是type字段,在setup.s代码中定义为不同的宏

    %define STA_X 0x8     ; Executable segment
    %define STA_E 0x4     ; Expand down (non-executable segments)
    %define STA_C 0x4     ; Conforming code segment (executable only)
    %define STA_W 0x2     ; Writeable (non-executable segments)
    %define STA_R 0x2     ; Readable (executable segments)
    %define STA_A 0x1     ; Accessed
    
  • mov ebp, 0x0是因为初始的栈帧ebp是多少无所谓,在调用函数时,会有mov ebp, esp

  • ld -nostdlib 不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。这个选项常用于编译内核、bootloader等程序,它们不需要启动文件、标准库文件。

  • ld -T file将file作为链接脚本

重新修改boot.s文件

BITS 16
; this is designed to be the first sector, it's code must be lower than 512B
; cs:ip=0000:7c00
org 0x7c00

; init segment registers
xor ax, ax
mov ds, ax
mov ss, ax
mov es, ax
mov sp, 0x7c00

; print some message, use bios's intrupt vector fucntion
printStr:
    mov ax, 0x1301
    mov bx, 0x0002
    mov cx, len
    mov dx, 0x0101
    mov bp, msg
    int 0x10

; read sector to 0x8000
; buffer address  =>  es:bx = es<<4 + bx
next:
    mov ax, 0800h
    mov es, ax
    mov bx, 0
    mov ch, 0   ; Cylinder
    mov dh, 0   ; Heads
    mov cl, 2   ; Sector start number
    mov ah, 2   ; read function 
    mov al, 3   ; read 3 sectors
    mov dl, 80h ; 
    int 13h

    ; jump to setup.s
    jmp word 0000:8000h


; data to print
msg: db "oh~ hello labOS!", 10, 0
len: equ 18

; padding with 0 to 510B
times 510-($-$$) db 0
; padding with 0x55aa, it represent gurb sector
dw 0xaa55

编写boot.s跳转到的setup.s文件

补充:

0x8000~0x81ff留给启动扇区,这里也算启动扇区,所以读到这里完全可以

BITS 16
org 0x8000

; define main function
; main function is in the third sector, 0x8000+512=0x8200 
%define c_main 8200h

; define gdt 
%define seg_null times 8 db 0

; set_seg(%1, %2, %3), type:, base, lim
%macro set_seg 3
    dw (((%3) >> 12) & 0xffff), ((%2) & 0xffff)
    db (((%2) >> 16) & 0xff), (0x90 | (%1)), \
       (0xC0 | (((%3) >> 28) & 0xf)), (((%2) >> 24) & 0xff)
%endmacro

; macro set_seg's %1 => type
%define STA_X 0x8     ; Executable segment
%define STA_E 0x4     ; Expand down (non-executable segments)
%define STA_C 0x4     ; Conforming code segment (executable only)
%define STA_W 0x2     ; Writeable (non-executable segments)
%define STA_R 0x2     ; Readable (executable segments)
%define STA_A 0x1     ; Accessed

; close intrupt
cli

wait1:
    in al, 64h  ; 64 port is 8042's status register
    test al, 02h
    jnz wait1       ; input buffer has data 
    mov al, 0xd1    ; 0xd1 => port 0x64
    out 64h, al     ; 
wait2:
    in al, 64h
    test al, 02h
    jnz wait2
    mov al, 0xdf    ; 0xdf => port 0x60
    out 60h, al     ; 0xdf = 11011111, open A20 gate

; load GDT
lgdt [temp_gdtdesc]

; cr0[0](PE) => 1, enter protect mode
mov eax, cr0
or eax, 1
mov cr0, eax

jmp dword 0x8:protect   ; jmpi 0x08 => gdt+1, eip=protect, 0x8 is PROT_MODE_CSEG

align 4    ; 4B align
temp_gdt:
    seg_null
    set_seg STA_X|STA_R, 0x0, 0xffffffff ; code segment
    set_seg STA_W, 0x0, 0xffffffff       ; data segment
temp_gdtdesc:
    dw 0x17         ; three segments, 3x8-1 = 23 = 0x17 = sizeof(gdt)-1
    dd temp_gdt     ; gdt start address


BITS 32

protect:
    ; init data segment registers
    mov ax, 10h     ; 0x10 is PROT_MODE_DSEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov ebp, 0x0
    mov esp, 0x7c00 

jmp c_main

补充:代码说明

setup.s代码先关闭中断,然后等待缓冲区数据结束,之后打开了A20,加载了GDT,接着就jmp修改cs段寄存器,这时就完全到了保护模式的32位寻址模式。然后初始化了一下各个段寄存器就跳转到c语言编写的c_main函数中去了

编写main.c文件

void _hlt();

void main(){
    while (1){
        _hlt();
    }
}
void _hlt(){
    asm("hlt");
}

编写main.c的链接脚本main.ld

OUTPUT_FORMAT("binary")
OUTPUT_ARCH(i386)
ENTRY(main)

SECTIONS {
    . = 0x8200;
    .text : {
        *(.text .rel.text)
    }
    .data : {
        *(.data)
    }
}

编写Makefile文件

V := @
OSIMG := labOS.img

all: $(OSIMG)

boot.o: boot.s
	$(V)nasm $< -o $@

setup.o: setup.s
	$(V)nasm $< -o $@

main.o: main.c
	$(V)gcc main.c -march=i386 -m32 -fno-builtin -fno-PIC -Wall -nostdinc -fno-stack-protector -ffreestanding -c -o main.o

main.out: main.o
	$(V)ld -nostdlib -T main.ld $< -o $@

$(OSIMG): boot.o setup.o main.out
	$(V)dd if=/dev/zero of=$@ count=100
	$(V)dd if=$< of=$@ conv=notrunc
	$(V)dd if=setup.o of=$@ seek=1 conv=notrunc
	$(V)dd if=main.out of=$@ seek=2 conv=notrunc

clean:
	$(V)rm *.img
	$(V)rm *.o
	$(V)rm *.out

之后就可以编译

make

qemu启动

qemu-system-i386 -drive file=labOS.img,format=raw,media=disk -monitor stdio

这样就结束了,也可以

用gdb调试一下

qemu-system-i386 -s -S -drive file=labOS.img,format=raw,media=disk -monitor stdio
gdb
target remote localhost:1234
set architecture i8086
set disassembly-flavor intel
b *0x8000
c
x/20i $pc
b *0x8200
c
x/20i $pc

在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 13:11:55  更:2022-10-17 13:12:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 6:30:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码