1. 程式人生 > >Linux0.00 “boot.s” 程式詳解

Linux0.00 “boot.s” 程式詳解

    /*程式描述:  
    ;   boot.s程式編譯出的程式碼共512位元組,將被存放在軟盤映像檔案的第一個扇區中。PC在加電啟動時,
    ;   BIOS程式會把啟動盤上第一個扇區載入到實體記憶體0x7c00位置開始處,
    ;   然後跳轉到0x7c00處開始執行boot.s程式程式碼。

    ;   本程式(boot.s程式)將核心程式碼(head.s程式碼)載入到0x10000處,然後再移動到0x0處0,  
    ;   注意;載入到0x0處是為了設定GDT表時可以簡單一些,因而也可以讓head.s程式儘量短一些,  
    ;   但不能一開始就載入到0x0處是因為載入操作需要使用BIOS提供的中斷過程,而BIOS使用的  
    ;   中斷向量表正處於記憶體0開始的地方,並且在記憶體1KB開始處是BIOS程式使用的資料區,所以  
    ;   若直接將head程式碼加載於此將導致BIOS中斷過程不能正常執行。  
    ;   最後進入保護模式,並轉到0x0處繼續執行。  
    */        
      

    BOOTSEG = 0x07c0   /*前面講到PC加電啟動時會載入本程式到0x7c00處,那為什麼這裡卻是0x7c0,而不
                         是0x7c00呢,因為8086CPU剛啟動時是處在實地址模式,真實模式下最多定址1M,
                         並將1MB儲存空間分成許多邏輯段,每個段的長度被固定為64K。這樣每個儲存
                         單元就可以用“段基地址+段內偏移地址”表示。段基地址由16位段暫存器
                         值左移4位表達,段內偏移表示相對於某個段起始位置的偏移量。所以這裡的
                         0x07c0實際上是段基地址。
                        */
    SYSSEG = 0x1000    //將head.s加載於此(這裡為什麼是0x1000而不是0x10000原因同上)

    SYSLEN = 17        /*核心佔用的最大磁碟扇區數,為了簡化程式,這裡只能載入長度不超過16個扇區
                         的核心,這個作為BIOS的讀扇區功能的引數。問:既然僅限制16個扇區,為何以
                         17作為讀入扇區數? 其實這裡設定成16系統也能照常執行,多拷貝一個扇區可
                         能出於安全考慮。
                        */
    entry start        //這個的作用是什麼?彙編器彙編時必須有一個start指明入口地址,否則出現彙編錯誤
    start:  
        jmpi    go, #BOOTSEG    /*jmpi是段間跳轉指令,執行的結果是CS暫存器值變為0x7c0,接下來執
                                  行“BOOTSEG:go”處的指令。 為什麼需要這句話呢?不寫不也是從下
                                  面順序執行嗎? 答:剛開機時所有段暫存器(包括CS)的值為0,資料傳
                                  送指令是不能把資料傳送給CS的,因為CS是程式碼段暫存器,CS如果被修
                                  改程式就無法執行。所以必須用jmpi把cs改為0x7c0。
                                 */
    go:  mov     ax, cs          //ax是屬於通用暫存器之一的累加暫存器  
         mov     ds, ax    
         mov     ss, ax      /*讓兩個段暫存器ds和ss指向0x7c0段,問:1.為何需要讓這兩個段寄存
                               器指向這裡? 2.為何要通過ax間接傳遞資料而不能直接賦值呢?答:
                               1.這裡是為了讓DS和SS指向和程式碼段一致的段,ss裡面存放堆疊段的
                               段地址,sp存放偏移地址,實體地址=ss* 10H+ sp。這樣結合下面一
                               句,堆疊從實體地址0x7c00+0x400開始,留1K的程式碼空間 2. 80x86
                               中規定不能對段暫存器(CS,DS等)直接給立即數 
                              */
         mov     sp, #0x400   /*設定臨時棧指標。其值大於程式末端並有一定空間即可。問:1.為何需要
                                一個臨時棧指標?  2.這個值怎麼定,程式末端在哪裡如何計算?1.中斷
                                需要使用到堆疊 2.8086堆疊的生長方向為向下增長。boot.s佔用512位元組
                                ,這裡設定成遠大於512的任意值就可以。
                               */
                              //現在載入核心程式碼(head.s程式)至0x10000開始處  
    load_system:              /*問:標號有沒有實際作用?標號指明其所在位置的地址
                                首先介紹一下BIOS的0x13的0x02號功能  
                                BIOS INT 0x13的0x02號功能 - 讀扇區  
      
                                INT 0x13/AH=0x02 - 將磁碟上的扇區讀入記憶體  
                                AH = 0x02  
                                AL = 要讀入的扇區數  
                                CH = 柱面(磁軌)號的低8位  
                                CL = 位7、6是柱面(磁軌號)高2位,位5-0是讀入的起始扇區號(從1計,
                                     第一扇區存放的是boot.s,第二扇區開始放的是head.s,這裡要
                                     讀的是head.s所以從第二個扇區開始讀)
                                DH = 磁頭號  
                                DL = 驅動器號  
                                ES:BX = 緩衝區(用於儲存讀入扇區)的位置  
                                返回值:  
                                AH = 狀態碼  
                                AL = 讀到的扇區數  
                                CF = 失敗為1,成功為0       
                               */
        mov ch, #0x00  
        mov cl, #0x02         /*問:為什麼是載入2號扇區?答:因為磁碟的第一扇區放置的即是本程式
                                (引導啟動程式boot.s),而緊鄰的第二扇區開始則放置核心程式碼head.s
                                。扇區號從1開始計算。
                               */
        mov dh, #0x00  
        mov dl, #0x00          //問:驅動器是指什麼?這裡的驅動器號是0,表示floppya,即第一個軟盤驅動器。
        mov ax, #SYSSEG        /*不能直接執行mov es, #SYSSEG,編譯時會出現illegal immediate mode
                                 錯誤,因為80x86中規定不能對段暫存器(CS,DS等)直接給值/立即數
                                */
        mov es, ax  
        xor bx, bx              //將核心放置於1000:0000位置處  
        mov ah, #0x02  
        mov al, #SYSLEN  
        int 0x13                //設定好各項引數後即可呼叫BIOS的0x13功能  
        jnc ok_load             //jnc(jump not c)是一跳轉指令,當進位標記C為0時跳轉,為1時執行後面的指令 
   die:    jmp die              //死迴圈
          
               /*到目前為止我們已將核心程式碼從磁碟讀入到記憶體中指定位置了,下面就開始將核心
                 程式碼轉移到0x0這個記憶體開始位置。共移動8K位元組((16個扇區*512B/每扇區)/1024=8KB)。  
                */
    ok_load:  
        cli            /*關中斷    問:為何在開始移動時要關中斷,是為了防止什麼事件的發生嗎?
                         若不關會怎樣?  
                         在搬移之前先介紹一下REP指令及MOVW指令       
                         REP:重複字首,字串操作本身每次只處理一個記憶體值,但如果使用重複前
                         綴的話,該指令就會使用ECX作為計數器進行重複。換句話說,就是可以用一
                         條指令處理整個陣列。  
                         MOVW:將DS:SI(源變址暫存器)的內容送至ES(附加段資料暫存器):DI
                         (目的變址暫存器),是複製過去,原來的程式碼還在。  
                         附:ES和DS的功能相同,程式中設有多個數據段時,可以選用ES暫存器。一般
                         在串處理時用得比較多。比如將一段記憶體空間儲存的資料複製到另一段空間,可
                         以分別設定DS:SI指向源儲存資料的地址,ES:DI指向目的儲存資料的地址  
                        */
        mov ax, #SYSSEG    //移動開始位置:DS:SI=0x1000:0;目的位置:ES:DI=0:0  
        mov ds, ax  
        xor ax, ax  
        mov es, ax  
        mov cx, #0x1000    //設定共移動4K次,每次1個字(即移動16個扇區的程式碼)。  
        sub si, si  
        sub di, di  
        rep movw           //執行重複移動指令  
       
      
                           //載入IDT和GDT基地址暫存器IDTR和GDTR  
        mov ax, #BOOTSEG  
        mov ds, ax    /*讓DS重新指向0x7c0段(問:1.不一定要讓資料段指向這個位置吧? 答:這裡必
                       須讓ds重新指向0x07c0段,因為lidt和lgdt隱含的完整格式上是ds:idt_operand和
                       ds:gdt_operand,會在ds:operand這個位置去尋找它們的六位元組運算元。2.這時
                       的情況是不是:0x0-0x2000:簡單核心的程式碼區;0x7c00:資料段起始地址;
                      */
        lidt idt_48         //載入IDTR。6位元組運算元:2位元組表長度,4位元組線性基地址  
        lgdt gdt_48         //載入GDTR。6位元組運算元:2位元組表長度,4位元組線性基地址  
      
           /*設定好了中斷描述符表IDT和全域性描述符表GDT,並且載入好IDTR和GDTR後,準備進入保護模式  
             設定控制暫存器CR0(即機器狀態字),進入保護模式。段選擇符值8對應GDT表中第2個段描述符  
             控制暫存器(CR0、CR1、CR2和CR3)用於控制和確定處理器的操作模式以及當前執行任務的特性
                CR0中含有控制處理器操作模式和狀態的系統控制標誌  
                CR1保留不用  
                CR2含有導致頁錯誤的線性地址  
                CR3含有頁目錄表實體記憶體基地址(因此該暫存器也被稱為頁目錄基地址暫存器PDBR)  
            */
        mov ax, #0x0001 //(運算元的第四位是0x1=0001,將傳給CR0)  
          /*先介紹一下LMSW指令:LMSW: Load Machine Status Word(置處理器狀態字)  只有運算元的低4
            位被存入CR0,只有PE(位0),MP(位1)和EM(位2)和TS(位3)被改寫,CR0其他位不受影響。  
           */
        lmsw    ax      //設定CR0,進入保護模式。 
         /*先介紹下JMPI指令(段間跳轉指令),在真實模式下JMPI B, A 是跳到以A為段基地址,以B為偏
           移地址處執行,在保護式下JMPI B,A是跳轉到以A為段選擇符,偏移為B處執行。JMP是段內的跳轉
          */
    jmpi    0, 8    //然後跳轉至段選擇符指定的段中,偏移0處  
             /*注意此時段值已是段選擇符。該段的線性基地址是0。  
               這個8是怎麼來的?這個8(實際是 0x0008 ,二進位制:0000000000001 0 00)是段選擇符,由
               段選擇符的定義可知,該選擇符選擇的是RPL為0,索引為1的GDT描述符,前面的lgdt指令已
               經在GDTR暫存器中存放了GDT表的位置跟長度,每個段描述符固定佔用8位元組,所以根據索引
               值就可以找到GDT表中的段描述1,可以看到該段基地址是0x0000000,加上偏移值0,由於這
               裡沒有開啟分頁保護,所以是跳到實體地址0處執行。
              */
      //下面是全域性描述符表GDT的內容。其中包含3個段描述符。第1個不用,另2個是程式碼和資料段描述符 
    gdt:    .word   0,0,0,0       //段描述符0,不用。每個描述符項佔8位元組。 
      
            .word   0x07FF        //段描述符1,段限長8M,(2048*4096 = 8M)
            .word   0x0000        //段基地址是0x00000000
            .word   0x9A00        //可讀可執行的程式碼段
            .word   0x00C0        //顆粒度:4K
      
            .word   0x07FF        //段描述符2
            .word   0x0000  
            .word   0x9200  
            .word   0x00C0  
      
           //下面分別是LIDT和LGDT指令的6位元組運算元  
    idt_48: .word   0  
            .word   0,0  
    gdt_48: .word   0x7ff  
            .word   0x7c00+gdt,0  
      
    .org 510    /*問:這是什麼?答:.org指令表示以後的內容從510位元組開始存放,下面的AA55是引導
                  扇區的結束標誌,佔二位元組,這就是為什麼boot.s剛好佔512位元組的原因,
                  如果該標誌錯誤系統就不能啟動。
                 */
    .word   0xAA55 



相關推薦

Linux0.00boot.s程式

/*程式描述: ; boot.s程式編譯出的程式碼共512位元組,將被存放在軟盤映像檔案的第一個扇區中。PC在加電啟動時, ; BIOS程式會把啟動盤上第一個扇區載入到實體記憶體0x7c00位置開始處, ; 然後跳轉到0x7c

linux0.00 "head.s"程式

// head.s包含32位保護模式初始化設定程式碼、時鐘中斷程式碼、系統呼叫中斷程式碼和兩個任務的程式碼。 // 在初始化完成之後程式移動到任務0開始執行,並在時鐘中斷控制下進行任務0和1之間的切換操作。 LATCH = 119

spring-boot入門程式

1.建立一個普通的maven專案,專案名為boot-learnning 2.在pom.xml新增parent依賴 <parent> <groupId>org.springframework.boot</groupId> <

Spring Boot 之 HelloWorld

www repos lookup dex lns jar nap put epo SpringBoot介紹~<暫時假裝有> 配置 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht

Spring Boot異常處理

在Spring MVC異常處理詳解中,介紹了Spring MVC的異常處理體系,本文將講解在此基礎上Spring Boot為我們做了哪些工作。下圖列出了Spring Boot中跟MVC異常處理相關的類。 Spring Boot在啟動過程中會根據當前環境進行AutoConfigurat

Flask程式

本文將簡要的介紹Flask的各部分作用,並編寫並執行第一個最簡單的Flask程式。 1. 初始化 所有Flask程式都必須建立一個程式例項。Web伺服器使用一種名為Web伺服器閘道器介面(Web Server Gateway Interface,WSGI)的協議,把接收自客戶端的所有請求

spring boot之配置

spring boot支援的配置檔案 spring boot支援兩種型別的配置檔案,一種是傳統的預設配置檔案application.properties ,還有一種是現在被廣泛推薦使用的YAML檔案。書寫上properties是採用鍵值對的形式來表示,而YAML是以類似大綱的縮排形式,這

第一個SpringMVC程式

建立一個java類: package com.myproject.cn; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org

PyInstaller打包Python程式

PyInstaller可以將Python程式及其依賴項打包到一個包中,使用者可以在不安裝Python直譯器或任何模組的情況下執行打包的可執行程式,PyInstaller已經支援Python2.7和3.3+,可以構建多平臺的應用程式,在Windows平臺中建立可執行的Windows應用程式

大資料篇:Spark入門第一個Spark應用程式:WordCount

任務要求 編寫一個Spark應用程式,對某個檔案中的單詞進行詞頻統計。 備註:本文spark的根目錄名:spark-1.6.3-bin-hadoop2.6 #準備工作 cd /usr/local/spark-1.6.3-bin-hadoop2.6 mkdir mycode

SpringBoot非官方教程 | 第二篇:Spring Boot配置檔案

springboot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨在讓您儘快啟動和執行。在一般情況下,我們不需要做太多的配置就能夠讓spring boot正常執行。在一些特殊的情況下,我們需要做修改一些配置,或者需要有自己的配置屬性。 當我們

Spring boot 配置檔案 (properties 和yml )

從其他框架來看 我們都有自己的配置檔案, hibernate有hbm,mybatis 有properties, 同樣, Spring boot 也有全域性配置檔案。 Springboot使用一個全域性的配置檔案,而且配置檔案的名字是固定的。 有兩種 application.properties

Spring Boot 配置檔案:自定義屬性、隨機數、多環境配置等

相信很多人選擇Spring Boot主要是考慮到它既能兼顧Spring的強大功能,還能實現快速開發的便捷。我們在Spring Boot使用過程中,最直觀的感受就是沒有了原來自己整合Spring應用時繁多的XML配置內容,替代它的是在pom.xml中引入模組化的Starter POMs,其中各個模組都有自己的預

Spring boot 2 -配置

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

在Linux上進行原始碼編譯安裝程式

文章轉載自:http://xuweitao.blog.51cto.com/11761672/1905357 1. 編譯安裝概述 前面兩篇關於程式包管理器的文章談到,無論是使用rpm命令還是yum命令安裝的都是已編譯好的程式包,在整個安裝過程中使用者只需執行一條命令即可完成安裝。這樣帶

SpringBoot學習第二篇:Spring Boot配置檔案

原文首發於:https://www.fangzhipeng.com/springboot/2017/07/11/springboot2-config-file/ 本文出自方誌朋的部落格 springboot採納了建立生產就緒Spring應用程式的觀點。 Spring Boot優先於配置的慣例,旨

Spring Boot配置檔案-ConfigurationProperties和Value優缺點-(轉)好文

文章轉自 http://www.cnblogs.com/itdragon/p/8686554.html Spring Boot提供了兩種常用的配置檔案,分別是properties檔案和yml檔案。他們的作用都是修改Spring Boot自動配置的預設值。相對於properties檔案而言,yml檔

Spring Boot 整合 FreeMarker 案例

一、Springboot 那些事 SpringBoot 很方便的整合 FreeMarker ,DAO 資料庫操作層依舊用的是 Mybatis,本文將會一步一步到來如何整合 FreeMarker 以及配置的詳解 二、執行 springboot-freemarker 工程 1.資

Spring Boot事務管理

什麼是事務? 我們在開發企業應用時,對於業務人員的一個操作實際是對資料讀寫的多步操作的結合。由於資料操作在順序執行的過程中,任何一步操作都有可能發生異常,異常會導致後續操作無法完成,此時由於業務邏輯並未正確的完成,之前成功操作資料的並不可靠,需要在這種情況下進行回退。 事務的作用就是為了

Linux從原始碼編譯安裝程式

1.原始碼編譯概述 1.1 使用原始碼安裝軟體的優點: 獲得最新的軟體版本,及時修復bug 根據使用者需要,靈活定製軟體功能 1.2 應用場合舉例 安裝較新版本的應用程式時 當前安裝的程式無法滿足需要時  需要為應用程式新增新的功能時