如何获取RAM大小,bootloader

时间:2016-08-18 18:02:36

标签: assembly x86 ram bootloader osdev

我会问如何获取引导加载程序中的总RAM大小和可用RAM大小。截至目前,我知道如何获得更低的内存。但由于某种原因,我无法将其打印到屏幕上,因为它保存在ax寄存器中。这就是我到目前为止所做的:

[BITS 16] ; BootLoader always starts 16 BIT Moded

    jmp main_bootloader ; Jump to Main Bootloader

     ;************** INITALIZED VARIABLES *********************;
      string db 'BoneOS Loading . . .', 0x0
      string2 db 'Starting of 16Bit Bootloader' , 0x0
      press_to_cont db 'Press any key to continue . . .' , 0x0
      carry_flag_err db ' CARRY FLAG HAS BEEN SET! ERROR ' , 0x0
      magic_number equ 0x534D4150
      limit dw 0
      base  dw 0
      low_memory dd 0
      answer resb 64
     ;*********************************************************;  

     ;******************** GDTs *****************************;


         null_descriptor :
            dd 0                ; null descriptor--just fill 8 bytes with zero
            dd 0 

        ; Notice that each descriptor is exactally 8 bytes in size. THIS IS IMPORTANT.
        ; Because of this, the code descriptor has offset 0x8.

         code_descriptor:           ; code descriptor. Right after null descriptor
            dw 0FFFFh           ; limit low
            dw 0                ; base low
            db 0                ; base middle
            db 10011010b            ; access
            db 11001111b            ; granularity
            db 0                ; base high

        ; Because each descriptor is 8 bytes in size, the Data descritpor is at offset 0x10 from
        ; the beginning of the GDT, or 16 (decimal) bytes from start.

         data_descriptor:           ; data descriptor
            dw 0FFFFh           ; limit low (Same as code)
            dw 0                ; base low
            db 0                ; base middle
            db 10010010b            ; access
            db 11001111b            ; granularity
            db 0                ; base high

        end_of_gdt: 
            toc: 
                dw end_of_gdt - null_descriptor - 1     ; limit (Size of GDT)
                dd null_descriptor          ; base of GDT   

        load_gdt:
            lgdt [toc]

        .done:
            ret


     ;*********************************************************;

     ;*************** LABELS FOR MAIN **************************;
        print_char_boot:
            mov ah, 0Eh ; Code For BIOS To Print Char 0Eh

        .repeat:
            lodsb ; Load Byte From SI Register
            cmp al, 0 ; Compare AL With 0 If so Done
            je .done 
            int 10h ; Call Interupt. Checks AH Register for code 0EH = Print char
            jmp .repeat ; Loop Back

        .done:
            ret ; Return to previous code


        print_new_line:
               mov al, 0    ; null terminator '\0' 
               ;Adds a newline break '\n'
               mov ah, 0x0E
               mov al, 0x0D
               int 0x10
               mov al, 0x0A 
               int 0x10
               ret

        get_pressed_key:
            mov ah, 0
            int 0x16  ;BIOS Call. Key goes to al register
            ret 
        GET_RAM_SIZE:

        reboot:
            mov si, press_to_cont
            call print_char_boot
            call get_pressed_key ; Gets Pressed Key

            int 19h ;Reboot
            ret

        enable_A20: ; Enabling A20 Line For Full Memory
            cli ; Stop Interupts before doing so

            call    a20wait ; a20wait call
            mov     al,0xAD ; Send 0xAD Command to al register
            out     0x64,al ; Send command 0xad (disable keyboard).

            call    a20wait ; When controller ready for command
            mov     al,0xD0 ; Send 0xD0 Command to al register
            out     0x64,al ; Send command 0xd0 (read from input)

            call    a20wait2 ; When controller ready for command
            in      al,0x60 ; Read input from keyboard
            push    eax ; Save Input by pushing to stack

            call    a20wait ; When controller ready for command
            mov     al,0xD1 ; mov 0xD1 Command to al register
            out     0x64,al ; Set command 0xd1 (write to output)

            call    a20wait ; When controller ready for command
            pop     eax ; Pop Input from Keyboard
            or      al,2 ; Mov 0xD3 to al register
            out     0x60,al ; Set Command 0xD3

            call    a20wait ; When controller ready for command
            mov     al,0xAE ; Mov Command 0xAE To al register
            out     0x64,al ; Write command 0xae (enable keyboard)

            call    a20wait ; When controller ready for command
            sti ; Enable Interrupts after enabling A20 Line
            ret

                a20wait:
                    in      al,0x64 ; input from 0x64 port, goes to al register
                    test    al,2 ; compares al register with 2
                    jnz     a20wait ; If it is zero loop again
                    ret


                a20wait2:
                    in      al,0x64 ; input from 0x64 port, goes to al register
                    test    al,1 ; compares al register with 2
                    jz      a20wait2 ; If it is zero loop again
                    ret 


            get_lower_memory:
                clc ; Clears Carry Flag
                int 0x12 ; BIOS Call Request Lower Memory Size in KB
                jc .err ; If Carry Flag Has Been Set , the system its running on dosent support this
                jmp .done ; If Sucessfull ax register contains contiguous low memory in KB

            .err:
                times 5 call print_new_line ; Prints New Line
                mov si, carry_flag_err
                call print_char_boot 
                jmp .repeat

            .done:
            ret

        .repeat:
            jmp .repeat





;**************************************************************;


;*******************'MAIN' BOOTLOADER FUNCTION ****************;
main_bootloader:
    xor ax, ax
    mov ss, ax
    mov sp, 4096

    mov ax, 07C0h       ; Set data segment to where we're loaded
    mov ds, ax

    mov si, string ; si register usefull for lodsb command
    call print_char_boot ; Call print_char_boot label below
    call print_new_line ; Prints New Line
    mov si, string2
    call print_char_boot 
    times 2 call print_new_line
    ; Enable A20 Line
    call enable_A20

    call get_lower_memory ; Get Low Memory


    mov si,ax
    call print_char_boot 
    times 5 call print_new_line

    call reboot ; Reboot

    ;call null_descriptor


    jmp $ ; Infinite Loop 
    ;Bootloader gets infinite loop 
    ;Incase No Infinite Loop in Kernel

;****************************************************************;  


;************************* BOOTLOADER REQUIREMENTS **************;  
times 510 - ($ - $$) db 0 ; Has to be 512 bytes .. Repeats 510 byes to make it so
dw 0xAA55 ; BootLoader Sig. To Validate this is a bootloader
;

****************************************************************;

你可以在我的主要call get_lower_memory ; Get Low Memory看到,以获得低记忆。但我测试了印刷轴寄存器,屏幕上没有显示任何内容。我也不知道如何在系统中获得总和可用的内存。帮助会得到极大的赞赏!

1 个答案:

答案 0 :(得分:6)

虽然帖子正文中提到的问题更多的是关于打印寄存器的值而不是检测系统可用的内存,但我只是忠于标题问题,并提供一个示例如何检测系统内存映射。

作为奖励,提供了一个显示32位无符号整数作为十六进制数字的函数,以及支持占位符的非常原始的 print

检测内存并非易事,它需要完全了解已安装的硬件 1 ,如果没有它,则无法完成(请参阅OSDev上的Detecting memory)。 作为一个简单的例子,考虑一个aliased memory,如果没有任何相关和缓慢的方法,软件就无法检测到它。

我们已经承认必须与BIOS合作,我们可以看到16位实模式引导加载程序可以使用哪些服务。
上面提到的关于detecting memory的OSDev页面已经有一个专门用于标题目的的服务列表,可以参考。

我们将专注于Int 15/AX=E820h服务 它的用途是返回一个记忆范围列表及其描述 每次调用都返回下一个描述符,使用ebx来跟踪进度。寄存器ebx应视为不透明值 尽管 Ralf的布朗中断列表中有描述,描述符可以是24个字节长,因此最好使用该长度并最终检查ecx中返回的值以告知20/24字节描述符开。

一旦我们有了描述符列表,它们就可以被指定用于分配内存 2 的例程使用。
值得一提的是:

  1. 描述符有序。一些错误的BIOS可能会返回重叠区域(在这种情况下做出最保守的选择)。

  2. 即使订购了描述符,也可能存在间隙。没有报告没有内存映射的范围,这是标准孔的情况(范围从0a0000h到0fffffh)。

  3. 虽然报告了BIOS明确保留的区域(例如从0f0000h到0fffffh的阴影区域)。

  4. 在下面的示例中,描述符与非保留存储器 3 的总量一起打印在屏幕上。

    顺便说一句,您可以使用itoa16函数在EAX中打印32位值,假设您更改了屏幕上字符的打印方式。

    BITS 16
    
    ;Set CS to a known value
    ;This makes the offsets in memory and in source match 
    ;(e.g. __START__ is at offset 5h in the binary image and at addres 7c0h:0005h)
    
    jmp 7c0h:__START__
    
    __START__:
     ;Set all the segments to CS 
     mov ax, cs
     mov ds, ax
     mov es, ax
     mov ss, ax
     xor sp, sp
    
     ;Clear the screen
     mov ax, 03h
     int 10h
    
     ;FS will be used to write into the text buffer
     push 0b800h
     pop fs
    
     ;SI is the pointer in the text buffer 
     xor si, si 
    
     ;These are for the INT 15 service
     mov di, baseAddress                    ;Offset in ES where to save the result
     xor ebx, ebx                           ;Start from beginning
     mov ecx, 18h                           ;Length of the output buffer (One descriptor at a time)
    
     ;EBP will count the available memory 
     xor ebp, ebp 
    
    _get_memory_range:
     ;Set up the rest of the registers for INT 15 
     mov eax, 0e820h 
     mov edx, 534D4150h
     int 15h
     jc _error 
    
     ;Has somethig been returned actually?
     test ecx, ecx
     jz _next_memory_range
    
     ;Add length (just the lower 32 bits) to EBP if type = 1 or 3 
     mov eax, DWORD [length]
    
     ;Avoid a branch (just for the sake of less typing)
    
     mov edx, DWORD [type]         ;EDX = 1        | 2        | 3        | 4   (1 and 3 are available memory)
     and dx, 01h                   ;EDX = 1        | 0        | 1        | 0 
     dec edx                       ;EDX = 0        | ffffffff | 0        | ffffffff 
     not edx                       ;EDX = ffffffff | 0        | ffffffff | 0 
     and eax, edx                  ;EAX = length   | 0        | length   | 0 
    
     add ebp, eax
    
     ;Show current memory descriptor 
     call show_memory_range
    
    _next_memory_range:
     test ebx, ebx 
     jnz _get_memory_range
    
     ;Print empty line
     push WORD strNL 
     call print 
    
     ;Print total memory available 
     push ebp 
     push WORD strTotal
     call print 
    
     cli
     hlt
    
    _error:
     ;Print error
     push WORD strError
     call print
    
     cli 
     hlt
    
    
     ;Memory descriptor returned by INT 15 
     baseAddress dq 0
     length      dq 0
     type        dd 0
     extAttr     dd 0
    
     ;This function just show the string strFormat with the appropriate values 
     ;taken from the mem descriptor 
     show_memory_range:
      push bp
      mov bp, sp
    
      ;Extend SP into ESP so we can use ESP in memory operanda (SP is not valid in any addressing mode)
      movzx esp, sp 
    
      ;Last percent
      push DWORD [type]
    
      ;Last percents pair
      push DWORD [length]
      push DWORD [length + 04h]
    
      ;Add baseAddress and length (64 bit addition)
      push DWORD [baseAddress]
      mov eax, DWORD [length]
      add DWORD [esp], eax               ;Add (lower DWORD)
      push DWORD [baseAddress + 04h]
      mov eax, DWORD [length + 04h]
      adc DWORD [esp], 0                 ;Add with carry (higher DWORD)
    
      ;First percents pair
      push DWORD [baseAddress]
      push DWORD [baseAddress + 04h]
    
      push WORD strFormat
      call print
    
      mov sp, bp                         ;print is a mixed stdcall/cdecl, remove the arguments
    
      pop bp
      ret
    
     ;Strings, here % denote a 32 bit argument printed as hex 
     strFormat db "%% - %% (%%) - %", 0
     strError  db "Som'thing is wrong :(", 0
     strTotal  db "Total amount of memory: %", 0 
     ;This is tricky, see below 
     strNL     db 0
    
     ;Show a 32 bit hex number
     itoa16:
      push cx
      push ebx
    
      mov cl, 28d
    
     .digits:
       mov ebx, eax
       shr ebx, cl
       and bx, 0fh                     ;Get current nibble
    
       ;Translate nibble (digit to digital)
       mov bl, BYTE [bx + hexDigits]
    
       ;Show it 
       mov bh, 0ch
       mov WORD [fs:si], bx
       add si, 02h   
    
       sub cl, 04h
      jnc .digits
    
      pop ebx
      pop cx
      ret
    
      hexDigits db "0123456789abcdef"
    
      ;This function is a primitive printf, where the only format is % to show a 32 bit 
      ;hex number 
      ;The "cursor" is kept by SI.
      ;SI is always aligned to lines, so 1) never print anything bigger than 80 chars
      ;2) successive calls automatically print into their own lines 
      ;3) SI is assumed at the beginning of a line 
    
      ;Args
      ;Format
      print:
       push bp
       mov bp, sp
    
       push di
       push cx
    
       mov di, WORD [bp+04h]      ;String 
       mov cx, 80*2               ;How much to add to SI to reach the next line 
    
       add bp, 06h                ;Pointer to var arg 
    
      .scan:
    
        ;Read cur char 
        mov al, [di]
        inc di
    
        ;Format?
        cmp al, '%'
        jne .print
    
        ;Get current arg and advance index 
        mov eax, DWORD [bp]
        add bp, 04h
        ;Show the number 
        call itoa16
    
        ;We printed 8 chars (16 bytes) 
        sub cx, 10h
    
       jmp .scan    
    
      .print:
        ;End of string?
        test al, al
        je .end
    
        ;Normal char, print it 
        mov ah, 0ch
        mov WORD [fs:si], ax
        add si, 02h
        sub cx, 02h
    
       jmp .scan   
    
    
      .end:
       add si, cx
    
       pop cx
       pop di
    
       pop bp
       ret 02h
    
       ;Signature
       TIMES 510 - ($-$$) db 0 
       dw 0aa55h
    

    在64MiB Bochs仿真机中,结果是

    Memory map of a 64 MiB machine

    格式为开头 - 结尾(大小) - 输入

    使用我们获得的图片

    Map

    计算的内存总量为66.711.552字节或64 MiB - 1 KiB(EBDA) - 96 KiB(阴影区域) - 288 KiB(标准孔)。
    ACPI表被认为是可用的,因为它们是可回收的。

    1 特别是北桥的部分,现在是iMC,致力于处理DRAM。可以使用SPDSMBus控制器通过I2C检索已安装模块(主要是DIMM和移动设备)的信息。 然后BIOS考虑启用的内存映射设备和总线拓扑(连同其路由和桥接信息),并通过SMBios specification公开所有这些。

    2 因为它无论如何都会使用某种范围描述符,所以最终会执行格式转换。

    3 此计数包括新类型5(内存不良)范围。

相关问题