Page 1 of 3

RAM size? BIOS or other? [Working]

Posted: Sat Nov 17, 2007 8:42 pm
by LordMage
I got paging working and now I am working on my own system. I was reading the memory management 1 tutorial on osdever and It suggested using the BIOS to read an accurate RAM size, I then proceeded to look up INT15 and found a page of OsFaqWiki. That page has the appropriate BIOS info, but it also suggests a possible C/C++ solution for finding the RAM size but warns about dangers of shared memory devices.

I was wondering if I should just use the BIOS way of doing it or if anyone knows of a clever way to find the RAM size after my kernel is loaded. I would rather not have to put any important bits into my bootloader and transfer the info to my kernel. Just seems like back pedaling to me.

Posted: Sun Nov 18, 2007 12:05 am
by bewing
AFAIK the BIOS method is the only effective way to do it. I haven't coded it up yet, though. I'm still messing with my directory caches. I'll be curious to hear the other responses. :)

Posted: Sun Nov 18, 2007 5:58 am
by Kieran
If you boot using the GRUB bootloader you can read the memory size from the multiboot information block.

Re: RAM size? BIOS or other?

Posted: Sun Nov 18, 2007 6:21 am
by Brendan
Hi,
LordMage wrote:I was wondering if I should just use the BIOS way of doing it or if anyone knows of a clever way to find the RAM size after my kernel is loaded.
The first question is "how good is good enough"?

For most modern computers "Int 0x15, eax = 0xE820" is all you need.

For older computers there's older BIOS functions, and for some computers none of the BIOS functions are reliable (they're not supported by the BIOS, or return values my OS doesn't trust). My boot code for the PC BIOS tries these things (in order, until one works):
  • - Int 0x15, EAX = 0xE820
    - Int 0x15, AX = 0xE801
    - Int 0x15, AH = 0x8A
    - Int 0x15, AX = 0xDA88
    - Int 0x15, AH = 0x88
    - CMOS locations 0x17 and 0x18
    - Manual probing
All of these build a list of areas to pass to the OS, similar to that returned by "Int 0x15, eax = 0xE820", including areas that may not be usable for memory mapped PCI devices (e.g. the area from 0xFE000000 to 0xFFFFFFFF is assumed to be "reserved" if I only know about RAM areas).

For all of these my code is cautious. It handles any bugs mentioned in Ralph Brown's Interrupt List and handles limited return results (e.g. some BIOS functions return the maximum they can represent when more RAM is present).

I also sanitize the resulting list before the OS sees it - make sure that no areas overlap, combine adjacent areas of the same type together, sort it so that it's in order, etc.

My advice would probably be to get the information from "Int 0x15, eax = 0xE820" and copy it to a list in memory, and perhaps sanitize it; and then write the rest of your OS (if "Int 0x15, eax = 0xE820" doesn't work, just display an error message and lock up for now). You can always add other methods of "physical address range detection" later on, if you need to.
LordMage wrote:I would rather not have to put any important bits into my bootloader and transfer the info to my kernel. Just seems like back pedaling to me.
I'm mostly the reverse - I don't want to put anything in the kernel that the kernel won't need after boot (I don't want stuff like "physical address range detection" taking up RAM for no reason after boot). In addition my early boot code is responsible for gathering information from the firwmare and putting the computer into a well-defined state, so that the rest of the OS doesn't need to care what the firmware was (PC BIOS, EFI, LinuxBIOS, OpenFirwmare, aeBIOS, custom ROM code, etc) or where the boot loader's list of physical address ranges actually came from.


Cheers,

Brendan

Re: RAM size? BIOS or other?

Posted: Sun Nov 18, 2007 6:55 am
by egos
LordMage wrote:I was wondering if I should just use the BIOS way of doing it or if anyone knows of a clever way to find the RAM size after my kernel is loaded. I would rather not have to put any important bits into my bootloader and transfer the info to my kernel. Just seems like back pedaling to me.
There are two good ways to solve this problem at all. Both use the BIOS.
1. Second stage bootloader detects available memory using the BIOS and passes the data to the PM kernel.
2. Bootloader loads the kernel, which consists of two parts (RM part and PM part), then the kernel detects available memory using the BIOS.

Posted: Sun Nov 18, 2007 11:15 am
by LordMage
Alright, I am going to try to get the info from the BIOS until I can find a different way, I still don't like it. I have made a funciton based on the stuff on the wiki and I just want to know if this is close to what you guys are using. I am implementing the function the best way I know how, ASM is not my strong point. I did have to leave out the ES:DI part, not sure if I was supposed to inlclude it in the first place though. From what I read I thought I should have put 01h into ES:DI but I am not sure how so I left it out. on the bright side when I check the carry flag using JB everything seems to have worked. here is the code please let me know if this is close to right. I still have to tweak my stack after I move the stack pointer later in the bootloader but this is the important bit because if this is wrong then I will be passing junk onto my kernel.

Code: Select all


;-------------------------------;
;   Read size of RAM from BIOS  ;
;-------------------------------;

mov ax, 0xE820
mov eax, 0xE820
mov edx, 0x534D4150
mov ebx, 0
mov ecx, 20

int 0x15

jb RAMerror
push eax
push es
push di
push ebx
push ecx


Posted: Sun Nov 18, 2007 11:34 am
by cyr1x
How to determine the RAM size on a NUMA system? Does the BIOS provide a
special Memory-Map? Or can you get the size from the ACPI-tables?

Posted: Sun Nov 18, 2007 12:24 pm
by Mounter
I use this code for get the memory size:

Code: Select all

unsigned long getRamSize(const uint08_t unit)
{
	unsigned long rez;
	unsigned long memsize;
	unsigned long last;
	unsigned char tmp;

	memsize = 1;            // 1MB
	rez = 0x12;             // Number for teste
	last = 0x200000-0x1;    // From 2MB of RAM

	while (rez == 0x12) {
		tmp = *(char *)last; 
		*(char *)last = rez;   
		rez = *(char *)last; 
		last += 0x100000;

		if (rez == 0x12) {
			memsize++;
			*(char *)last = tmp;
		}
	}

	switch (unit)
	{
        /*case 'M':
            break;*/
        case 'K':
            memsize <<= 0x0A;
            break;
        case 'B':
            memsize <<= 0x14;
	}

    return memsize;
};

Posted: Sun Nov 18, 2007 1:17 pm
by JAAman
if i understood that correctly, your probing -- that is very dangerous, and in some cases (admittedly rare) can permanently destroy your computer so that it will never work again...

even if that doesnt happen, there is a good chance that it will return the wrong answer, and possibly prevent the computer from working properly -- there are portions of RAM you shouldnt ever touch, and there are often holes in ram, where the ram stops, then starts again later, and sometimes there are hardware devices located in between portions of ram (and if you try to write to these devices, you can permanently damage your computer -- depending on what you write, and to what part of which device) even if you dont damage the computer with it, sometimes writing to a device will look exactly the same as writing to ram, in which case your test will say its real memory, where it really isnt (for example, video ram, or shared ram acting as video ram -- neither of these can be detected with your program)

the only way to know for certain what is real memory, and what is safe to write to, is to ask the one that knows all -- the BIOS (technically, you could also do it with a MB driver, but since you would need a separate driver for each MB, it becomes very impractical -- even MS doesnt do that...

Posted: Sun Nov 18, 2007 2:22 pm
by Dex
Try something like this;

Code: Select all

 ;----------------------------------------------------;
 ; Get Ram Size.                                      ;
 ;----------------------------------------------------;
GetRamSize:
	mov   ax,0xe801
	int   15h
	jc    @f   ;may jump to some error code
	movzx ebx, bx
	shl   ebx,6
	movzx eax, ax
	add   ebx,eax
	mov   [ExtMemorySize],ebx
	call  TotalRam
@@:
	ret

 ;----------------------------------------------------;
 ; TotalRam.                                          ;
 ;----------------------------------------------------;
TotalRam:
	shr   ebx,10
	inc   ebx
	test  ebx,1
	jnz   @f
	mov   [TotalMemoryMB],ebx
	ret
@@:	inc   ebx
	mov   [TotalMemoryMB],ebx
	ret

Posted: Sun Nov 18, 2007 2:43 pm
by LordMage
thanks Dex, that is what I wanted but it looks like you are passing variables which would mean that my kernel would already be loaded??? does your code expect me to be in V86 mode? Isn't that the older version of the call, is it reliable enough? I will implement it, thanks

Posted: Sun Nov 18, 2007 3:37 pm
by Mounter
I use this function only in the initiation of the kernel, and write it on a variable.

The function save the memory position, write and test if it's 0x12 and return the original value to the memory position, I don't consider it so dangerous.

Sorry for my inglish, but I am a brazillian programmer...

Posted: Sun Nov 18, 2007 6:09 pm
by LordMage
well, I instituted the function the Dex gave me, I also tried my own implimentation and both of them returned a funny value. Well, at least one I wasn't expecting. does this look right

0xE820

RAM: 589845

0xE801

RAM: 589832
Extended Memory: 1049647

I mean bochs says the system has 0x02000000 RAM and then I get those returns from the RAM functions it doesn't make sense to me.

Posted: Sun Nov 18, 2007 8:48 pm
by JAAman
The function save the memory position, write and test if it's 0x12 and return the original value to the memory position, I don't consider it so dangerous.
consider this:
a device has a register at location x, this register regulates a timing clock for a chip -- you write '0x12' into that location -- you just told it to overclock that chip to 250GHz -- and now that chip will never function again -- all thats left is a pile of ashes

when i said it was dangerous, i didnt mean you could change the values at that location (besides, you cannot prevent the computer from relying on the information at that memory address while you are 'testing' it), i meant placing the wrong value in a hardware register, will itself cause damage to the computer -- sometimes rebooting will fix it, but sometimes the damage may be permanent -- and if it is a hardware register, you (most likely) cannot restore the previous value, because you dont know what the previous value was -- sometimes the register is read only (in which case the value you read from it was completely nonsense) and sometimes the same address will work as a read port for another register, and the value you read, was the device status - not the previous value for that port

and sometimes the read will work perfectly well, and will decide that there is memory at that address, but in fact its not memory, but either a r/w port, or onboard memory from some device (such as video memory -- which on many systems is actually part of system memory, and the only way to tell that it is separate, is to ask the BIOS), your function will declare all device memory to be system memory

Posted: Mon Nov 19, 2007 12:36 pm
by Dex
LordMage wrote:well, I instituted the function the Dex gave me, I also tried my own implimentation and both of them returned a funny value. Well, at least one I wasn't expecting. does this look right

0xE820

RAM: 589845

0xE801

RAM: 589832
Extended Memory: 1049647

I mean bochs says the system has 0x02000000 RAM and then I get those returns from the RAM functions it doesn't make sense to me.
Are you use the code as i posted ?, also how are you converting it ?.
As that same code works fine in BOCHS for my OS, and it work on all real PC's, i and other have tested it on (other 100)
Running in bochs