实验5.内存容量检测

简介

实验:利用BIOS中断获取物理内存容量

注意:mbr不再跳转到loader的起始位置,而是loader的loader_start位置

代码

boot/mbr.s

; boot/mbr.s
; 功能:读取磁盘,加载loader到内存并跳转到loader的loader_start位置

%include "boot.inc"
SECTION MBR vstart=0x7c00
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
    mov sp,0x7c00
    mov ax, 0xb800
    mov gs,ax

; 清屏利用0x06号功能,上卷全部行,则可清屏。
; 输入:
; AH 功能号=0x06
; AL = 上卷的行数(如果为0,表示全部)
; BH = 上卷行属性
; (CL,CH) = 窗口的左上角的(X,Y)位置
; (DL,DH) = 窗口的右上角的(X,Y)位置
; 无返回值:
    mov ax,0x600
    mov bx,0x700
    mov cx,0
    mov dx,0x184f
    int 0x10

    mov eax,LOADER_START_SECTOR
    mov bx,LOADER_BASE_ADDR
    mov cx,2
    call rd_disk_m_16
    jmp LOADER_BASE_ADDR + 0x300

; 读取硬盘n个扇区
; eax = LBA扇区号
; bx = 将数据写入的内存地址
; cx=读入的扇区数
rd_disk_m_16:
; 备份
    mov esi,eax
    mov di,cx

; 设置要读取的扇区数
    mov cl,0x8
    mov dx,0x1f2
    mov al,cl
    out dx,al
    mov eax,esi

; 设置要读取的地址
    mov dx,0x1f3 ; 写入0-7位
    out dx,al

    mov cl,0x8
    shr eax,cl
    mov dx,0x1f4 ; 写入8-15位
    out dx,al

    shr eax,cl
    mov dx,0x1f5 ; 写入16-23位
    out dx,al

    shr eax,cl
    and al,0x0f ; 24~27位
    or  al,0xe0 ; 设置7~4位为1110,表示lba模式
    mov dx,0x1f6
    out dx,al

; 写入读命令
    mov dx,0x1f7
    mov al,0x20
    out dx,al

; 检测硬盘状态
.not_ready:
    nop
    in al,dx
    and al,0x88
    cmp al,0x08
    jnz .not_ready

; 读数据
    mov ax,di
    mov dx,256
    mul dx
    mov cx,ax
    mov dx,0x1f0
.go_on_read:
    in ax,dx
    mov [bx],ax
    add bx,2
    loop .go_on_read
    ret

    times 510-($-$$) db 0
    db 0x55,0xaa

boot/loader.s

; boot/loader.s
; 功能:完成在内存里写好3个段描述符(代码段、数据段、显存段)、赋值好GDTR寄存器、创建好表示3个选择子的字段后。
;      读取物理内存。
;       进入保护模式,在保护模式下执行最后三句代码

%include "boot.inc"
SECTION loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR

; ---构建gdt---
GDT_BASE:
    dd 0x0000_0000
    dd 0x0000_0000

CODE_DESC:
    dd 0x0000_ffff
    dd DESC_CODE_HIGH4

DATA_STARK_DESC:
    dd 0x0000_ffff
    dd DESC_DATA_HIGH4

VEDIO_DESC:
    dd 0x8000_0007 ; limit=(0xbffff-0xb8000)/4k=0x7
    dd DESC_VIDEO_HIGH4

    GDT_SIZE equ $ - GDT_BASE
    GDT_LIMIT equ GDT_SIZE - 1
    times 60 dq 0 ; 预留60个段描述的空间
; ---内存容量---
total_mem_bytes dd 0 ; 此处的地址为0x900 + (64 * 8 = 0x200) = 0xb00,偏移地址为0x200
; ---定义选择子---
    SELECTOR_CODE equ  (0x0001<<3) + TI_GDT + RPL0 
    SELECTOR_DATA equ  (0x0002<<3) + TI_GDT + RPL0
    SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0

; ---赋值GDTR寄存器---
    gdt_ptr dw GDT_LIMIT 
            dd GDT_BASE 

; ards_buf为缓冲区地址,缓冲区用来装0xe820子功能返回的ARDS结构体
; 一个ARDS结构体20字节,本次实验测试一共返回了6个
ards_buf times 244 db 0
ards_nr dw 0 ; 大小2个字节,用于记录 ARDS 结构体数量
; 人工对齐:total_mem_bytes+gdt_ptr+ards_buf+ards_nr=4+6+244+2=256,共256=0x100字节


loader_start: ; loader_start地址为0x900+0x300,偏移地址为0x300

;jmp .e820_failed_so_try_e801 ; 直接测试第2种
;jmp .e801_failed_so_try88 ; 直接测试第3种

; ---方法1 int 15h eax = 0000e820h ,edx = 534D4150h ('SMAP')获取内存布局 begin---
    xor ebx,ebx         ; 异或运算,第一次使用0xe820子功能ebx要清0
    mov edx, 0x534d4150 ; edx 只赋值一次,循环体中不会改变
    mov di,ards_buf     ; ES:DI是ards结构缓冲区的指针,es已在mbr.s赋值完毕了,这里不用再赋值.
.e820_mem_get_loop:     ; 循环获取每个ARDS内存范围描述结构
    mov eax,0x0000e820  ; 执行int0x15后,eax值为0x534d4150, 所以每次执行int前都要更新为子功能号
    mov ecx, 20         ; 1个ards结构体大小为20字节
    int 0x15;
    jc .e820_failed_so_try_e801 ;cf 位为 1则 有错误发生,尝试 Oxe801子功能
    
    add di, cx         ; 使di 增加 20 字节指向缓冲区中新的 ARDS 结构位置
    inc word [ards_nr] ; 记录 ARDS 数量
    cmp ebx, 0
    jnz .e820_mem_get_loop ; 若ebx不等于0,继续调用e820子功能返回ards结构体

    ; 在所有的ARDS结构体中,找到(base_addr_low + length_low)的最大值,即内存的容量
    mov cx, [ards_nr] ; 遍历每一个 ARDS 结构体,循环次数是 ARDS 的数量
    mov ebx, ards_buf ; ards缓冲区首地址
    xor edx, edx      ; edx为最大容量,在此先清0
.find_max_mem_area:
; 无需判断 type 是否为1,最大的内存块一定是可被使用的
    mov eax, [ebx]   ; 拿到结构体的 base_addr_low
    add eax, [ebx+8] ; base_addr_low + length_low
    add ebx, 20      ; 指向缓冲区中下-个 ARDS 结构
    cmp edx,eax 
    jge .next_ards   ; edx大于eax就访问下一个ards结构体
    mov edx, eax     ; 小于就记录在dex中
.next_ards:
    loop .find_max_mem_area ; cx记录了ards结构体数量,cx为0说明已经循环结束了
    jmp .mem_get_ok         ; 将最大内存容量放入total_mem_byte中
; ---方法1 int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP')获取内存布局 end---

;---方法2 int 15h ax = e801h 获取内存大小,最大支持4G begin---
; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位
; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。
.e820_failed_so_try_e801:
    mov ax,0xe801
    int 0x15
    jc .e801_failed_so_try88   ;若当前e801方法失败,就尝试0x88方法
; 1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位
    mov cx,0x400      ; cx和ax值一样,cx用做乘数 0x400 = 1024
    mul cx
    shl edx,16
    and eax,0x0000FFFF
    or edx,eax
    add edx, 0x100000 ; ax只是15MB,故要加1MB
    mov esi,edx
;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量
   xor eax,eax
   mov ax,bx		
   mov ecx, 0x10000	;0x10000十进制为64KB
   mul ecx		;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax.
   add esi,eax		;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可
   mov edx,esi		;edx为总内存大小
   jmp .mem_get_ok

;---方法2 int 15h ax = e801h 获取内存大小,最大支持4G end---
;---方法3 int 15h ah = 0x88 获取内存大小,只能获取64MB以内 begin---
.e801_failed_so_try88:
    ; int 15后,ax存入的是以kb为单位的内存容量
    mov  ah, 0x88
    int  0x15
    jc .error_hit
    and eax,0x0000FFFF
    
    ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中
    mov cx, 0x400     ;0x400等于1024,将ax中的内存容量换为以byte为单位
    mul cx
    shl edx, 16	     ;把dx移到高16位
    or edx, eax	     ;把积的低16位组合到edx
    add edx,0x100000  ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB
;---方法3 int 15h ah = 0x88 获取内存大小,只能获取64MB以内 end---

.mem_get_ok:
   mov [total_mem_bytes ], edx
; ---进入保护模式---
; 打开A20
    in al,0x92
    or al,0000_0010B
    out 0x92,al
; 加载gdt
    lgdt [gdt_ptr]
; cr0第0位给1
    mov eax,cr0
    or eax,0x0000_0001
    mov cr0,eax

; 刷新流水线
 jmp dword SELECTOR_CODE:p_mode_start

.error_hit: ; 出错则挂起
     jmp $

[bits 32]
p_mode_start:
    mov ax,SELECTOR_DATA
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov esp,LOADER_STACK_TOP

    mov ax,SELECTOR_VIDEO
    mov gs,ax
    mov byte [gs:160], 'P'
    jmp $

编译

Makefile

BUILD_DIR = ./build

.PHONY : mk_dir bootloader clean all

mk_dir: #创建build目录
	if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi

bootloader: #编译启动内核的文件 bootloader
	nasm -I include/ -o $(BUILD_DIR)/mbr.bin boot/mbr.s
	nasm -I include/ -o $(BUILD_DIR)/loader.bin boot/loader.s
	dd if=/home/c/tityos/build/mbr.bin of=/home/c/tityos/hd60M.img bs=512 count=1 conv=notrunc
	dd if=/home/c/tityos/build/loader.bin of=/home/c/tityos/hd60M.img bs=512 count=2 seek=2 conv=notrunc

clean: #删除build目录里的全部文件
	cd $(BUILD_DIR) && rm -f  ./*

# 创建build目录。编译启动内核的文件。
all: mk_dir bootloader

运行

start.sh

# !/bin/bash
# 功能:启动bochs

bin/bochs -f bochsrc.disk

在这里插入图片描述

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 07:20:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 07:20:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 07:20:03       58 阅读
  4. Python语言-面向对象

    2024-07-17 07:20:03       69 阅读

热门阅读

  1. 用于图像增强的学习型可控ISP

    2024-07-17 07:20:03       28 阅读
  2. 掌握Xcode的魔术:自定义Storyboard的创建与管理

    2024-07-17 07:20:03       29 阅读
  3. Telegram Bot、小程序开发(三)Mini Apps小程序

    2024-07-17 07:20:03       23 阅读
  4. Google 地图参考手册

    2024-07-17 07:20:03       26 阅读
  5. 通过swagger自动生成jmeter脚本

    2024-07-17 07:20:03       24 阅读
  6. B树(B-tree)

    2024-07-17 07:20:03       28 阅读
  7. 081、Python 关于方法重写

    2024-07-17 07:20:03       22 阅读
  8. 【linux 100条命令】

    2024-07-17 07:20:03       24 阅读
  9. Emacs: 可扩展的编辑器之神

    2024-07-17 07:20:03       28 阅读
  10. 如何在VSCode中配置Python环境

    2024-07-17 07:20:03       28 阅读
  11. 计算机视觉和自然语言处理:OCR 模型

    2024-07-17 07:20:03       26 阅读