How to fix memory manager

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.
Post Reply
oscoder
Member
Member
Posts: 59
Joined: Mon Mar 27, 2006 12:00 am
Location: UK

How to fix memory manager

Post by oscoder »

Hi,
I haven't posted any of my problems here is a while - development has been going rather smoothly! However, I now have a bug in my (stack based) memory manager, and I cannot fathom what is causing it.

The problem is this: the stack-filling function pushes memory addresses onto the stack that is higher than the highest memory address. Let me give you an example - on bochs, the highest memory address is 0x200000, but when I allocate a page from the stack, it returns 0x27FF00 as an address! The same thing happens on real hardware - highest address should be 0x107EB000, memory allocated is at 0x107EB000.

I have some code that gets the memory map from grub, which I won't post here - I've tested it and it returns the correct base and limit for each part of useable memory (checked against the multiboot example kernel).

This is the code that fills the memory map:

Code: Select all

 /*Fill stack through use of memory map*/
	for(i=0; i<memholes; i++) {
	  if(memhole_type[i] == 0x01 && (memhole_base[i] + memhole_limit[i])>(SYSMEM_BASE + SYSMEM_LIMIT)) { /*useable memory, that is not sysmem*/
		this_base = memhole_base[i];			/*temp var's to use*/
		this_limit = memhole_limit[i];
		if((memhole_base[i] % PAGE_SIZE) != 0)	/*make sure everything's on a page boundary*/
		  this_base += PAGE_SIZE - (memhole_base[i] % PAGE_SIZE);
		if((memhole_limit[i] % PAGE_SIZE) != 0)
		  this_limit -= (memhole_limit[i] % PAGE_SIZE);
		if(this_base < (SYSMEM_BASE + SYSMEM_LIMIT))
		  this_base = SYSMEM_BASE + SYSMEM_LIMIT;
		for(j=this_base; j<(this_base+this_limit); j+=PAGE_SIZE)
		  pmem_stack_push(j);
	  }
	}
And here is the code that gets stuff on and off of the alloc stack:

Code: Select all

void* p_alloc_stackmem_page()
{
 return((void *)pmem_stack_pop());
}

inline int pmem_stack_push(POINTER mem_addr)
{
	if((unsigned long) stack < (stack_base+stack_limit)) {
	  stack++;				/*increase top of stack*/
	  *stack = mem_addr;	/*put address at the top of the stack*/
	} else {
 return(1);
	}
 return(0);
}

inline POINTER pmem_stack_pop()
{
 POINTER retval;
	if(((unsigned long) stack) > stack_base) {
	  retval = *stack;	/*get top of stack value*/
	  stack--;			/*shrink stack*/
  return(retval);
	} else {
  return(0);
	}
}
(POINTER is an unsigned long -> used to reference memory where I'd rather not use void* or int*)

I can provide more of the code and explanation of it if nescessary.

Thanks in advance for your help,
OScoder
User avatar
naiksidd_85
Member
Member
Posts: 76
Joined: Thu Jan 17, 2008 1:15 am

Post by naiksidd_85 »

hi,
I have some code that gets the memory map from grub, which I won't post here - I've tested it and it returns the correct base and limit for each part of useable memory (checked against the multiboot example kernel).
can you pls post the code or a link which will help me for the same.[/quote]
Learning a lot these days THANKS to OSdev users
oscoder
Member
Member
Posts: 59
Joined: Mon Mar 27, 2006 12:00 am
Location: UK

Post by oscoder »

Hi,
I've fixed the problem now, with the help of some peoples on #osdev on freenode. The problem was that I was increasing the base of a memory section (as show below), without increasing the limit when the section should have been shrunk:

Code: Select all

      if(this_base < (SYSMEM_BASE + SYSMEM_LIMIT))
        this_base = SYSMEM_BASE + SYSMEM_LIMIT;
fixed code:

Code: Select all

		if(this_base < (SYSMEM_BASE + SYSMEM_LIMIT)) {
		  this_limit -=  (SYSMEM_BASE + SYSMEM_LIMIT) - this_base;	 /*if we're increasing the lower boundary, we must shrink the overall segment*/
		  this_base = (SYSMEM_BASE + SYSMEM_LIMIT);
		}

@naiksidd_85:
Do you mean getting the memory map from grub as per the multiboot standard, or getting a map from the BIOS in the first place?

This is an example kernel that should show you how to do it: (it's waaay better than my code, which is only half-done atm)
http://www.gnu.org/software/grub/manual ... -code.html

As to loading a memory map in the bootloader, the following may work (taken from an old bootloader found on my PC - written by 'James Pritchett'):

Code: Select all

;==========Read in Memory Map==========
;Because the BIOS knows essentially everything about the RAM (and we can't figure most
;of it out ourselves, we use the BIOS here to load a memory map into RAM at 0x9B004. 
;Note that even though this claims to be a full map, the base + length of the first
;entry (we'll get into structure in the C code) doesn't always equal the base of the 
;the next entry (there are some holes in the map). The end result of adding the lengths
;together is however, acceptable (it registers 99%-99.99% of the RAM). Hopefully this
;will be refined later on somehow.

readmemmap:
mov ebx, 0x9A00		;Initialize es:di to be 0x9B08 - 20 because 20 will be added 
mov es, bx		;upon starting the function.
mov di, 0xFF4		

xor ebx, ebx		;EBX=0 for this 

ramloop:
mov eax,0x0000E820	;EAX=E820h
mov edx,0x534D4150	;EDX='SMAP'
mov ecx,20		;ECX=Amount to read per call (>=20)
add di,20		;Increment DI by 20 each time to move to the next
			;place in RAM the entry should be loaded. 
int 0x15
inc byte [mmapentries]
jc error		;If there was an error, error out
cmp ebx, 00000000h	
je saveentries		;If ebx=0 then we're done
jmp ramloop		;Otherwise, continue reading.

error:
jmp error

saveentries:
mov ax, 9b00h
mov es, ax
mov edi, 0x4
mov esi, mmapentries
movsb
ret
Hope that helps,
OScoder
Post Reply