Verification of GRUB Memory Map

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Verification of GRUB Memory Map

Post by zhiayang »

Hey. Based on a suggestions on the IRC channel, I've been recommended to use the memory map from from GRUB instead of using the mem_upper and men_lower fields.

Basically,

Code: Select all

if(MBT->flags & (1 << 6))
{
	unsigned long TotalMapAddress = MBT->mmap_addr;
	unsigned long TotalMapLength = MBT->mmap_length;

	puts("----\n");
	puthex(TotalMapAddress);
	puts("\n");
	puthex(TotalMapLength);
	puts("\n----");
	puts("\n\n");

	memory_map_t *MemMap = (memory_map_t*)MBT->mmap_addr;

	unsigned long CurrentReadingAddress = TotalMapAddress;

	while(MemMap < TotalMapAddress + TotalMapLength)
	{
			
		puthex(MemMap);
		puts(" -- ");
		putnum(MemMap->type);
		puts(" == ");
		puthex(MemMap->base_addr_low);
		puts(" == ");
		puthex(MemMap->length_low);
		puts("\n");
			
		if(MemMap->type == 1)
		{

			//UsableMemoryEnd = CurrentReadingAddress + MemMap->size;
			
		}
		//CurrentReadingAddress += MemMap->size;
		MemMap = (memory_map_t*)((unsigned long)MemMap + MemMap->size + sizeof(unsigned int));
	}
}

1. Is this the right way of getting the memory map?
2. Is this a valid memory map?

Code: Select all

0x359A0 -- 1 == 0x1 == 0x9F400
0x359B8 -- 2 == 0x9F400 == 0xC00
0x359D0 -- 2 == 0xF0000 == 0x10000
0x359E8 -- 1 == 0x100000 == 0x1FEFE000
0x35A00 -- 2 == 0x1FFE000 == 0x2000
0x35A18 -- 2 == 0xFFFC0000 == 0x40000
The first field is the address of the memory map structure, which can be discarded (right?)

The second field is the type, which leads to another question:

2. Is the 'type' field from GRUB memory map the same as the type field returned from INT 0x15, EAX=0xE820?


3. There seems to be an apparent lack of usable memory; Whether I pass -m 2 or -m 64 to qemu-system-x86_64, I get the same values.


4. Is that simply a problem with QEMU? I get different values on VMWare Fusion:

Code: Select all

0x359A0 -- 1 == 0x1 == 0x9F800
0x359B8 -- 2 == 0x9F800 == 0x800
0x359D0 -- 2 == 0xDC000 == 0x2400
0x359E8 -- 1 == 0x100000 == 0xFDF0000
0x35A00 -- 3 == 0xFEF0000 == 0xF000
0x35A18 -- 4 == 0xFEFF000 == 0x1000
0x35A30 -- 1 == 0xFF00000 == 0x100000
0x35A48 -- 2 == 0xE0000000 == 0x10000000
0x35A60 -- 2 == 0xFEC00000 == 0x10000
0x35A78 -- 2 == 0xFEE00000 == 0x1000
0x35A90 -- 2 == 0xFFFE0000 == 0x20000
With 256 MB of memory.

With 4 MB (I'll post a screenie) Some values are different. However, there seems to be memory beyond whatever MB I allocate to the VM. I can't test this on hardware because I have a mac.
VMWare on 4MB Memory
VMWare on 4MB Memory

EDIT:

4. Is there a way to verify the memory map?
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Verification of GRUB Memory Map

Post by xenos »

Code: Select all

0x359A0 -- 1 == 0x1 == 0x9F400 <-- this should probably start at 0x0, not 0x1
0x359B8 -- 2 == 0x9F400 == 0xC00
0x359D0 -- 2 == 0xF0000 == 0x10000
0x359E8 -- 1 == 0x100000 == 0x1FEFE000 <-- there is a typo either here
0x35A00 -- 2 == 0x1FFE000 == 0x2000 <-- or here
0x35A18 -- 2 == 0xFFFC0000 == 0x40000
This looks fine for me, except for one or two possible typos. Look at the length and the start of the two memory regions above and you'll see what I mean. I am a bit surprised you get 0x1 instead of 0x0 for the start of the first region... But everything else is fine - there is some lower RAM, the EBDA, BIOS ROM, upper RAM, some ACPI area and video memory (I suppose).

The same strange 0x1 appears here again:

Code: Select all

0x359A0 -- 1 == 0x1 == 0x9F800
0x359B8 -- 2 == 0x9F800 == 0x800
0x359D0 -- 2 == 0xDC000 == 0x2400
0x359E8 -- 1 == 0x100000 == 0xFDF0000
0x35A00 -- 3 == 0xFEF0000 == 0xF000
0x35A18 -- 4 == 0xFEFF000 == 0x1000
0x35A30 -- 1 == 0xFF00000 == 0x100000
0x35A48 -- 2 == 0xE0000000 == 0x10000000
0x35A60 -- 2 == 0xFEC00000 == 0x10000
0x35A78 -- 2 == 0xFEE00000 == 0x1000
0x35A90 -- 2 == 0xFFFE0000 == 0x20000
Again, everything else looks fine. For your information, here's the output of my kernel running on QEMU with 64MB RAM (it shows start and end of memory regions instead of their lengh):

Code: Select all

Mem: 0x0000000000000000-0x000000000009f3ff, Type: 0x01
Mem: 0x000000000009f400-0x000000000009ffff, Type: 0x02
Mem: 0x00000000000f0000-0x00000000000fffff, Type: 0x02
Mem: 0x0000000000100000-0x0000000003ffcfff, Type: 0x01
Mem: 0x0000000003ffd000-0x0000000003ffffff, Type: 0x02
Mem: 0x00000000fffc0000-0x00000000ffffffff, Type: 0x02
And here with 2MB:

Code: Select all

Mem: 0x0000000000000000-0x000000000009f3ff, Type: 0x01
Mem: 0x000000000009f400-0x000000000009ffff, Type: 0x02
Mem: 0x00000000000f0000-0x00000000000fffff, Type: 0x02
Mem: 0x0000000000100000-0x00000000001fcfff, Type: 0x01
Mem: 0x00000000001fd000-0x00000000001fffff, Type: 0x02
Mem: 0x00000000fffc0000-0x00000000ffffffff, Type: 0x02
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
turdus
Member
Member
Posts: 496
Joined: Tue Feb 08, 2011 1:58 pm

Re: Verification of GRUB Memory Map

Post by turdus »

requimrar wrote:1. Is this the right way of getting the memory map?
2. Is this a valid memory map?
Yep, you're fine, and it seems valid.
2. Is the 'type' field from GRUB memory map the same as the type field returned from INT 0x15, EAX=0xE820?
Yes. And it also correlate to EFI System map types. But you won't need it, read further.
3. There seems to be an apparent lack of usable memory; Whether I pass -m 2 or -m 64 to qemu-system-x86_64, I get the same values.
I never had problem with this. If you use more memory, the map will be bigger (at a minimum size of the last free block will change).
4. Is that simply a problem with QEMU? I get different values on VMWare Fusion:
...
With 4 MB (I'll post a screenie) Some values are different.
And it will be different on every machine. That's the point of such firmware data, get info in a well defined structure that describes a varying hardware.
However, there seems to be memory beyond whatever MB I allocate to the VM. I can't test this on hardware because I have a mac.
Never use memory that weren't reported as free. Maybe in VM it will work, but it can and probably will cause strange problems on real hardware.
4. Is there a way to verify the memory map?
Yes, you have to order the memory map records, and make them disjunct. That is, there should be no overlaping, and should not contain free memory that was also reported non-free in another map record. Most OS devers specify their own map, and transform firmware provided map on boot. This way it does not matter whether you use E820 or GRUB map or EFI map (with fancy type codes), because you "translate" type codes too. As a matter of fact, your memory allocator has to deal with only 3 types of memory:
- free, unused and usable memory
- used by kmalloc, reusable memory allocated by your kmalloc (that can be freed)
- used by firmware, simply lost memory to kmalloc (cannot be freed)
It really doesn't matter if unusable memory is an LFB MMIO space or ACPI tables or whatever from memory management point of view. The only thing matters do not free it.
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Re: Verification of GRUB Memory Map

Post by zhiayang »

XenOS wrote:

Code: Select all

0x359A0 -- 1 == 0x1 == 0x9F400 <-- this should probably start at 0x0, not 0x1
0x359B8 -- 2 == 0x9F400 == 0xC00
0x359D0 -- 2 == 0xF0000 == 0x10000
0x359E8 -- 1 == 0x100000 == 0x1FEFE000 <-- there is a typo either here
0x35A00 -- 2 == 0x1FFE000 == 0x2000 <-- or here
0x35A18 -- 2 == 0xFFFC0000 == 0x40000
This looks fine for me, except for one or two possible typos. Look at the length and the start of the two memory regions above and you'll see what I mean. I am a bit surprised you get 0x1 instead of 0x0 for the start of the first region... But everything else is fine - there is some lower RAM, the EBDA, BIOS ROM, upper RAM, some ACPI area and video memory (I suppose).
Indeed there was a typo, 0x1FEFE000 was supposed to be 0x1EFE000.

However, I can verify that there is no typo on the 0x1 starting address.
Is it a problem with my memory map checking code?

Or is it the fact that I disregard base_addr_high?

Also: If I map the first x MB (x > 1) of memory to some virtual address that I can access from my 64-bit kernel (this is a loader) can I still access the memory map again, then re-evaluate the memory map (including translating it to some arbitrary format of mine?)
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Re: Verification of GRUB Memory Map

Post by zhiayang »

turdus wrote:
Yes, you have to order the memory map records, and make them disjunct. That is, there should be no overlaping, and should not contain free memory that was also reported non-free in another map record. Most OS devers specify their own map, and transform firmware provided map on boot. This way it does not matter whether you use E820 or GRUB map or EFI map (with fancy type codes), because you "translate" type codes too. As a matter of fact, your memory allocator has to deal with only 3 types of memory:
- free, unused and usable memory
- used by kmalloc, reusable memory allocated by your kmalloc (that can be freed)
- used by firmware, simply lost memory to kmalloc (cannot be freed)
It really doesn't matter if unusable memory is an LFB MMIO space or ACPI tables or whatever from memory management point of view. The only thing matters do not free it.
Okay. But according to the wiki, when (if) I am done with the ACPI tables, I can reclaim it?
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Verification of GRUB Memory Map

Post by xenos »

requimrar wrote:However, I can verify that there is no typo on the 0x1 starting address.
Is it a problem with my memory map checking code?
This is indeed a bit strange. I don't see any problem in your code above which could lead to such strange behavior. I suspect that some bug somewhere in the code that is executed before this snippet which somehow writes a 0x1 to that address and overwrites part of the memory map. Perhaps you can try to set a memory watchpoint (with QEMU + GDB, or Bochs debugger (I'm not sure whether it supports memory watchpoints)) at that address and find out when it gets modified.
Or is it the fact that I disregard base_addr_high?
No, that shouldn't be a problem (unless you want to use RAM above 4G, of course).
Also: If I map the first x MB (x > 1) of memory to some virtual address that I can access from my 64-bit kernel (this is a loader) can I still access the memory map again, then re-evaluate the memory map (including translating it to some arbitrary format of mine?)
Yes, you just need to map it somewhere and remember where you mapped it. I do that in my 32 / 64 bit kernel as well.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Verification of GRUB Memory Map

Post by AJ »

Hi,

Is the output of puthex(0) as you would expect?

Cheers,
Adam
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Re: Verification of GRUB Memory Map

Post by zhiayang »

AJ wrote:Hi,

Is the output of puthex(0) as you would expect?

Cheers,
Adam

AAAAND… AJ! With the simplest solution!
User avatar
turdus
Member
Member
Posts: 496
Joined: Tue Feb 08, 2011 1:58 pm

Re: Verification of GRUB Memory Map

Post by turdus »

requimrar wrote:Okay. But according to the wiki, when (if) I am done with the ACPI tables, I can reclaim it?
Yes, if you want. I handle it as firmware rom, and do not free it ever, because:
- it's only a few Kb of memory
- every driver parse it to get it's device info, and because driver loading not restricted to boot time, I will need the tables later too.

Maybe parsing the tables several times is not the best way to do things, but it's very easy and clear, also modular approach. Every driver parses the blocks that it's interested in (typically different info for different drivers), and it's only done once whenever a driver installed, so performance is not crutial. I thought parsing acpi tables into an universal driver structure would require more memory with only a little lookup performance benefit. Also there's a chance that a new device would require such info that cannot be represented in your universal structure, and modifying a common structure would require a lot of work in every driver. My solution was an acpi table parser library, that's get called in every driver's init() method, but of course you can do things differently in your OS.
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Re: Verification of GRUB Memory Map

Post by zhiayang »

Okay. Quick problem analysis here:

As from the above posts, in QEMU I have this memory map:

Code: Select all

0x359A0 -- 1 == 0x1 == 0x9F400
0x359B8 -- 2 == 0x9F400 == 0xC00
0x359D0 -- 2 == 0xF0000 == 0x10000
0x359E8 -- 1 == 0x100000 == 0x1FEFE000
0x35A00 -- 2 == 0x1FFE000 == 0x2000
0x35A18 -- 2 == 0xFFFC0000 == 0x40000
Look at entry number 4. No matter how much memory I give QEMU (I gave 2 MB) I get this funny bit.

Most importantly, it's marked as '1'. Clearly, it doesn't exist and screws up my memory calculation.

It's something wrong with QEMU. I always thought the constant size of 32MB was a problem with the grub mem_upper and men_lower fields… But it's QEMU, always reporting 32MB.

What. The. ****. Is. The. Problem?
I'll try and build qemu from source (This was from homebrew) later.
Tosi
Member
Member
Posts: 255
Joined: Tue Jun 15, 2010 9:27 am
Location: Flyover State, United States
Contact:

Re: Verification of GRUB Memory Map

Post by Tosi »

It is probably one, or both, of the following:

1. You are parsing the memory map incorrectly. The multiboot specification is a little vague on the exact structure of the memory map, but remember that the base address and length fields in the memory map are 64-bit quadwords, while the size and type-fields are 32-bit dwords, so you would define the structure something like this:

Code: Select all

#define GCC_PACKED __attribute__ ((packed))
typedef struct tagMMAPENTRYT
{
      uint32_t size;
      uint32_t base_lo;
      uint32_t base_hi;
      uint32_t len_lo;
      uint32_t len_hi;
      uint32_t type;
} GCC_PACKED mmap_entry_t;
This applies to x64 (which I think you're using?) too, just replace the base_lo/base_hi and len_lo/len_hi with single uint64_t definitions.
Also, you should probably not count on the type field being 4 bytes and use the size field to get the next entry in a loop.

2. You are using the -kernel flag with QEMU. QEMU's built-in multiboot implementation is lacking, I have looked at the code and the
size values in the memory map entries are off, along with other problems. It would be preferable to use a disk image with
GRUB (or any other good multiboot boatloader) installed and test with that.
User avatar
zhiayang
Member
Member
Posts: 368
Joined: Tue Dec 27, 2011 7:57 am
Libera.chat IRC: zhiayang

Re: Verification of GRUB Memory Map

Post by zhiayang »

Tosi wrote:It is probably one, or both, of the following:

1. You are parsing the memory map incorrectly. The multiboot specification is a little vague on the exact structure of the memory map, but remember that the base address and length fields in the memory map are 64-bit quadwords, while the size and type-fields are 32-bit dwords, so you would define the structure something like this:

Code: Select all

#define GCC_PACKED __attribute__ ((packed))
-snip-
This applies to x64 (which I think you're using?) too -snip-

2. You are using the -kernel flag with QEMU. QEMU's built-in multiboot implementation is lacking, I have looked at the code and the
size values in the memory map entries are off, along with other problems. It would be preferable to use a disk image with
GRUB (or any other good multiboot boatloader) installed and test with that.

1. I am using the multiboot.h file from the GNU website. No, I am not using x86_64. I will, but not yet.

2. No, I am running it off -cdrom, not the -kernel flag.

Also: Notice that this problem only occurs on QEMU; I get no such bogus reported memory on VMWare. Including the regions 2, 3 and 4 (the first few entries, minus the ROM code at high 3GB+) I get exactly 4MB, so no problem there. On QEMU, it always reports as about 31-32MB.



EDIT:
I'm making the decision not to put anything in lower memory for now, since I don't want to risk destroying anything there (the biggest fear being my precious memory map). Therefore, I'm storing it somewhere above 1MB. Unfortunately…

In QEMU, there's only one other region with type 0x01: that bogus bit to about 32MB that *doesn't exist*. I could always use VMWare, but it doesn't open/close fast enough and is a hassle to change settings.
sounds
Member
Member
Posts: 112
Joined: Sat Feb 04, 2012 5:03 pm

Re: Verification of GRUB Memory Map

Post by sounds »

I don't know why your QEMU is giving you problems but mine is fine. The first valid region at or above 1MB is exactly 1MB and it should be the rest of memory.

If you'd like I can post my QEMU version, E820 results, etc.
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Verification of GRUB Memory Map

Post by xenos »

Could you post your exact GRUB command line?
What happens if you enter the command "displaymem" in the GRUB command line? Could you post the output here?
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
sounds
Member
Member
Posts: 112
Joined: Sat Feb 04, 2012 5:03 pm

Re: Verification of GRUB Memory Map

Post by sounds »

Hmm, I don't use GRUB to boot, I prefer to write my own bootloader, but I installed GRUB on a disk image and booted it. I followed these instructions:

http://wiki.osdev.org/Disk_Images

So I downloaded http://download.berlios.de/libosdk/fd.img.bz2 which has GRUB 0.96

Code: Select all

$ bzip2 -dc fd.img.bz2 > fd.img
$ qemu-system-x86_64 -cpu Nehalem -m 4 -fda fd.img -curses

    GNU GRUB  version 0.96  (637K lower / 3064K upper memory)

 [ Minimal BASH-like line editing is supported.  For the first word, TAB
   lists possible command completions.  Anywhere else TAB lists the possible
   completions of a device/filename. ]

grub> displaymem
 EISA Memory BIOS Interface is present
 Address Map BIOS Interface is present
 Lower memory: 637K, Upper memory (to first chipset hole): 3064K
 [Address Range Descriptor entries immediately follow (values are 64-bit)]
   Usable RAM:  Base Address:  0x0 X 4GB + 0x0,
      Length:   0x0 X 4GB + 0x9f400 bytes
   Reserved:  Base Address:  0x0 X 4GB + 0x9f400,
      Length:   0x0 X 4GB + 0xc00 bytes
   Reserved:  Base Address:  0x0 X 4GB + 0xf0000,
      Length:   0x0 X 4GB + 0x10000 bytes
   Usable RAM:  Base Address:  0x0 X 4GB + 0x100000,
      Length:   0x0 X 4GB + 0x2fe000 bytes
   Reserved:  Base Address:  0x0 X 4GB + 0x3fe000,
      Length:   0x0 X 4GB + 0x2000 bytes
   Reserved:  Base Address:  0x0 X 4GB + 0xfffc0000,
      Length:   0x0 X 4GB + 0x40000 bytes

grub>
Post Reply