Get Memory Map - Freien Speicher bestimmen
-
INT 0x15, EAX = 0xE820 ... available on all PCs built since 2002
http://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map
Ich würde in PrettyOS gerne den physikalischen Speicher zuverlässig messen, da ich diesen an einer Stelle für die Zahl der Frames benötige.
Mein Bootloader arbeitet momentan nur mit [Bits 16] (oder weg lassen) richtig, aber nicht mit [Bits 32] (wird zwar übersetzt, funktioniert aber nicht, nur Reset).
Für obigen Code bräuchte ich aber eine 32-Bit-Umgebung, wenn ich das richtig sehe. Diese habe ich erst nach dem Sprung nach PM, dort ist dann aber int 0x15 weg.
-
Der sollte im Real Mode funktionieren. Die BIOS-Funktion wäre sonst ziemlich sinnfrei Die 32 Bit Register stehen dir (auf einem 386er und aufwärts) im Real Mode zur freien Verfügung.
-
Die 32 Bit Register stehen dir (auf einem 386er und aufwärts) im Real Mode zur freien Verfügung.
OK, daher geht auch so etwas wie hier:
switch_to_PM: mov eax, cr0 ; switch-over to Protected Mode or eax, 1 ; set bit 0 of CR0 register mov cr0, eax ;
Was bewirkt dann die Angabe [Bits 16], [Bits 32], < nichts angeben >, die sich ja auf die Operationen bezieht, genau in diesem Zusammenhang bei NASM? Kann ich diese BIOS-Funktion mit [Bits 16] sinnvoll ausführen?
Ich habe die Funktion eingebaut und rufe sie im Bootloader auf. Zumindest wird alles übersetzt, und das OS bootet noch. Wird langsam eng in den 512 Byte. Nun muss ich mal schauen, wie ich meine Werte zusammen suche und an den C-Kernel übergebe.
-
Von http://developer.apple.com/documentation/DeveloperTools/nasm/nasmdoc5.html (Google scheint die Nasm Dokumentation von Apple irgendwie zu bevorzugen):
You do not need to specify BITS 32 merely in order to use 32-bit instructions in a 16-bit DOS program; if you do, the assembler will generate incorrect code because it will be writing code targeted at a 32-bit platform, to be run on a 16-bit one.
When NASM is in BITS 16 state, instructions which use 32-bit data are prefixed with an 0x66 byte, and those referring to 32-bit addresses have an 0x67 prefix. In BITS 32 state, the reverse is true: 32-bit instructions require no prefixes, whereas instructions using 16-bit data need an 0x66 and those working on 16-bit addresses need an 0x67.
Die BITS Direktive scheint also nur etwas mit der Auswahl der passenden "echten" Maschinenbefehle zu tun.
-
Danke für die interessante Information.
Ich überlege gerade, wo ich den Puffer für die Listen hinlege:
Stack ist bei 0x7000, bootloader liegt bei 0x7C00 und lädt den Kernel beginnend ab 0x10000. Also wäre doch Platz für solche Dinge ab 0x8000, also ES:DI = 0x0000:8000 ?;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Determine physical memory ; ; ; ; input: ; ; es:di -> destination buffer ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; xor ax, ax mov es, ax mov ax, 0x8000 mov di, ax call get_memory_by_int15_e820
Hinterher landet der "entry count" hier:
mov [mmap_ent], bp ; store the entry count
Ich habe da mal Platz für mmap_ent gehalten:
mmap_ent dw 0
Ist define word o.k., bp ist doch 16 bit?
Hab's mal eingebaut, sowas in der Art finde ich aber noch nicht in bochs output:
Typical Output by a call to INT 15h, EAX=E820 in Bochs:
Base Address | Length | Type
0x0000000000000000 | 0x000000000009FC00 | Free Memory (1)
0x000000000009FC00 | 0x0000000000000400 | Reserved Memory (2)
0x00000000000E8000 | 0x0000000000018000 | Reserved Memory (2)
0x0000000000100000 | 0x0000000001F00000 | Free Memory (1)
0x00000000FFFC0000 | 0x0000000000040000 | Reserved Memory (2)Kann ich mir jetzt vom C-Kernel nach dem Sprung nach PM die Zahlen ab 0x8000 einlesen? Wie finde ich von C aus dieses mmap_ent? Assembler ist leider nicht meine Homebase, wird aber sicher noch, wenn ich so weiter mache und mich GRUB trotzig widersetze.
Hier ein Ausdruck aus dem C-Kernel (als ULONG, da muss man wegen little endian sicher noch etwas ändern) beginnend ab 0x8000:
// physical memory ULONG i; for(i=0; i<80; i+=4) printformat("%x: %x\n",0x8000+i,( *( (ULONG*)(0x8000+i) ) ) );
00008000h: 00000000h
00008004h: 00000000h
00008008h: 0009F000h
0000800Ch: 00000000h
00008010h: 00000001h
00008014h: 00000001h
00008018h: 0009F000h
0000801Ch: 00000000h
00008020h: 00001000h
00008024h: 00000000h
00008028h: 00000002h
0000802Ch: 00000001h
00008030h: 000E8000h
00008034h: 00000000h
00008038h: 00018000h
0000803Ch: 00000000h
00008040h: 00000002h
00008044h: 00000001h
00008048h: 00100000h
0000804Ch: 00000000hqword sind dann doch zwei ULONG, dword ist ein ULONG.
* First qword = Base address
* Second qword = Length of "region" (if this value is 0, ignore the entry)
* Next dword = Region "type"
o Type 1: Usable (normal) RAM
o Type 2: Reserved - unusable
o Type 3: ACPI reclaimable memory
o Type 4: ACPI NVS memory
o Type 5: Area containing bad memory
* Next dword = ACPI 3.0 Extended Attributes bitfield (if 24 bytes are returned, instead of 20)
o Bit 0 of the Extended Attributes indicates if the entire entry should be ignored (if the bit is clear). This is going to be a huge compatibility problem because most current OSs won't read this bit and won't ignore the entry.
o Bit 1 of the Extended Attributes indicates if the entry is non-volatile (if the bit is set) or not. The standard states that "Memory reported as non-volatile may require characterization to determine its suitability for use as conventional RAM."
o The remaining 30 bits of the Extended Attributes are currently undefined.D.h. pro Eintrag müssten es qword, qword, dword, dword also 6 ULONG oder 24 BYTE sein.
-
Erhard Henkes schrieb:
Wie finde ich von C aus dieses mmap_ent? Assembler ist leider nicht meine Homebase, wird aber sicher noch, wenn ich so weiter mache und mich GRUB trotzig widersetze.
Da es auch nicht gerade mein Spezielgebiet ist (vor allem keine praktische Erfahrung) kann ich grad auch nur (mit Begründung) drauflos raten und weiß weder ob es elegant oder richtig ist.
Nach meinem Verständniss ist bei dem Bootloader, der ja im Bin Format vorliegt und nicht gelinkt wird, Code und Daten zusammengewürftelt und so hinternander wie man es in den Assemblercode schreibt. Wir wissen, dass der Bootloader an der Stelle 0x7C00 liegt, und mmap_ent liegt dann an irgendeinem Offset von dort aus, den wir geeignet wählen sollten. Offset 0 geht nicht, da dort ein Befehl liegen sollte, da das Bios dorthin springt um den Bootloader auszuführen. Daher würde ich es ans Ende (natürlich vor den Magic-Werten) schreiben. Also nur noch bis 509 bytes mit Nullen füllen und dann dort mmap_ent hinlegen. Die Adresse ist dann fest 0x7DFD (0x7C00 + 509).
Erhard Henkes schrieb:
Kann ich mir jetzt vom C-Kernel nach dem Sprung nach PM die Zahlen ab 0x8000 einlesen?
Davon gehe ich aus, sonst würde meine Idee auch nicht funktionieren
-
Also nur noch bis 509 bytes mit Nullen füllen und dann dort mmap_ent hinlegen. Die Adresse ist dann fest 0x7DFD (0x7C00 + 509).
Danke, gute Idee! Da ich wegen bp ein "word" frei gehalten habe, werde ich aber 2 Byte vorsehen:
bootdrive db 0 loadmsg db "loading kernel ...",13,10,0 errormsg db "sector read error ...",13,10,0 progressmsg db "*",0 times 508-($-$$) hlt mmap_ent dw 0 db 0x55 db 0xAA
Habe mir das in MyOS.bin mit dem Hex-Editor angeschaut:
f4 f4 f4 00 00 55 aa (exakt am richtgien Platz zum Booten)0x7C00 + 508 = 0x7DFC
printformat("%x: %x\n\n",0x7DFC,( *( (USHORT*)(0x7DFC) ) ) );
00007DFCh: 00000006h
6 Einträge
Ich habe mal den Test durchgeführt und die Funktion nicht aufgerufen, sieht gut aus:
00007DFCh: 00000000h
00008000h: 00000000h
00008004h: 00000000h
00008008h: 00000000h
0000800Ch: 00000000h
00008010h: 00000000h
00008014h: 00000000h
00008018h: 00000000h
0000801Ch: 00000000h
00008020h: 00000000h
00008024h: 00000000h
00008028h: 00000000h
0000802Ch: 00000000h
00008030h: 00000000h
00008034h: 00000000h
00008038h: 00000000h
0000803Ch: 00000000h
00008040h: 00000000h
00008044h: 00000000h
00008048h: 00000000h
0000804Ch: 00000000hJetzt muss ich mir nur noch die Infos passend zusammen suchen. Ein kleines Puzzle.
Habe mir das jetzt mal auf einem richtigen PC (512 MB RAM = 0x1FFFFFFF) angeschaut:
Zahl der Einträge: 5 (wie erwartet)Erster Eintrag: (im Ausdruck alles ULONG also dword, beim qword daher zuerst low dword, dann high dword)
qword, qword, dword, dword 0x0 0x0 0xA0000 0x0 0x1 0x1 Base address Length of "region" Region "type" Extended Attributes bitfield if this value is 0, Type 1: ignore the entry Usable RAM Bit0: 1 ignore if clear Bit1: 0 volatile (?)
sieht gut aus:
Erste Region geht also bis 0xA0000 und ist normales RAM, klingt vernünftig:
http://www.henkessoft.de/OS_Dev/Bilder/Speicherbelegung.JPGZweiter Eintrag: (im Ausdruck alles ULONG also dword)
qword, qword, dword, dword 0xF0000 0x0 0x10000 0x0 0x2 0x1 Base address Length of "region" Region "type" Extended Attributes bitfield if this value is 0, Type 2: ignore the entry Reserved Bit0: 1 ignore if clear = unusable Bit1: 0 volatile (?)
Dritter Eintrag: (im Ausdruck alles ULONG also dword)
qword, qword, dword, dword 0x100000 0x0 0x1FEF0000 0x0 0x1 0x1 Base address Length of "region" Region "type" Extended Attributes bitfield if this value is 0, Type 1: ignore the entry Usable RAM Bit0: 1 ignore if clear Bit1: 0 volatile (?)
Beim vierten Eintrag sehe ich momentan nur die Basisadesse:
0x1FFF0000Da muss ich meine Printroutine erweitern und umbauen, am besten gleich mit Auswertung nach diesem Schema.
Zusammengefasst (hex):
0 - A0000 RAM
F0000 - 100000 Reserved
100000 - 1FFF0000 RAM
1FFF0000 - ? ...Super! Hat perfekt geklappt, wenn ich es richtig sehe, außer diesem non-volatil/volatil (Bit1 von letztem DWORD: 1=non-volatil 0=volatil, die 0x1 für das DWORD bedeutet für Bit1 = 0, also volatil) haut alles bestens hin.
Danke für die moralische Unterstützung und die Tipps!
Jetzt muss das nur noch auf möglichst vielen PCs klappen.
-
Hier ist das Foto der vollständigen Memory-Analyse des untersuchten PC:
http://www.henkessoft.de/OS_Dev/Bilder/detect_memory.JPGZurück zur Frage der Bestimmung des "Physical Memory" des untersuchten Rechners. Wenn ich das richtig sehe, muss man nun die "length" der Speicherbereiche addieren, die "type" gleich 1 (usable RAM) haben.
Region "type"
* Type 1: Usable (normal) RAM
* Type 2: Reserved - unusable
* Type 3: ACPI reclaimable memory
* Type 4: ACPI NVS memory
* Type 5: Area containing bad memoryDas wären in diesem konkreten Fall (RAM 512 MB wird beim Booten angezeigt):
0xA0000 + 0x1FEF0000 = 0x1FF90000 = 536412160 Byte = 523840 KB = 511,5625 MB0xA0000 = 640 KB ("low memory")
Passt also sehr gut.
Ich habe diese Experimente hier dokumentiert:
http://www.henkessoft.de/OS_Dev/OS_Dev3.htm#mozTocId584885So kann man das sehr schön als Gesamt Usable RAM erfassen und anzeigen:
typedef struct Mem_Chunk_struct { ULONG base_lo; ULONG base_hi; ULONG length_lo; ULONG length_hi; ULONG type; ULONG extended; }Mem_Chunk_t; //... Mem_Chunk_t Mem_Chunk[10]; // contiguous parts of memory detected by int 15h eax = 820h //... // physical memory USHORT num_of_entries = *( (USHORT*)(0x7DFC) ); //printformat("# entries: %d\n\n",(num_of_entries) ); //printformat("base length type extended\n"); pODA->Memory_Size = 0; ULONG i,j; for(i=0; i<num_of_entries; ++i) { //printformat("\n"); for(j=0; j<24; j+=4) { if(j== 0) Mem_Chunk[i].base_lo = *( (ULONG*)(0x8000+i*24+j) ); if(j== 4) Mem_Chunk[i].base_hi = *( (ULONG*)(0x8000+i*24+j) ); if(j== 8) Mem_Chunk[i].length_lo = *( (ULONG*)(0x8000+i*24+j) ); if(j==12) Mem_Chunk[i].length_hi = *( (ULONG*)(0x8000+i*24+j) ); if(j==16) Mem_Chunk[i].type = *( (ULONG*)(0x8000+i*24+j) ); if(j==20) Mem_Chunk[i].extended = *( (ULONG*)(0x8000+i*24+j) ); // printformat("%x ", *( (ULONG*)(0x8000+i*24+j) ) ); } if((Mem_Chunk[i].type)==1) pODA->Memory_Size += Mem_Chunk[i].length_lo; // printformat("\n"); } printformat("\nUsable RAM: %d KB", (pODA->Memory_Size)/1024); printformat("\n\n");
PrettyOS [Version 0.1.0087] (C) 2009 henkessoft.de
Usable RAM: 523840 KB
Command Line:
$>
-
Könnte ein Mod diesen Thread bitte nach OS-Development verschieben?
-
Dieser Thread wurde von Moderator/in Nobuo T aus dem Forum Assembler in das Forum Projekt: OS-Development verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.