免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: mik

mouseOS 项目 -- x64 体系的实验品(10.13 更新 -- 实现新的分页管理) [复制链接]

论坛徽章:
0
发表于 2009-07-18 17:10 |显示全部楼层
10、OS 核心代码

; 0x10000:
next:
;        call init_tss
        mov ax, tss1_sel
        ltr ax
        
; test #DE ....
;        mov eax,0
;        div al        

        mov ax, ds3_sel|0x03
        mov ds, ax
        mov es, ax
        
        push (ds3_sel) | 0x03                ; stack 3
        push 0x1fff0000                                ; stack 3
        push (user_cs_sel)|0x03
        push os_main
        retf
        
done:

        jmp $
        

message db "... protected mode...",0
exception0 db " exception: divide by zero...",0



os_main:
        jmp os_next
os_msg db 'Shell > ',0
os_next:
        push os_msg
        push 0
        push 1
        call display_message
        
        jmp $



1、加载 TSS 到 tr
2、最终转入 user 级代码(3 级代码)os_main
3、在 os_main()代码里
  它是一个 3 级权限的代码。显示 shell>,死循环

论坛徽章:
0
发表于 2009-07-19 18:29 |显示全部楼层
11、开启分页机制


  今天,我为 x64os 加入了分页机制。

  这里,我的目标平台是在 bochs 上。分配的内存只有 1G,从 0x00000000 ~ 0x3FFFFFFF,

    也就是可以访问的物理地址范围只有 0x00000000 ~ 0x3FFFFFFF,了解这一点非常重要。

因为:
  1、在未开启分页时,在代码中有如下代码访问:
如:
  mov dword [0x80000000], 0           /* 访问超过 1g 的物理地址 */
   
那么:以上的代码将会抛出 #GP 异常。

  
  2、开启分页机制,必须了解物理内存的多少,才能做到将哪部分 virtual address 映射到哪部分 physical address





下面是我在 x64os 中的映射表:

virtual address                                     physical address
---------------------------------------------------------------
0x6000 ~ 0x6fff                                   0x6000 ~ 0x6fff
0x7000 ~ 0x7fff                                   0x7000 ~ 0x7fff
0x10000 ~ 0x10fff                                0x10000 ~ 0x10fff
0xb8000 ~ 0xb8fff                                0xb8000 ~ 0xb8fff

0x00400000 ~ 0x00bfffff                       0x00400000 ~ 0x00bfffff

0x80000000 ~ 0x801fffff                       0x30000000 ~ 0x301fffff
0xffc00000 ~ 0xffdfffff                         0x3fc00000 ~ 0x3fdfffff
--------------------------------------------------------------------


1、0x6000 ~ 0x6fff (4K)
    0x7000 ~ 0x7fff (4K)
   0x10000 ~ 0x10fff (4K)
    0xb8000 ~ 0xb8fff (4K)

    上面这几个空间,它们的 virtual address 和 physical address 是一样的。


2、0x00400000 ~ 0x00bfffff (3M) :这 3M 空间 的 virtual address 和 physical address 也是一样的。
  这部分以后是用户程序代码的空间,这部分是 4K-page 结构

3、0x80000000 ~ 0x801fffff (2M):这 2M 空间被映射到 0x30000000 ~ 0x301fffff(physical address)上。
  这部分作为 kernel 代码空间,这部分是 2M-page 结构

4、0xffc00000 ~ 0xffdfffff (2M):这 2M 空间被映射到 0x3fc00000 ~ 0x3fdfffff(physical address)上。
  这部分作为 kernel 空间,这部分也是 2M-page 结构的。




下面是开启分页机制的代码:

     call init_page        ; page structure initialization


        mov eax, PDPE_BASE
        mov cr3, eax                ; page structure base
        
        mov eax, cr4
        or eax, 0x20                ; CR4.PAE = 1
        mov cr4, eax
        
        mov eax, cr0
        or eax, 0x80000000        ; CR0.PG = 1
        mov cr0, eax


x64os 中的分页中开启了 PAE 模式,因此,可以有 4K page 和 2M page 结构


1、PDPE_BASE 的值为:0x3fc00000,在 x64os.inc 文件中定义
  在 PAE 模式下,CR3 指向 page directory pointer table(PDPT),它的 entry 是 PDPE

2、分别打开了 CR4.PAE 和 CR0.PG  从而进入 PAE 分页模式。

3、在打开 CR4.PAE 和 CR0.PG 之前,首先了对 page 结构进行初始化,也就是定义映射表格。这是这里讲的重点。

论坛徽章:
0
发表于 2009-07-19 19:14 |显示全部楼层
下面看一看 init_page() 的代码:

-------------------------------------------------------------------------------------------------


init_page:

PG_P          equ 0x01
PG_W         equ 0x02
PG_USER     equ 0x04
PG_PWT     equ 0x08
PG_PCD      equ 0x10
PG_PS        equ 0x80

;PDP-table structure initialization
        ; PDPE0: virtual address: 0x00000000 ~ 0x3fffffff

        mov dword [PDPE_BASE], 0x3fc01000 | PG_P
        mov dword [PDPE_BASE+4], 0
        
        ; PDPE1: virtual address: 0x40000000 ~ 0x7fffffff (not support)
        mov dword [PDPE_BASE+8], 0                                
        mov dword [PDPE_BASE+8+4],0
        
        ; PDPE2: virtual address: 0x80000000 ~ 0xbfffffff
        mov dword [PDPE_BASE+16], 0x3fc02000 | PG_P
        mov dword [PDPE_BASE+16+4], 0
        
        
        ; PDPE3: virtual address: 0xc0000000 ~ 0xffffffff
        mov dword [PDPE_BASE+24], 0x3fc03000 | PG_P
        mov dword [PDPE_BASE+24+4], 0
        

        
; PD-table structure initialization
;0x00400000~0x00bfffff(virtual address) map into 0x00400000~0x00bffffff(physical address)


;------- PDE[0] map 0x6000 ~ 7fff  and 0xb8000 ~ b9fff, 0x10000 ~ 0x10fff --------------        

        ; PDE[0](00000000~xxxxxxxx): r/w=1, u/s=1,p=1

        mov dword [0x3fc01000], 0x3fd01000 | PG_P | PG_USER | PG_W
        mov dword [0x3fc01000+4], 0

        
;----------------------kernel's 2M page -----------------------
; 0x80000000~0x801fffff(virtual address) map into physical page(0x30000000~0x301fffff)

        mov dword [0x3fc02000], 0x30000000 | PG_P | PG_PS | PG_W
        mov dword [0x3fc02004], 0

        
        ; PDE[1](00200000~xxxxxxxx) not support
        
        
;------- PDE[2] ~ PDE[5] map 0x00400000~0x00bfffff -------------------

        ; PDE[2](00400000~xxxxxxxx): r/w=1, u/s=1,p=1

        mov dword [0x3fc01000+2*8], (0x3fd02000) | PG_P | PG_USER | PG_W
        mov dword [0x3fc01000+2*8+4], 0
        
        ; PDE[3](00600000~xxxxxxxx): r/w=1, u/s=1,p=1
        mov dword [0x3fc01000+3*8], (0x3fd03000) | PG_P | PG_USER | PG_W
        mov dword [0x3fc01000+3*8+4], 0
        
        ; PDE[4](00800000~xxxxxxxx): r/w=1, u/s=1,p=1
        mov dword [0x3fc01000+4*8], (0x3fd04000) | PG_P |PG_USER | PG_W
        mov dword [0x3fc01000+4*8+4], 0
        
        ; PDE[5](00a00000~xxxxxxxx): r/w=1,u/s=1,p=1
        mov dword [0x3fc01000+5*8], (0x3fd05000) | PG_P | PG_USER | PG_W
        mov dword [0x3fc01000+5*8+4], 0


        
;-------- PDE[510] map 0xffc00000 ~ 0xffdfffff ---------------------
;--------- 0xffc00000~0xffdfffff(virtual address) map into physical page(0x3fc00000~ 0x3fdfffff)
        
        ; PDE[510](ffc00000~xxxxxxxx):r/w=1, u/s=0,p=1,ps=1 (2M page)

        mov dword [0x3fc03000+510*8], 0x3fc00000 | PG_P | PG_W | PG_PS
        mov dword [0x3fc03000+510*8+4], 0

        
        
        

; Page Table structure
;
;----------- PTE[6](0x6000~0x6fff) map into 0x6000~0x6fff(physical address) -------------

        mov dword [0x3fd01000+6*8], 0x6000 | PG_P | PG_USER | PG_W
        mov dword [0x3fd01000+6*8+4], 0
        
        ; PTE[7](0x7000~0x7fff) map into 0x7000~0x7fff(physical address)
        mov dword [0x3fd01000+7*8], 0x7000 | PG_P | PG_USER | PG_W
        mov dword [0x3fd01000+7*8+4], 0
        
        ; PTE[0x10](0x10000~0x10fff) map into 0x10000~0x10fff(physical address)
        mov dword [0x3fd01000+0x10*8], 0x10000 | PG_P | PG_USER | PG_W
        mov dword [0x3fd01000+0x10*8+4], 0
        
        ; PTE[0xb8](0xb8000~0xb8fff) map into 0xb8000~0xb8fff(physcial address)
        mov dword [0x3fd01000+0xb8*8], 0xb8000| PG_P | PG_USER | PG_W
        mov dword [0x3fd01000+0xb8*8+4], 0
        
        ;PTE[0xb9](0xb9000~0xb9fff) map into 0xb9000~0xb9fff(physical address)
        mov dword [0x3fd01000+0xb9*8], 0xb9000 | PG_P | PG_USER | PG_W
        mov dword [0x3fd01000+0xb9*8+4], 0
        
;----------------------------------------------------------------
; PTE[0] (0x00400000~00400fff) map into 0x400000~0x400fff(physical address)
;        mov dword [0x3fd02000], 0x400000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+4], 0

; PTE[1] (0x401000~0x401fff) map into 0x401000~0x401fff
;        mov dword [0x3fd02000+8], 0x401000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+8+4], 0
        
; PTE[2] (0x402000~0x402fff) map into 0x402000~0x402fff
;        mov dword [0x3fd02000+2*8], 0x402000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+2*8+4], 0
        
; PTE[3] (0x403000~0x403fff) map into 0x403000~0x403fff
;        mov dword [0x3fd02000+3*8], 0x403000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+3*8+4], 0
        
; PTE[4] (0x404000~0x404fff) map into 0x404000~0x404fff
;        mov dword [0x3fd02000+4*8], 0x404000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+4*8+4], 0        
        
; PTE[5] (0x405000~0x405fff) map into 0x405000~0x405fff
;        mov dword [0x3fd02000+5*8], 0x405000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+5*8+4], 0        

; PTE[6] (0x406000~0x406fff) map into 0x406000~0x406fff
;        mov dword [0x3fd02000+6*8], 0x406000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+6*8+4], 0        

; PTE[7] (0x407000~0x407fff) map into 0x407000~0x407fff
;        mov dword [0x3fd02000+7*8], 0x407000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+7*8+4], 0        
        
; PTE[8] (0x408000~0x408fff) map into 0x408000~0x408fff
;        mov dword [0x3fd02000+8*8], 0x408000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+8*8+4], 0
        
; PTE[9] (0x409000~0x409fff) map into 0x409000~0x409fff
;        mov dword [0x3fd02000+9*8], 0x409000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+9*8+4], 0
        
; PTE[0xa] (0x40a000~0x40afff) map into 0x40a000~0x40afff
;        mov dword [0x3fd02000+0xa*8], 0x40a000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0xa*8+4], 0
        
; PTE[0xb] (0x40b000~0x40bfff) map into 0x40b000~0x40bfff
;        mov dword [0x3fd02000+0xb*8], 0x40b000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0xb*8+4], 0
        
; PTE[0xc] (0x40c000~0x40cfff) map into 0x40c000~0x40cfff
;        mov dword [0x3fd02000+0xc*8], 0x40c000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0xc*8+4], 0
        
        
; PTE[0xd] (0x40d000~0x40dfff) map into 0x40d000~0x40dfff
;        mov dword [0x3fd02000+0xd*8], 0x40d000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0xd*8+4], 0
        
; PTE[0xe] (0x40e000~0x40efff) map into 0x40e000~0x40efff
;        mov dword [0x3fd02000+0xe*8], 0x40e000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0xe*8+4], 0
        
; PTE[0xf] (0x40f000~0x40ffff) map into 0x40f000~0x40ffff
;        mov dword [0x3fd02000+0xf*8], 0x40f000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0xf*8+4], 0

; PTE[0x10] (0x00410000~00410fff) map into 0x410000~0x410fff(physical address)
;        mov dword [0x3fd02000+0x10*8], 0x410000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x10*8+4], 0

; PTE[0x11] (0x411000~0x411fff) map into 0x411000~0x411fff
;        mov dword [0x3fd02000+0x11*8], 0x411000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x11*8+4], 0
        
; PTE[0x12] (0x412000~0x412fff) map into 0x412000~0x412fff
;        mov dword [0x3fd02000+0x12*8], 0x412000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x12*8+4], 0
        
; PTE[0x13] (0x413000~0x413fff) map into 0x413000~0x413fff
;        mov dword [0x3fd02000+0x13*8], 0x413000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x13*8+4], 0
        
; PTE[0x14] (0x414000~0x414fff) map into 0x414100~0x414fff
;        mov dword [0x3fd02000+0x14*8], 0x414000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x14*8+4], 0        
        
; PTE[0x15] (0x415000~0x415fff) map into 0x415000~0x415fff
;        mov dword [0x3fd02000+0x15*8], 0x415000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x15*8+4], 0        

; PTE[0x16] (0x416000~0x416fff) map into 0x416000~0x416fff
;        mov dword [0x3fd02000+0x16*8], 0x416000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x16*8+4], 0        

; PTE[0x17] (0x417000~0x417fff) map into 0x417000~0x417fff
;        mov dword [0x3fd02000+0x17*8], 0x417000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x17*8+4], 0        
        
; PTE[0x18] (0x418000~0x418fff) map into 0x418000~0x418fff
;        mov dword [0x3fd02000+0x18*8], 0x418000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x18*8+4], 0
;        
; PTE[0x19] (0x419000~0x419fff) map into 0x419000~0x419fff
;        mov dword [0x3fd02000+0x19*8], 0x419000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x19*8+4], 0
        
; PTE[0x1a] (0x41a000~0x41afff) map into 0x41a000~0x41afff
;        mov dword [0x3fd02000+0x1a*8], 0x41a000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x1a*8+4], 0
        
; PTE[0x1b] (0x41b000~0x41bfff) map into 0x41b000~0x41bfff
;        mov dword [0x3fd02000+0x1b*8], 0x41b000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x1b*8+4], 0
        
; PTE[0x1c] (0x41c000~0x41cfff) map into 0x41c000~0x41cfff
;        mov dword [0x3fd02000+0x1c*8], 0x41c000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x1c*8+4], 0
        
        
; PTE[0x1d] (0x41d000~0x41dfff) map into 0x41d000~0x41dfff
;        mov dword [0x3fd02000+0x1d*8], 0x41d000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x1d*8+4], 0
        
; PTE[0x1e] (0x41e000~0x41efff) map into 0x41e000~0x41efff
;        mov dword [0x3fd02000+0x1e*8], 0x41e000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x1e*8+4], 0
        
; PTE[0x1f] (0x41f000~0x41ffff) map into 0x41f000~0x41ffff
;        mov dword [0x3fd02000+0x1f*8], 0x41f000 | PG_P | P_USER | PG_W
;        mov dword [0x3fd02000+0x1f*8+4], 0

        
; virtual address(0x00400000~0x00bfffff        )
; -> (map into)
; physical address(0x00400000~0x00bfffff)

        push 0                                            ; nxe flages
        push PG_P|PG_USER|PG_W                ; page attribute        
        push 0x00400000                              ; physical page
        push 0xbff-0x400                             ; size of pages(0x00400000~0x00bfffff)
        push 0x3fd02000                                ; pte base of va(0x00400000)
        call init_pae4K_pte                             ; init_pae4K_pte()
        
        
        ret


        
        

; init_pae4K_pte(pte_base, size, physcial_page, attribute, nxe)        
init_pae4K_pte:
        push ebp
        mov ebp,esp

        mov ebx, [ebp+8]                        ; pte_base
        mov edi, [ebp+16]                        ; physcial_page
        mov eax, [ebp+20]                        ; attribute
        mov edx, [ebp+24]                        ; NXE flags
        
        mov esi, [ebp+12]                        ; size
        
        xor ecx, ecx
        or edi, eax                                ; physcial page | attribute
        shl edx,31                                 ; NXE for pte[63]
        
init_pae4K_pte_l1:        
        test esi, esi
        jz init_pae4K_pte_done
        
        mov dword [ebx+ecx*8], edi                ; pte[31:0]
        mov dword [ebx+ecx*8+4], edx              ; pte[63:0]
        
        add edi, 0x1000                             ; next 4K-page
        inc ecx
        dec esi
        jmp init_pae4K_pte_l1
               
init_pae4K_pte_done:               
        mov esp, ebp
        pop ebp
        ret 20


注释部分用颜色标出

1、看一看 0x8010 0000(virtual address)是如何映射到 phyiscal address 的(2M page)

0x80100000 分解为:

PDPT   PDT               PT                   offset
-------------------------------------------
10      000000000      10000000         XXX


即: PDPT[2] + PDT[0] + offset (2M)


(1) PDPT 的地址
  CR3 装载的是 PDPT(page directory pointer table)的地址,它的值是 0x3fc00000

因此: 0x80100000 的 PDPE (page directory pointer entry)是 PDPT[2]
      PDPT[2] = 0x3fc00000 + 2*8

即代码中的:
       mov dword [PDPE_BASE+16], 0x3fc02000 | PG_P
       mov dword [PDPE_BASE+16+4], 0
---------------------------------------------------------------
PDPE[31:0]     = 0x3fc02000 | PG_P
PDPE[63:32]   =  0x00000000

此处:PDT 基址被设为: 0x3fc02000




(2) PDT (page directory table)的地址

  由 (1) 步所得,PDT 的地址在 0x3fc02000 处,来看一看 0x80100000 的 PDE 是多少

     PDE = PDT[0] = 0x3fc02000 处

代码中:
;----------------------kernel's 2M page -----------------------
; 0x80000000~0x801fffff(virtual address) map into physical page(0x30000000~0x301fffff)
mov dword [0x3fc02000], 0x30000000 | PG_P | PG_PS | PG_W
mov dword [0x3fc02004], 0


因此:0x80100000 的 PDE 是:

PDE[31:0]        =  0x30000000 | PG_P | PG_PS | PG_W
PDE[63:32]      =  0x00000000
---------------------------------------------------------------
代码中,开启了 2M pgae 结构。因此,0x80100000 将被直接映射到 0x30000000(2M page)上

即,标志中的 PG_PS 标志位。

在此,PDE 也就等于它的 PTE 结构。




2、再来看一看 0x00404000(virtual address)是如何映射的(4K page)

0x00404000 分解为:

PDPT   PDT             PT                offset
-----------------------------------------
00      000000010    000000100      XXX


即:   PDPT[0] + PDT[2] + PT[4] + offset(4K)


(1) PDPE 的值
  由上面得 PDE = PDPT[0],
因此,在 0x3fc00000 地址上

代码中:
mov dword [PDPE_BASE], 0x3fc01000 | PG_P
mov dword [PDPE_BASE+4], 0

因此,PDPE[31:0]       = 0x3fc01000 | PG_P
        PDPE[63:32]     = 0


(2) PDE 的值
  由 (1) 步得,PDE = PDT[2],PDT 的地址在 0x3fc01000 上

因此,PDE = PDT[2] = 0x3fc01000 + 2*8

代码中:

; PDE[2](00400000~xxxxxxxx): r/w=1, u/s=1,p=1
mov dword [0x3fc01000+2*8], 0x3fd02000 | PG_P | PG_USER | PG_W
mov dword [0x3fc01000+2*8+4], 0
------------------------------------------------------------

PDE[31:0]     = 0x3fd02000 | PG_P | PG_USER | PG_W
PDE[63:32]    = 0

即,page table 地址在 0x3fd02000 上




(3) PTE 的值 
 PTE 的值为 PT[4]。由上可得 PT 为 0x3fd02000

因此,PTE 的值应为:0x3fd02000 + 4*8 上

代码中:

; PTE[4] (0x404000~0x404fff) map into 0x404000~0x404fff
; mov dword [0x3fd02000+4*8], 0x404000 | PG_P | P_USER | PG_W
; mov dword [0x3fd02000+4*8+4], 0


(4) page 的值
 由 (3) 得,0x00404000 最终映射到 0x00404000 page 上




在蓝色注释部分,是一条条写的方式,若从 0x00400000 ~ 0x00bfffff 一条条写映射表,几乎是不可能的事情

所以:定了一个 init_pae4K_pte() 函数,自动完成填写从 0x400000 ~ 0xbfffff 映射表的填写

[ 本帖最后由 mik 于 2009-7-19 19:49 编辑 ]

论坛徽章:
0
发表于 2009-07-30 23:40 |显示全部楼层
12、开启 long mode, 让 kernel 运行在 64 bit 模式

  下面,我们要进入 x64os 最终的模式 long mode - 64 bit 模式。

  在 64 位的 OS 中,kernel 一定是运行在 64 bit 模式,而应用程序可以选择进入 compatibility 模式还是 64  bit 模式


下面是改写过的 kernel.asm 源码 (在 1 楼的附件中有)

%include "x64os.inc"


        org 0x6000

        bits 16

        jmp code16_entry


cursor dd 0xb8000


%include "legacy.inc"

%include "long.inc"

        
code16_entry:
        mov ax,cs
        mov ds,ax
        mov es,ax
        mov ss,ax
        mov sp,0x6000
        
        cli

        lidt [idt]                        ; load from ds:[idt] into idtr
        lgdt [gdt]                        ; load from ds:[gdt] into gdtr


        mov eax,cr0
        bts eax,0                         ; CR0.PE = 1
        mov cr0,eax                        ; enable protected mode


        jmp dword kernel_cs_sel:code32_entry
        
message db 'running at protected mode...',0
        
        bits 32
        
code32_entry:        
        mov ax,ds0_sel
        mov ds,ax
        mov es,ax
        mov ax,ss0_sel
        mov ss,ax
        mov esp, 0x7ff0


        sti

        call clear
        lea esi,[message]
        push esi
        push dword 0
        push dword 0
        call display_message
        
        cld
        mov edi,0x10000
        lea esi,[next]
        mov ecx, kernel_end-next
        rep movsb


        push dword 0x10000
        ret

        
; 0x10000:
next:
        mov ax, tss1_sel
        ltr ax
        
        call init_legacy_page
        
        mov eax, PDPE_BASE             ; legacy PAE-mode PDPE base address
        mov cr3, eax                    ; page structure base
        
        mov eax, cr4
        bts eax, 5                        ; CR4.PAE = 1
        mov cr4, eax
        
        mov eax, cr0
        bts eax, 31                        ; CR0.PG = 1
        mov cr0, eax
        
        
        mov eax,0x80000000
        cpuid
        cmp eax,0x80000000                        ; is support externed feature?
        jbe legacy_mode
        mov eax,0x80000001                         ; yes,test externd feature
        cpuid
        bt edx,29                                     ; is support long mode?
        jnc legacy_mode
        
        ; support long mode
        
        ;-------------------------------------------
        ; set long mode system data structure
        ;-------------------------------------------
        
        cli
        
        mov eax, cr0
        btr eax, 31                                        ; disable page
        mov cr0, eax
        
        call init_long_page        
        
        mov ecx, 0xc0000080                        ; efer registe address
        rdmsr                                          ; read into edx:eax
        bts eax,8                                      ; enable long mode
        wrmsr                                          ; write into efer with edx:eax
        
        mov eax, PML4_BASE                        ; long mode page sturcture
        mov cr3, eax
        
        
        mov eax,cr0
        bts eax, 31                            ; enable page
        mov cr0,eax                            ; active long mode


; Now,here processor running at long mode
; but it's compatibility mode with legacy GDT and legacy IDT, legacy TSS
; CS is legacy protected mode's code segment selector

        


        
        ; jmp for enter long mode - 64bit mode
        ; and selector index descriptor at legacy GDT

        jmp jmp_kernel64_cs_sel:code64_entry
        
        
legacy_mode:
        mov ax, ds3_sel|0x03
        mov ds, ax
        mov es, ax
        
        push (ds3_sel) | 0x03                ; stack 3
        push 0x004fff00                                ; stack 3
        push (user_cs_sel)|0x03
        push os_main
        retf
        
done:

        jmp $
        


exception0 db 'exception: divide by zero...',0


os_main:
        jmp os_next
os_msg db 'x64os running with pageing...',0
os_next:
        push os_msg
        push 0
        push 2
        call display_message
        
        ;mov dword[0x5000],0
        
        

        jmp $


%include "legacy_routin.asm"

%include "long_routin.asm"        
        
        
;---------------------------------------
; 64bit code
;---------------------------------------        
        
        
        bits 64



        
code64_entry:

        ; Now! here processor runing at 64bit mode        
        ; but the 64bit environment be not initailiztion
        ; So! first set 64bit environment

        mov rsp, 0xffffffffffd00000

        lgdt [gdt64_table]
        lidt [idt64_table]
        
        mov ax, tss64_sel
        ltr ax
        
        mov rax, 0x20000
        mov cr3,rax
        
        sti
        
        jmp code64_next
        
code64_msg db 'Now x64os running on 64bit!!!   ',0

code64_next:

        mov r8, 4
        mov r9, 0
        lea r10, [code64_msg]

        call print_str
               

        push data64_sel | 0x3                         ; ss selector: level-3
        push 0x004fff00                                ; rsp
        push user64_cs_sel | 0x3                      ; user code: level-3
        push os_main64                                 ; rip
        retf


        os64_msg db 'shell64 > ', 0


os_main64:

; Now, processor running at CPL = 3 (64 bit mode)
;

        mov r8, 6
        mov r9, 0
        lea r10, [os64_msg]
        call print_str
        

        jmp $
        
kernel_end:


1、蓝色部分是进入 long mode 前的设置工作
2、红色部分是进入 long mode (64 bit ) 后的处理

论坛徽章:
0
发表于 2009-07-31 00:49 |显示全部楼层
----- 接上



一、进入 64 位模式的准备工作



1、检测 processor 是否支持 long mode


        mov eax,0x80000000
        cpuid
        cmp eax,0x80000000                        ; is support externed feature?
        jbe legacy_mode
        mov eax,0x80000001                         ; yes,test externd feature
        cpuid
        bt edx,29                                        ; is support long mode?
        jnc legacy_mode

检测 processor 是否支持 long mode,首先要先检测 processor 是否支持扩展功能
(1)使用功能号 0x80000000 检测 processor,若结果大于 0x80000000 则说明支持扩展功能
(2)使用扩展功能号 0x80000001 检测 processor,从结果的第 29 位检测 processor 是否支持 long mode




2、开启 long mode


        ; support long mode
        
        ;-------------------------------------------
        ; set long mode system data structure
        ;-------------------------------------------

        
        cli
        
        mov eax, cr0
        btr eax, 31                                        ; disable page
        mov cr0, eax
        
        call init_long_page        
        
        mov ecx, 0xc0000080                        ; efer registe address
        rdmsr                                         ; read into edx:eax
        bts eax,8                                     ; enable long mode
        wrmsr                                         ; write into efer with edx:eax
        
        mov eax, PML4_BASE                        ; long mode page sturcture
        mov cr3, eax
        
        
        mov eax,cr0
        bts eax, 31                                        ; enable page
        mov cr0,eax                                        ; active long mode

; Now,here processor running at long mode
; but it's compatibility mode with legacy GDT and legacy IDT, legacy TSS
; CS is legacy protected mode's code segment selector

        
        
        ; jmp for enter long mode - 64bit mode
        ; and selector index descriptor at legacy GDT

        jmp jmp_kernel64_cs_sel:code64_entry


(1)关闭 paging 模式

mov eax, cr0
btr eax, 31   
mov cr0, eax
------------------------
  这一步是必须的,因为当前 processor 正处于 32 位保护模式,要进入 long mode 必须先把 paging 模式关闭




(2)设置 long mode 的 paging 数据结构

  代码中,调用 init_long_page() 去设置 long mode 下的 paging 数据结构。

long mode 下的 paging 启用了 5 级转换结构:

CR3 --> PML4 --> PDPT --> PDT --> PT --> page  +  offset

具体的 init_long_page() 的代码在 long_routin.asm 文件里

这里 init_long_page() 里的代码很简单,先做了 long mode 部分的映射,然后接到原来的 32 位保护模式的 paging 结构


因此,仅仅不同的是:

      virtaul address                                                physical address
-------------------------------------------------------------------------------
0xffffffff80000000 ~ 0xffffffff801fffff                       0x30000000 ~ 0x301fffff
0xffffffffffc00000 ~ 0xffffffffffdfffff                         0x3fc00000 ~ 0x3fdfffff

将 virutal address 扩展为 64 位,物理地址与 legacy 保持不变




(3)打开 long mode 模式

mov ecx, 0xc0000080    
rdmsr      
bts eax,8   
wrmsr      
------------------------------------------
  置 EFER 寄存器的第 8 位为 1,EFER 寄存器的地址为 0xc0000080

EFER.LME = 1

  在这种情况下,processor 的 long mode 已经是 enable 了,但是此时仍未激活 active
即:
  EFER.LMA = 0





(4)激活 long mode


mov eax, PML4_BASE   
mov cr3, eax

mov eax,cr0
bts eax, 31     
mov cr0,eax
-------------------------------------------------------
  激活前,应先设置好 CR3,即 PML4T 的基地址。
  
  重新打开 paging,即 CR0.PG = 1

当:
  EFER.LME = 1 时,CR0.PG = 1 就激活了 long mode
即:
  此时,EFER.LMA = 1


注意:
  当 EFER.LMA = 1 时(即 long mode 被激活)的瞬间, processor 处于 compatibility 模式,那是因为 CS 还是原来 32 位保护模式下的 CS 值。
  processor 所处于的执行环境还是原来 32 位保护模式下的环境。



(5)跳入 64 bit 模式(脱离 compatibility 模式)

jmp jmp_kernel64_cs_sel:code64_entry
------------------------------------------------------
  执行一条 far jmp 指令进入 64 bit 模式

  这里的 jmp_kernel64_cs_sel 是原来 32 位保护模式下的 GDT 结构里的 code segment selector


因此:
  在 32 位保护模式的 GDT 里必须有一个 code segment descriptor 是 64 位 code segment descriptor
  
     在这种情况下,也就是说:32 位保护模式下的 GDT 必须有 32 位的 code segment descriptor 还必须为进入 64 位模式的 64 位 code semgent descriptor。






二、进入 64 位模式后的设置工作


1、 stack pointer 的设置

mov rsp, 0xffffffffffd00000
--------------------------------------------
  将 64 位 kernel 的 rsp 设为 0xffffffffffd00000


2、加载新的 64 位的 GDT 和 IDT

lgdt [gdt64_table]
lidt [idt64_table]
--------------------------------------------------
  分别重新加载 64 位的 GDT 和 IDT 表



3、 加载 TR

mov ax, tss64_sel
ltr ax
-----------------------------------------
  加载必须的 64 位 TSS 结构的基址到 TR 寄存器。






三、 最终转入 ring3 的用户程序

        push data64_sel | 0x3                             ; ss selector: level-3
        push 0x004fff00                                   ; rsp
        push user64_cs_sel | 0x3                        ; user code: level-3
        push os_main64                                    ; rip
        retf


        os64_msg db 'shell64 > ', 0
os_main64:
        mov r8, 6
        mov r9, 0
        lea r10, [os64_msg]
        call print_str
        

        jmp $

  使用 far ret 指令转入到 user 级代码。

论坛徽章:
0
发表于 2009-08-10 23:52 |显示全部楼层
13、加入定时中断机制,为以后的 x64os 任务切换做准备


  今天,我为 x64os 加入定时中断机制,实现定时中断,用来进行任务切换

  下面是一个定时中断的举例应用。在 1 楼的源码里都完整的。




1、设置 8259A  PIC 芯片工作模式


init8259A:
; master 8259A
        mov al, 0x11                        ; ICW1
        out 0x20,al
        jmp n1
n1:
        nop
        mov al, 0x20                        ; ICW2
        out 0x21,al
        jmp n2
n2:
        nop
        mov al, 0x04                        ; ICW3               
        out 0x21,al
        jmp n3
n3:
        nop
        mov al,0x01                            ; ICW4
        out 0x21,al
        jmp n4
        
        
; slave 8259A
n4:
        nop
        mov al, 0x11                ; ICW1
        out 0xa0,al
        jmp n5
n5:
        nop
        mov al, 0x30                ; ICW2
        out 0xa1, al
        jmp n6
n6:
        nop               
        mov al, 0x02                ; ICW3
        out 0xa1,al
        jmp n7
n7:
        nop
        mov al, 0x01                ; ICW4
        out 0xa1,al

        ret

    实际只需对 8259A 主片进行设置就行了,在 bios 启动自检阶段, bios 已经将 8259A 芯片工作模式设置好了。

  因此,这里主要的目的是更改 8259A 的中断向量号,bios 将 8259A 的 IRQ0 的中断向量号设为 0x08,而 IRQ0 接 8253 定时器的通道 0,也就是原来的定时中断向量被设为 0x08。也将与 #DF 异常的中断向量号重叠了。
  
  所以,这里将 IRQ0 的中断向量改为 0x20,那么定时中断的 vector 就设为了 0x20




2、设定 8253 定时器的工作模式

init8253:
    mov al, 0x34
    out 0x43, al
    mov ax, 0x03e9      ; 1ms 中断一次
    out 0x40, al
    mov al, ah
    out 0x40, al
    ret


 同样,我们只需设想要的东西就行了,bios 已经预先设置好了工作方式。

  8253 的频率是 1.19318MHz = 1193180 Hz,初始状态是 1193180/65536 = 18.2 次中断,也就是说,每秒种产生 182.2 次时间中断。

  那么这里,我设置为 1ms 中断一次,即每次约中断 1000 次,也就是 1193180/1191=1001次 = 0x03e9





3、定时中断处理例程的示例

interrupt_timer:

        jmp timer_next


timer_str        db '-\|/',0

timer_next:
        mov ah, 0x0c
        cmp r15, 4
        jb timer_nnext
        xor r15,r15

timer_nnext:        
        mov al, byte [timer_str + r15]
        mov word [0xb8000+10*80*2+40*2], ax

        inc r15
        
        mov al, 0x20                             ; EOI
        out 0x20, al
        iretq


  这个定时中断例程很简单,不断地执行中断例程时,将不断跳变地在屏幕中间组合成红色 '*' 显示。


  效果图见,1 楼里在 vmware 里运行的结果。

              


4、cpu  权限级别的切换
 
  这里已经体现出 cpu 权限级别的切换过程,

  在 ring3 级里,不断执行 jmp $ 指令(死循环)

  然后,发生定时中断时,转入 ring0 的中断例程,

  ring3 (os_main 的死循环) -->  ring0 (中断服务例程)




这里就出现了任务切换机制的原理:
  
  shell 等待 --> 定时中断 ---> task switch 例程  --->  program A







:wink:

[ 本帖最后由 mik 于 2009-8-10 23:57 编辑 ]

论坛徽章:
0
发表于 2009-08-28 00:45 |显示全部楼层
14、增加系统调用,并使用 int 30 进行系统调用



  今天我为 x64os 添加一个系统调用,这个系统调用既可使用 int 0x30 进行系统调用,也可使用 sysenter 指令快速调用。

  int 0x30 功能调用类似 linux 的 int 0x80 功能调用。


int 0x30 的调用流程:

int 0x30   ---->  sys_services()  ---->  syscall_table  ----> write_video()  (系统调用)





1、首先在 IDT 新增一个 0x30 号的 interrupter gate-descriptor

    这个 gate-descriptor 定义如下

sys_caller  dw sys_services                        ; vector 0x30
             dw kernel64_cs_sel                        
             db 1                                      ; IST1 = TSS_IST[0]
             db 0xee                                   ; RPL = 3
             dw 0
             dq 0

 这个 interrupte gate-descriptor 使用的 IST(interrupt stack table),定义为 1,也就是使用了 IST1 指针
 
 这个 handler 指向 sys_services 例程





2、定义一个 syscall table(系统调用表)
 
如下:


syscall_table:
        dq write_video
        dq 1
        dq 2
syscall_table_end:

  这个表很简单,只是定义了一个 0 号系统调用:write_video()




3、 0 号系统调用的实现

很简单:
write_video:
     mov word [video_current], bx
     ret

这个 0 号功能,只是向 video_current 写一个 word 值







4、 sys_services() 的实现

sys_services:
        push rcx
        push rdx
        mov rax, [rax + syscall_table]
        call rax

        mov rbx, IST_rsp1
        lea rax, [rsp+7*8]
        xor rax, rbx
        jz do_int30

        mov rax, [Vendor_ID]
        cmp rax, INTEL_ID                                 ; is intel ?
        jz intel_sys_services_done
        cmp rax, AMD_ID
        jz amd_sys_services_done                        ; is amd ?
;        jmp sys_services_done                             ; unknown ?

intel_sys_services_done:

        pop rdx
        pop rcx
        
        test r8,0x200                                   ; rflags.IF == 1?
        jz intel_sys_services_exit
        sti
intel_sys_services_exit:        
        db 0x48
        db 0x0f
        db 0x35
        ;sysexit
        
amd_sys_services_done:
        sysret

do_int30:
        pop rdx
        pop rcx
        iretq

  这个 sys_services() 的实现也很简单,

(1)在 syscall_table 里索引找到 wirte_video() ,然后直接调用 0 号系统调用

(2)判断这个 sys_services() 是由 int 0x30 调用的还是 sysenter 指令调用的?
  由于,sys_services() 既可以由 int 0x30 进行调用,也可以由 sysenter 指令调用,所以要判断到底是由哪种途径进入的。由于,int 0x30 的 rsp 使用的是 IST1 指针,所以,可以从 IST1 判断出是否从 int 0x30 进入

(3)write_vide() 调用完成后返回到 sys_serviecs(),再回到 int 0x30 或 sysenter 指令执行处

(4)sysenter 指令执行时,processor 会自动清 IF 标志,因此,由 sysexit 返回时,应根据情况置 IF 标志位。
   r8 是由调用者传递过来的原 rflags 寄存器值。

论坛徽章:
0
发表于 2009-08-28 00:56 |显示全部楼层
15、使用 sysenter/sysexit 进行系统调用



  接着,实现从 sysenter 快速进入系统调用。


使用 sysenter 指令的流程:


sys_services_enter()  ---->   sys_services()   ----> syscall_table   -----> write_video()  ----> sysexit

  除了从 sys_services_enter() 进入外,其它都和 int 0x30 方式是一样的。


sys_services_enter:
        pushfq
        push rax
;        mov rax, [Vendor_ID]
;        cmp rax, INTEL_ID                                        ; is intel ?
;        jz intel_sys_services_enter
;        cmp rax, AMD_ID
;        jz amd_sys_services_enter                        ; is amd ?
;        jmp sys_services_enter_done                                ; unknown ?


intel_sys_services_enter:
        pop rax
        pop r8                                                ; pop rflags
        
        mov rcx, rsp
        lea rdx, [sys_services_enter_done]

        sysenter
        
amd_sys_services_enter:
        pop rax
;        mov rsp, sysenter_rsp
        syscall
sys_services_enter_done:
        ret
sys_services_enter:
        pushfq
        push rax
;        mov rax, [Vendor_ID]
;        cmp rax, INTEL_ID                                   ; is intel ?
;        jz intel_sys_services_enter
;        cmp rax, AMD_ID

;        jz amd_sys_services_enter                        ; is amd ?
;        jmp sys_services_enter_done                       ; unknown ?


intel_sys_services_enter:
        pop rax
        pop r8                                                ; pop rflags
        
        mov rcx, rsp
        lea rdx, [sys_services_enter_done]

        sysenter
        
amd_sys_services_enter:
        pop rax
;        mov rsp, sysenter_rsp
        syscall
sys_services_enter_done:
        ret



(1)pushfq 保存原来的 rflags 值

(2)保存返回值
       mov rcx, rsp                                        ; 返回 rsp
       lea rdx, [sys_services_enter_done]          ; 返回 rip

(3)sysenter 指令进入






最后,看一看,如何使用 int 0x30 和 sys_services_enter() 进行系统调用


os_main64:
        mov r8, 6
        mov r9, 0
        lea r10, [os64_msg]
        call print_str

        mov rax, 0
        mov bx, 0x0f00
        mov bl, '#'
        
        int 0x30
        
        mov rax, 0
        mov bx, 0x0f00
        mov bl, '#'
        
        call sys_services_enter

     jmp $


1、  rax 放入功能号,rbx 传入参数,然后执行:int 0x30

2、 和 int 0x30 同样的参数处理方式,执行 call sys_services_enter() 进入点

论坛徽章:
0
发表于 2009-08-30 16:18 |显示全部楼层
16、将用 sysenter/sysexit 去掉,改用 syscall/sysret 进行系统调用

  使用 sysenter/sysexit 将不能在 AMD 的机器上 64 位模式下运行。因为:AMD 不支持 sysenter/sysexit 指令在 64 位执行。
所以,我将 sysenter/sysexit 改为 syscall/sysret,达到 64 位下 AMD/Intel 的机器上都能运行。


改动代码如下:


1、 置 EFER.SCE = 1(syscall enable)

        mov ebx, edx
        mov ecx, 0xc0000080                        ; efer registe address
        rdmsr                                        ; read into edx:eax
        bts eax,8                                      ; enable long mode
        bt ebx, 11                                ; is support syscall/sysret
        jnc nnn
        bts eax, 0                                  ; enable syscall/sysret

代码先测试是否支持 syscall/sysret 指令,然后置 EFER[0] 为 1,即:EFER.SCE = 1



2、 简化 sys_services_enter()

sys_services_enter:
     syscall
     ret

sys_services_enter() 仅仅是个中转,直接 syscall





3、 sys_services() 改动较大

sys_services:

  mov rbx, IST_rsp1
  lea rdx, [rsp+5*8]    ; 5 params
  xor rdx, rbx
  jz do_int30


;------------------------------
; enter from syscall instruction
;------------------------------

  mov rbp, rsp     ; save old_rsp

  mov rsp, sysenter_rsp
  push rbp


;------------------------------
; call syscall_table function
;------------------------------

  mov rax, [rax + syscall_table]
  call rax

  pop rbp
  mov rsp, rbp      ; restore old_rsp

  db 0x48
  db 0x0f
  db 0x07
  
  ;sysret


;----------------------------------
; enter from int 0x30
;-----------------------------------

do_int30:

  mov rax, [rax + syscall_table]
  call rax

  iretq



1、syscall 指令不会自动传递所需的 stack pointer(rsp),因此,先保存原来的 rsp 值(ring 3),然后设置为 ring 0 级的 rsp 值。

2、根据功能号,调用系统服务

3、返回之前,要先恢复原来的 rsp 值(ring 3)

4、执行 sysret 指令

  由于,nasm / yasm 不能自动识别是返回到 64 位模式还是 compatibility 模式,因此,需要硬编码

48 0f 07 也就是 sysret 指令(返回到 64 位模式)


5、而经由于 int 0x30 途径进入系统服务例程的,调用完系统调用后 iretq 返回



6、最后,看一看 init_syscall() 例程


init_syscall:

  jmp init_amd_syscall

  mov rax, [Vendor_ID]
  cmp rax, INTEL_ID     ; is intel ?
  jz init_intel_syscall
  cmp rax, AMD_ID
  jz init_amd_syscall     ; is amd ?
  jmp init_syscall_done    ; unknown ?

init_intel_syscall:
  xor edx, edx
  mov eax, sysenter_cs_sel     ; sysenter_cs
  mov ecx, 0x174             ; msr_sysenter_cs's address
  wrmsr

  mov rax, sys_services        ; sys_services routin
  mov rdx, rax
  shr rdx, 32
  mov ecx, 0x176             ; msr_sysenter_rip's address
  wrmsr

  mov rax, sysenter_rsp        ; sysenter_rsp
  mov rdx, rax
  shr rdx, 32
  mov ecx, 0x175             ; msr_sysenter_rsp's address
  wrmsr

  jmp init_syscall_done




init_amd_syscall:

;-------------------------------------------------------
; Note: the sysret instruction: not change SS.RPL to 3
;       So: MSR_STAR.SYSRET_CS.RPL must be to set 3 !!!!
;-------------------------------------------------------

  mov edx, sysenter_cs_sel | ((sysret_cs_32_sel | 0x3) << 16)
  xor eax, eax
  mov ecx, MSR_STAR        ; MSR_STAR's address
  wrmsr                 ; write edx:eax into MSR_STAR register

  mov rax, sys_services
  mov rdx, rax
  shr rdx, 32
  mov ecx, MSR_LSTAR
  wrmsr

init_syscall_done:
  ret



1、最重要的一点:

  mov edx, sysenter_cs_sel | ((sysret_cs_32_sel | 0x3) << 16)

代码将设:
(1)、MSR_STAR[63:48] 为 ring 3 的 32 位 code segment descriptor selector
(2)、MSR_STAR[47:32] 为 ring 0 的 64 位 code segment descriptor selector

MSR_STAR[63:48] 为 sysret 指令提供返回 cs selector
MSR_STAR[47:32] 为 syscall 指令提供 kernel cs selector



2、MSR_LSTAR 将设置为 sys_services() 例程的地址



有关 syscall/ sysret 指令使用的详情,请参阅:http://linux.chinaunix.net/bbs/thread-1052389-4-1.html 贴子

论坛徽章:
0
发表于 2009-09-07 00:37 |显示全部楼层
17、内存管理1 ---- 检测系统内存


   在进一步作 os 内存管理之前,先做检测系统内存数量的工作


1、在实模式下,使用 int 15 进行获取系统内存


mem_rangs        dd        0
mem_rang_buf times (20*10) db 0
mem_total        dd 0

get_mem:
        mov ebx, 0
        mov edi, mem_rang_buf

get_mem_loop:        
        mov eax, 0xe820
        mov ecx, 20
        mov edx, 0x534d4150                ; 'SMAP'
        int 0x15
        jc get_mem_failure
        xor eax, 0x534d4150                ; 'SMAP'
        jnz get_mem_failure
        add edi, ecx
        test ebx, ebx
        jz get_mem_done
        mov dword [mem_rangs], ebx
        jmp get_mem_loop
        
get_mem_failure:
        mov edi, mem_rang_buf
        mov ecx, 20*10
        xor ax, ax
        rep stosb
        
get_mem_done:        
        ret



2、 打印 memory address 范围的例程

print_mem:
        push r12
        push r13
        push r14
        push r15
        jmp print_mem_next
        
mem_base db 'BaseAddr:', 0                                ; qword
mem_length db 'Length:', 0                                ; qword
mem_type db 'Address type:', 0                        ; dword
mem_attribute db 'Extended Attributes:', 0                ; dword

mem_totals db 'memory total: ',0

print_mem_next:
        mov rax, 1
        mov rdi, 1
        mov rsi ,0
        int 0x30
        lea rdi, [mem_base]
        call print_str
        mov rax, 1
        mov rdi, 1
        mov rsi, 20
        int 0x30
        lea rdi, [mem_length]
        call print_str        
        mov rax, 1
        mov rdi, 1
        mov rsi, 40
        int 0x30
        lea rdi,[mem_type]
        call print_str
        
        mov r13, [mem_rangs]
        mov r12, 2
        xor r14, r14
        
print_mem_loop:        
        mov rax, 1
        mov rdi, r12
        mov rsi, 0
        int 0x30
        mov rdi, [mem_rang_buf+r14]
        mov rsi, 64
        call dump_hex
        mov rax, 1
        mov rdi, r12
        mov rsi, 20
        int 0x30
        mov rdi, [mem_rang_buf+r14+8]
        mov rsi, 64
        call dump_hex
        mov rax, 1
        mov rdi, r12
        mov rsi, 40
        int 0x30
        mov rdi, [mem_rang_buf+r14+16]
        mov rsi, 32
        call dump_hex
        
        mov r8, [mem_rang_buf+r14]
        mov r9, [mem_rang_buf+r14+8]
        movsx r10, dword [mem_rang_buf+r14+16]
        cmp r10, 4
        jge print_mem_loop_next
        add r8, r9
        shr r8, 20
        mov [mem_total], r8d
print_mem_loop_next:
        
        inc r12
        add r14, 20
        dec r13
        jnz print_mem_loop

print_memory_total:
        mov rax, 2
        int 0x30
        mov rdi, rax
        add rdi, 2
        mov rsi, 0
        mov rax, 1
        int 0x30
        
        lea rdi, [mem_totals]
        call print_str
        
        movsx rdi, dword [mem_total]
        call dump_dec
        
        mov rdi, 'M'
        or rdi, 0x0f00
        mov rax, 0
        int 0x30
        
        pop r15
        pop r14
        pop r13
        pop r12
        ret



3、再看一看打印十六进制数的例程

dump_hex:
        push r12
        push r13
        
        mov r12, rdi                        ; value


        mov rcx, rsi                        ; bits
        
        jmp print_hex_next
        
qword_hex_table db '0123456789ABCDEF',0
        
print_hex_next:        
        mov r10, r12
        sub rcx,4

        shr r10, cl
        and r10, 0x0f
        movzx rdi, byte [r10+qword_hex_table]
        or rdi, 0x0f00
        mov rax, 0
        int 0x30
        test rcx, rcx
        jz print_hex_done
        jmp print_hex_next

print_hex_done:
        pop r13
        pop r12
        ret

----------------------------------------------------
上面这个 dump_hex() 作用是打印出十六进制数,如:000000003FF00000



4、最后看一看打印十进制数例程


dump_dec:
        push rbp
        mov rbp, rsp
        jmp dump_dec_next
        
dec_table db '0123456789',0

dump_dec_next:
        mov rcx, 10
        mov rax, rdi
        
dump_dec_l1:        
        xor rdx, rdx
        div rcx
        push rdx
        test rax, rax
        jnz dump_dec_l1
        
dump_dec_do
        pop rdi
        movzx rdi, byte[rdi+dec_table]
        or rdi, 0x0f00
        mov rax, 0
        int 0x30
        cmp rbp, rsp
        jnz dump_dec_do
        
dump_dec_done:        
        mov rsp, rbp
        pop rbp
        ret


上面这个例程是打印出 10 进制数,如: 1024 M

程序,很简单,这就是使用 assembly 的强大之一。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP