Finding Memory Size in Boot without DOS, Windows, Linux
我正在用汇编 (NASM) 编写一个简单的程序。当引导扇区加载时,它必须以兆字节显示计算机中安装的所有内存 (RAM)。加载引导扇区时将没有操作系统(DOS、Windows、Linux),那么我将如何找出总 RAM 大小。我的计算机中有 2 GB RAM。我在互联网上搜索了很多,但找不到解决方案。
是否存在显示内存大小为 2 GB 的 BIOS 中断?在旧计算机中使用了一个中断来显示内存,但它并未显示所有 2 GB。我查了一下,在 Ralph Brown List 中没有解决方案。可能有人对 BIOS 了解更多。如果 BIOS 不提供此功能,那么我可以使用 C/C 找出总 RAM 大小吗?并从程序集中调用 C/C 代码? C/C 的什么函数将用于查找总 RAM 大小?
请记住,我的汇编代码会进行冷启动,并且没有操作系统可以为我的代码提供任何便利。
已编辑:
我阅读了网站 http://wiki.osdev.org/Detecting_Memory_(x86)。并决定检查 int 15 是否有效。所以我从这个网站得到了代码并对其进行了编辑以测试 int 15 EAX = E820 是否有效。但它无法工作,输出是 .failed1 中的 \\'F\\'。 \\'F\\' 是我为检查 "unsupported function" 所做的测试用例。测试用例是 \\'F\\'、\\'G\\' 和 \\'H\\'。这是代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | ; use the INT 0x15, eax= 0xE820 BIOS function to get a memory map ; inputs: es:di -> destination buffer for 24 byte entries ; outputs: bp = entry count, trashes all registers except esi do_e820: xor ebx, ebx ; ebx must be 0 to start xor bp, bp ; keep an entry count in bp mov edx, 0x0534D4150 ; Place"SMAP" into edx mov eax, 0xe820 mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry mov ecx, 24 ; ask for 24 bytes int 0x15 jc short .failed1 ; carry set on first call means"unsupported function" mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register? cmp eax, edx ; on success, eax must have been reset to"SMAP" jne short .failed2 test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless) je short .failed3 jmp short .jmpin .e820lp: mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry mov ecx, 24 ; ask for 24 bytes again int 0x15 jc short .e820f ; carry set means"end of list already reached" mov edx, 0x0534D4150 ; repair potentially trashed register .jmpin: jcxz .skipent ; skip any 0 length entries cmp cl, 20 ; got a 24 byte ACPI 3.X response? jbe short .notext test byte [es:di + 20], 1 ; if so: is the"ignore this data" bit clear? je short .skipent .notext: mov ecx, [es:di + 8] ; get lower dword of memory region length or ecx, [es:di + 12] ;"or" it with upper dword to test for zero jz .skipent ; if length qword is 0, skip entry inc bp ; got a good entry: ++count, move to next storage spot add di, 24 .skipent: test ebx, ebx ; if ebx resets to 0, list is complete jne short .e820lp .e820f: mov [mmap_ent], bp ; store the entry count clc ; there is"jc" on end of list to this point, so the carry must be cleared mov ah, 0x0E ; Teletype command mov bh, 0x00 ; Page number mov bl, 0x07 ; Attributes (7 == white foreground, black background) mov al, mmap_ent ; Character to print int 0x10 ret .failed1: push eax push ebx mov ah, 0x0E ; Teletype command mov bh, 0x00 ; Page number mov bl, 0x07 ; Attributes (7 == white foreground, black background) mov al, 70 ; Character 'F' to print int 0x10 pop ebx pop eax stc ;"function unsupported" error exit ret .failed2: push eax push ebx mov ah, 0x0E ; Teletype command mov bh, 0x00 ; Page number mov bl, 0x07 ; Attributes (7 == white foreground, black background) mov al, 71 ; Character 'G' to print int 0x10 pop ebx pop eax stc ;"function unsupported" error exit ret .failed3: push eax push ebx mov ah, 0x0E ; Teletype command mov bh, 0x00 ; Page number mov bl, 0x07 ; Attributes (7 == white foreground, black background) mov al, 72 ; Character 'H' to print int 0x10 pop ebx pop eax stc ;"function unsupported" error exit ret mmap_ent db 0 failmsg db 0 failmem db 'Failed', 0 ;times 512-($-$$) db 0 ;dw 0xAA55 |
已编辑:
我使用了 nasm memext.asm -o memext.com -l memext.lst 。使用 MagicISO 制作可启动映像文件 memext.iso 并使用 Windows 磁盘刻录机将其刻录到 DVD/RW。加载 Oracle VM 并创建一个具有 256 Mb RAM、CD/DVD、2GB 硬盘的新虚拟机。使用 DVD 启动以进行冷启动测试,不打印任何内容。
另外,我打开命令控制台并输入 memext,它给出了 \\'F\\' 作为输出。
您需要在 PC(或其他支持 ACPI 的机器)上读取 ACPI 表。
请注意,这不会给你一个数字的总大小,而是给你每个内存区域的内存大小 - 在一台简单的机器上,可能只有两个或三个区域(有""的孔不是real memory" 位于 0xA0000-0xFFFFF 以及 BIOS 决定放置 "PCI-hole" 的任何位置)。
考虑到某些引导扇区只有大约 400 字节的可用空间,我怀疑将 ACPI 读取器放入单个扇区并非易事(尽管如果您完全跳过分区表,我想您可以使用几乎所有的 512 个字节)。
至于"如何调用C/C",你将无法将任何有意义的C 或C 程序放在少于几个扇区中。您将需要查看 OS 引导加载程序,并了解它们如何实现编译器的设置(在许多情况下,您还需要特殊工具来生成位于适合加载到内存的特定位置的代码并直接执行)。这个页面可能对此有所帮助(我还没有通读它,它甚至可以告诉你有多少内存):http://www.codeproject.com/Articles/36907/How-to-develop-你自己的引导加载程序
编辑:我的错误,wiki 是正确的,只是把它留在这里是因为...
看起来 wiki 中有一个错字 - 行:
1 | mov edx,0x0534D4150 |
应该看起来像:
1 | mov edx,0x050414D53 |
请注意字节顺序相反(因为 x86 是小端)。