<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      自制x86 Bootloader開發筆記(2)——— Bootloader設計與啟動區代碼實現

      計算機啟動流程簡介

      要知道如何設計bootloader,需要先了解一下計算機啟動的流程。具體可見引用1,這里只需要關注以下這一點即可:

      • 系統啟動后會自動將硬盤的第一個扇區(主引導記錄,MBR)加載至內存0x7c00處,并檢查MBR的第511和第512個字節是否為0x55和0xaa,如果是,則跳轉至0x7c00出開始執行對應的代碼。

      只要知道了這一點,我們的開發之路就可以正式啟動了,因為從代碼跳轉至0x7c00處開始,控制權就轉交到了我們所編寫的代碼之中。

      總體設計

      bootloader的作用就和它的名字一樣,一個作用是boot,另一個作用是loader,它負責電腦上電啟動后的一些系統準備工作,完成后加載內核并且跳轉到內核的入口,將控制權轉交給內核。這次要實現的bootloader比較簡單,主要完成以下幾個功能

      編寫Makefile

      我們使用Makefile來控制項目的構建,這里分成了三個Makefile文件。第一個最外層的Makefile負責生成硬盤鏡像、格式化文件系統、往硬盤寫入booloader以及內核等環境初始化工作。其他兩個Makefile則是子Makefile,負責bootloader的構建和內核的構建,項目結構如下:

      |--最外層Makefile
      |--bootloader
      |   |-- bootloader的Makefile
      |   |-- bootloader的源代碼
      |--kernel
      |   |-- kernel的Makefile
      |   |-- kernel的源代碼
      

      最外層Makefile

      CORES = $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null || sysctl -n hw.ncpu)
      DISK = kernel.img
      FSC_OBJ = ./bootloader/boot.o
      LOADER_OBJ = ./bootloader/loader.o
      FORMATOR = ../tools/mkmyfs/mkmyfs
      KERNEL = ./kernel/arcus_kernel
      
      all: build qemu
      
      build: clean build_bootloader build_kernel
      ifneq ($(FORMATOR), $(wildcard $(FORMATOR)))
      	$(MAKE) all -j$(CORES) -C ../tools/mkmyfs
      endif
      ifneq ($(DISK), $(wildcard $(DISK)))
      	dd if=/dev/zero of=$(DISK) bs=1024 count=524288
      	sleep 5
      endif
      	$(FORMATOR) $(DISK) -f
      	dd if=$(FSC_OBJ) of=$(DISK) conv=notrunc
      	dd if=$(LOADER_OBJ) of=$(DISK) conv=notrunc seek=3
      	$(FORMATOR) $(DISK) -w $(KERNEL)
      	sleep 1
      
      .PHONY: build_bootloader
      build_bootloader:
      	$(MAKE) all -j$(CORES) -C bootloader
      
      .PHONY: build_kernel
      build_kernel:
      	$(MAKE) all -j$(CORES) -C kernel
      
      .PHONY: qemu
      qemu:
      	qemu-system-x86_64 -smp 8 -m 8g -hda $(DISK) -monitor stdio -no-reboot
      	
      .PHONY: clean
      clean:
      	$(MAKE) clean -C bootloader
      	$(MAKE) clean -C kernel
      

      這里介紹最關鍵的幾個部分:

      1. 硬盤鏡像生成:使用dd命令生成空的文件,dd if=/dev/zero of=$(DISK) bs=1024 count=524288,命令表示生成1024*524288字節的文件,內部全部填充空數據0x00。
      2. Makefile遞歸執行:使用Makefile -C參數切換makefile工作目錄
      3. 硬盤格式化:自己實現了一個建議文件系統和格式化工具,代碼可見項目的tool目錄,這里不進行贅述
      4. 啟動區代碼寫入:同樣使用dd命令寫入,dd if=$(FSC_OBJ) of=$(DISK) conv=notruncconv=notrunc表示不進行截斷,硬盤鏡像文件顯然比bootloader大很多,寫入bootloder后其他剩余的扇區當然要保留下來
      5. elf loader寫入:dd if=$(LOADER_OBJ) of=$(DISK) conv=notrunc seek=3,依舊是dd命令,啟動區我們使用匯編實現,而elf loader因此邏輯更復雜,采用了C語言,因此編譯結果是一個獨立的二進制文件,需要額外寫入,seek=3表示跳過前三個扇區,即跳過之前寫入的bootloader

      bootloader的Makefile

      ASM = nasm
      CC = x86_64-elf-gcc
      OBJCOPY = x86_64-elf-objcopy
      LD = x86_64-elf-ld
      
      FST_PART_SRC = boot.asm
      LOADER_C_SRC = $(shell find . -name "*.c")
      LOADER_C_OBJ = $(patsubst %.c,%.o,$(LOADER_C_SRC))
      LOADER_ASM_SRC = $(shell find . -name "*.asm" ! -path "./boot.asm")
      LOADER_ASM_OBJ = $(patsubst %.asm,%.o,$(LOADER_ASM_SRC))
      
      TARGET_FST_PART = boot.o
      TARGET_LOADER_TMP = loader_tmp.o
      TARGET_LOADER = loader.o
      
      all: clean first_part kernel_loader
      
      .PHONY: first_part
      first_part: $(FST_PART_SRC)
      	$(ASM) $(FST_PART_SRC) -f bin -g -o $(TARGET_FST_PART)
      
      .PHONY: kernel_loader
      kernel_loader: link
      	$(OBJCOPY) -O binary $(TARGET_LOADER_TMP) $(TARGET_LOADER)
      
      .PHONY: link
      link: $(LOADER_C_OBJ) $(LOADER_ASM_OBJ)
      	$(LD) -nostdlib -Ttext 0x8200 $(LOADER_C_OBJ) $(LOADER_ASM_OBJ) -T scripts/loader.ld -o $(TARGET_LOADER_TMP)
      
      %.o:%.c
      	$(CC) -c -w -ffreestanding -I ./include -o $@ $<
      
      %.o:%.asm
      	$(ASM) -f elf64 -o $@ $<
      
      .PHONY: clean
      clean:
      	-rm $(TARGET_FST_PART) $(TARGET_LOADER_TMP) $(TARGET_LOADER) $(LOADER_C_OBJ) $(LOADER_ASM_OBJ)
      

      同樣介紹最關鍵的幾個部分:

      1. 啟動區前三個扇區使用匯編實現,完成讀取硬盤內容,進入長模式等工作。elf loader因邏輯更復雜,使用C語言實現
      2. elf loader(即kernel_loader這個任務)編譯出來的產物不能直接使用,因為gcc編譯出來的64位可執行文件不是純二進制的,它有著自己的ABI,就是ELF格式,我們還指望elf loader來實現加載elf文件的功能呢,當前并沒有運行elf文件的能力,只能接受純二進制的代碼。因此這里需要一些trick,只把代碼里的可執行代碼和數據拿出來,這樣就得到了我們編寫的代碼的純二進制格式,使用objcopy命令即可完成這個工作$(OBJCOPY) -O binary $(TARGET_LOADER_TMP) $(TARGET_LOADER)
      3. 在最外層的Makefile我們知道elf loader的寫入位置是第四個扇區,即0x7c00 + (512 * 3) = 0x8200,因此需要指定elf loader的程序入口點是0x8200, 使用-Ttext 0x8200,并配合鏈接器腳本,即可讓鏈接器將程序的入口點函數錨定到0x8200的位置,鏈接器腳本如下:
      ENTRY(main)
      SECTIONS
      {
          .text :
          {
              *(.text.main);
              *(.text*);
          }
      }
      

      *(.text.main)指定了.text.main段在text段中排在最前面的位置,配合C語言void loader_main() __attribute__ ((section (".text.main"))); ,將loader_main函數放置到.text.main中,即可讓代碼跳轉到0x8200就開始執行loader_main函數。由此就完成了匯編語言和C語言純二進制產物的融合。

      編寫啟動區代碼

      Makefile編寫完成之后,就終于可以開始bootloader的代碼編寫了,首先開發第一個扇區,就是啟動區的代碼:

      [BITS 16]
      org 7c00h
      mov si, 0
      mov ax, cs
      mov ds, ax
      mov es, ax           
      mov esp, 7c00h
      
      jmp load_stage2
      
      disk_rw_struct:
          db 16  ; size of disk_rw_struct, 10h
          db 0   ; reversed, must be 0
          dw 0   ; number of sectors
          dd 0   ; target address
          dq 0   ; start LBA number
      
      read_disk_by_int13h:
          mov eax, dword [esp + 8]
          mov dword [disk_rw_struct + 4], eax
          mov ax, [esp + 6]
          mov word [disk_rw_struct + 2], ax
          mov eax,dword [esp + 2]
          mov dword [disk_rw_struct + 8], eax
          mov ax, 4200h
          mov dx, 0080h
          mov si, disk_rw_struct
          int 13h
          ret
      
      load_stage2:
          push dword 0x7e00   ; target address
          push word 50        ; number of blocks
          push dword 1        ; start LBA number
          call read_disk_by_int13h
          add esp, 10
          jmp enter_long_mode
      
      times 510-($-$$) db 0
      dw 0xaa55
      

      這段代碼編譯之后正好是512個字節,是一個扇區的大小,正好填滿啟動區。代碼中times 510-($-$$) db 0這行表示重復填0直到填滿510個字節位置,而dw 0xaa55則使得這個扇區的最后兩個字節滿足啟動區簽名的條件,使其能夠被識別為一個啟動區。

      第一個扇區的功能很簡單,就是調用BIOS中斷中的擴展INT 13h中斷,讀取之后的幾個扇區,并且跳轉到第二個扇區。下面這張表摘自引用2,描述了INT 13h中斷需要使用的參數:

      INT13H 中斷使用參數
      Registers Description
      AH 42h = 擴展讀功能函數編號
      DL 驅動器編號 (e.g. 1st HDD = 80h)
      DS:SI segment:offset pointer to the DAP
      DAP : Disk Address Packet
      offset range size description
      00h 1 byte size of DAP (set this to 10h)
      01h 1 byte unused, should be zero
      02h..03h 2 bytes 需要讀取的扇區數
      04h..07h 4 bytes 讀取的數據最終的傳輸地址
      08h..0Fh 8 bytes 開啟讀取的扇區LBA (LBA從0開始)LBA詳解見 logical block addressing.

      我們將之后幾個扇區加載到啟動區之后的扇區,也就是0x8000的位置,讓后跳轉至第二個扇區,啟動區的工作就結束了。

      之后進入長模式和文件系統以及ELF Loader的內容相對獨立,放在之后的章節描述。


      項目地址:https://github.com/basic60/ARCUS

      引用

      1. http://www.ruanyifeng.com/blog/2013/02/booting.html ,計算機是如何啟動的?
      2. https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH=42h:_Extended_Read_Sectors_From_Drive , 使用擴展INT 13H中斷讀取硬盤
      posted @ 2023-11-05 01:35  basic60  閱讀(909)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 精品国产午夜福利在线观看 | 人人妻人人狠人人爽| 精品人妻午夜福利一区二区| 欧美精品在线观看视频| 国产视频一区二区三区四区视频| 我要看亚洲黄色太黄一级黄 | 亚洲精品一区二区在线播| 人妻丰满熟妇无码区免费| 日韩日韩日韩日韩日韩| 久久日产一线二线三线| 午夜免费无码福利视频麻豆| 鄂州市| 国产精品国产三级国产专i| 国产精品三级国产精品高| 国产午夜福利视频在线观看| 国产熟女一区二区三区四区| 久久人与动人物a级毛片| 色一情一区二区三区四区| 国产成人精品1024免费下载| 亚洲色欲色欲WWW在线丝| 国产亚洲精品第一综合另类无码无遮挡又大又爽又黄的视频 | 国产SUV精品一区二区6| 亚洲理论在线A中文字幕| 97精品久久天干天天天按摩 | 国产一级黄色片在线观看| 国产首页一区二区不卡| 99福利一区二区视频| 国产成人亚洲精品成人区| 开心激情站开心激情网六月婷婷| 日韩熟女熟妇久久精品综合| 国产在线观看免费人成视频| 国产精品高清国产三级囯产AV| 精品国产成人国产在线视| 国产激情视频在线观看首页| 丰满人妻被黑人猛烈进入| 天堂mv在线mv免费mv香蕉| 人妻中文字幕亚洲精品| 日韩欧激情一区二区三区| 亚洲最新无码中文字幕久久| 中文激情一区二区三区四区| 无码人妻丝袜在线视频|