Hi,
NickJohnson wrote:Brendan wrote:These fields are both 32-bit unsigned integers. For GCC, "unsigned long" is probably a 64 bit integer. This means that you're loading the highest 32-bits with trash.
Wait, what? I'm pretty sure unsigned long on 32-bit GCC is a 32-bit integer (unsigned long long is 64-bit), and casting any integer to an integer of a larger width should never change its numerical value. I suspect the problem is more with the fields not being filled in correctly by GRUB, or the OP's print function having issues.
For some reason I assumed a 64-bit GCC was being used due to the "4294967294179488" value being displayed, and didn't look at the code closely enough.
NickJohnson wrote:Also, the calculation for how much memory you have is mem_upper - mem_lower, not mem_lower + mem_upper... (not that that sort of error would give you this problem.)
From
a version of the multiboot specification:
"
If bit 0 in the ‘flags’ word is set, then the ‘mem_*’ fields are valid. ‘mem_lower’ and ‘mem_upper’ indicate the amount of lower and upper memory, respectively, in kilobytes. Lower memory starts at address 0, and upper memory starts at address 1 megabyte. The maximum possible value for lower memory is 640 kilobytes. The value returned for upper memory is maximally the address of the first upper memory hole minus 1 megabyte. It is not guaranteed to be this value."
The correct calculation to determine "the number of KiB of RAM below the first hole, or less" would be "mbd->mem_lower + mbd->mem_upper" (which is literally the number of KiB below 0x00100000 plus the number of KiB between 0x00100000 and *somewhere*). Because both fields are KiB and not bytes, and because there's at least one hole below 0xFFFFFFFF, you shouldn't need to worry about overflows for this calculation if you use 32-bit (signed or unsigned) arithmetic.
The main point is that if GRUB says that the "mem_upper" and "mem_lower" fields fields are valid, and if these fields actually are valid; then "the number of KiB of RAM below the first hole, or less" is virtually useless anyway. There's no point bothering to fix "virtually useless" code.
There are only 3 values that might be useful:
- The amount of RAM that is physically installed. To get this value you need to use SMBIOS, chipset specific knowledge, or guesswork/estimation.
- The amount of RAM that is actually usable. To get this value you need parse the memory map that GRUB got from the BIOS (e.g. from "int 0x15, eax=0xE820") and find the sum of areas marked as either "usable RAM" or "ACPI reclaimable".
- The amount of RAM that is currently free. To get this value early during boot you'd need parse the memory map that GRUB got from the BIOS (e.g. from "int 0x15, eax=0xE820") and find the sum of areas marked as "usable RAM" (but not "ACPI reclaimable"); then subtract the amount of RAM used by your own code (including stack, modules, etc), and subtract the amount of RAM GRUB left "in use" for its boot information table. Later (after boot and/or after you've setup your own physical memory manager to track free pages) you should have better ways of doing this.
Finally, in my opinion (for a "production quality" OS), GRUB's code is relatively dodgy anyway. The code that sets the "mem_upper" and "mem_lower" fields doesn't use many of the possible BIOS functions and has too few sanity checks. In a similar way, the code that gets the memory map from the BIOS "int 0x15, eax=0xE820" function doesn't do many sanity checks and doesn't do *any* post-processing (e.g. doesn't sort the list, doesn't remove redundant entries, doesn't merge "similar but adjacent" entries, doesn't check for or handle overlapping areas, etc). In addition to these problems (or possibly because of these problems) GRUB also allows the user to screw it all up (or possibly allows the user to override it when GRUB screws it up) via. an "
uppermem" command.
Note: Despite my comments above; GRUB's memory detection code should work fine on almost all
modern computers, and is adequate for a hobby OS; especially if you refuse to use the "mem_upper" and "mem_lower" fields, and especially if you sanitize the memory map yourself.
Cheers,
Brendan