boot.asm:
Code:
extern meminit
[BITS 32]
global setup ;GRUB loads kernel and enters here
setup:
cmp eax,0x2BADB002
jne notmb
mov eax, 0xFFFF
mov esp, eax ;set temp stack pointer
mov ebp, eax ;set temp base pointer
push ebx ;push pointer to multiboot info
call meminit
pop ebx
notmb:
hlt
jmp notmb
;multiboot header info below
.
.
.
and here is meminit()
Code:
static uint32_t pages;
void meminit(multiboot_info_t *mb_info)
{
uint32_t *pdir, *ptab;
int i;
pages = ((mb_info->mem_lower + mb_info->mem_upper)*1024) /
PAGESIZE;
//Use the last page in memory for the page directory
pdir = (uint32_t *)(--pages * PAGESIZE);
//map the Page Directory to the last PDE
//this will now be mapped to virtual 0xffc00000
pdir[1023] = (uint32_t)pdir | PG_PRESENT | PG_WRITABLE;
//Identity Map the first 4Megs of memory
ptab = (uint32_t *)((--pages * PAGESIZE) | PG_PRESENT |
PG_WRITABLE);
for(i=0; i<1024; i++)
{
ptab[i]=(i*PAGESIZE)| PG_PRESENT | PG_WRITABLE;
}
pdir[0] = (uint32_t)ptab;
//set page directory base address
set_cr3(pdi
//enable paging
set_cr0(get_cr0 | CR0_PG);
}
using bochs, it reboots using that code.
but if i comment out the line that enables paging, it will hit the
notmb loop fine and endlessly loop like its supposed to. I dont see any
blatantly obvious problems. Could someone please help me with this?
I don't think the above line is generally safe. There's probably a reason
why GRUB returns separate memory counts, there're memory holes.
> //map the Page Directory to the last PDE
> //this will now be mapped to virtual 0xffc00000
> pdir[1023] = (uint32_t)pdir | PG_PRESENT | PG_WRITABLE;
>
> //Identity Map the first 4Megs of memory
> ptab = (uint32_t *)((--pages * PAGESIZE) | PG_PRESENT |
> PG_WRITABLE);
> for(i=0; i<1024; i++)
> {
> ptab[i]=(i*PAGESIZE)| PG_PRESENT | PG_WRITABLE;
> }
> pdir[0] = (uint32_t)ptab;
Here (in the above line) I think something important is missing.
> //set page directory base address
> set_cr3(pdi
Is this code the actual code you're trying to execute? The above line won't
even compile.
> //enable paging
> set_cr0(get_cr0 | CR0_PG);
get_cr0 doesn't look like a function either, so is this the actual code???
> }
>
>
> using bochs, it reboots using that code.
> but if i comment out the line that enables paging, it will hit the
> notmb loop fine and endlessly loop like its supposed to. I dont see
> any blatantly obvious problems. Could someone please help me with
> this?
What are these: PAGESIZE, PG_PRESENT, PG_WRITABLE, CR0_PG?
What are these: set_cr3(), set_cr0(), get_cr0?
Does GRUB do any page setup for you?
Where does GRUB load your code in memory?
Alex
Everything Alex says is right, but I don't think thats the problem.
Think about these 2 lines:
ptab = (uint32_t *)((--pages * PAGESIZE) | PG_PRESENT | PG_WRITABLE);
And later, in the loop:
ptab[i]=(i*PAGESIZE)| PG_PRESENT | PG_WRITABLE;
Now it should be clear whats causing the problem.
Hong
OK, I missed half of this. Once the two lines you mention are fixed, the
following line also needs fixing:
pdir[0] = (uint32_t)ptab;
Alex
that is the actual code,
the line: set_cr3(pdi accidentally got deleted a bit when copying.
the code
all those other functions you asked about are assembly stubs, elsewhere
in my code, but they should be self explanitory as to what they do if
you know about paging.
anyway, i made some of those changes, so the code looks like this now
for meminit()
static uint32_t pages;
void meminit(multiboot_info_t *mb_info)
{
uint32_t *pdir, *ptab;
int i;
pages = ((mb_info->mem_lower + mb_info->mem_upper)*1024) /
PAGESIZE;
//Use the last page in memory for the page directory
pdir = (uint32_t *)(--pages * PAGESIZE);
//map the Page Directory to the last PDE
//this will now be mapped to virtual 0xffc00000
pdir[1023] = (uint32_t)pdir | PG_PRESENT | PG_WRITABLE;
//Identity Map the first 4Megs of memory
ptab = (uint32_t *)(--pages * PAGESIZE);
for(i=0; i<1024; i++)
{
ptab[i]=(i*PAGESIZE)| PG_PRESENT | PG_WRITABLE;
}
pdir[0] = (uint32_t)ptab | PG_PRESENT | PG_WRITABLE;
//set page directory base address
set_cr3(pdir);
//enable paging
set_cr0(get_cr0 | CR0_PG);
}
I understand why i needed to change the line that said
ptab = (uint32_t *)((--pages * PAGESIZE) | PG_PRESENT | PG_WRITABLE);
bcause then the array would have been off by 0x3 when accessing it in
the loop.
but i dont understand the problem with the line in the loop that is
setting permissions to the PTE's themselves? anyway, that new code
still resets the computer. I'm assuming it is a triple fault or
something. i really dont understand what is wrong.
It was using that wrong "off by something" pointer.
> anyway, that new code
> still resets the computer. I'm assuming it is a triple fault or
> something. i really dont understand what is wrong.
Do you really know where your code is located? At what physical address? If
your code is located above 4 MB, the above won't work.
How does GRUB set the segment registers for your code? Are the base
addresses of the segment descriptors 0 for your code/data? If they aren't
the above can't work.
When you're done filling the tables, can you dump the following to the
screen to make sure everything makes sense:
pdir, pdir[0], pdir[1023]
ptab, ptab[0], ptab[1], ptab[1023]
What do you get?
Alex
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
i dont know why they are using the value 0x10 though.
should i maybe load a temp gdt before i enable paging and see if that
works?
You should have probably read the GRUB docs in the first place ;)
In case you've lost the link, it's all here:
http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
Alright, GRUB seems to be not doing anything funky according to the above
doc, so, my next question is how do you build what you feed to GRUB?
I'm particularly interested in how you link your thing and what addresses
you use. Don't tell me you don't know the addresses. :)
Alex
; Multiboot header
magic equ 0x1BADB002
flags equ 0x00000003
align 4
dd magic ; magic
dd flags ; flags
dd -(magic+flags) ; checksum
dd 0 ; header_addr (if flags bit 16 set)
dd 0 ; load_addr (if flags bit 16 set)
dd 0 ; load_end_addr (if flags bit 16 set)
dd 0 ; bss_end_addr (if flags bit 16 set)
dd 0 ; entry_addr (if flags bit 16 set)
dd 0 ; mode_type (if flags bit 2 set)
dd 0 ; width (if flags bit 2 set)
dd 0 ; height (if flags bit 2 set)
dd 0 ; depth (if flags bit 2 set)
Grub loads everything at 0x100000 (1mb). everything is compiled to
objects files, and
then everything is linked using this ld script:
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
phys = 0x100000;
ENTRY(setup)
SECTIONS
{
. = phys;
.text phys : AT(phys)
{
code = .; _code = .;
*(.text)
. = ALIGN(4096);
}
.data : AT(phys + ( data - code ) )
{
data = .; _data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + ( bss - code ))
{
bss = .; _bss = .;
*(.bss)
. = ALIGN(4096);
}
.rodata : AT(phys + (rodata - code ))
{
rodata = .; _rodata = .;
*(.rodata)
. = ALIGN(4096);
}
. = ALIGN(4096);
end = .; _end = .;
}
My ld doesn't like the above script. It barks at:
.text phys : AT(phys)
I used to use something like this for C (gcc=DJGPP):
OUTPUT_FORMAT("coff-go32")
ENTRY(__start)
FORCE_COMMON_ALLOCATION
SECTIONS
{
.text 0xC0000000: /* .text starts at 0xC0000000
virtual */
{
*(.text)
FILL(0x0) /* gap filler value */
. = ALIGN(0x1000); /* round up the size to 4KB page
size */
etext = .; _etext = .;
}
.data :
{
*(.data)
FILL(0x0)
. = ALIGN(0x1000);
edata = .; _edata = .;
}
.bss :
{
*(.bss)
FILL(0x0)
*(COMMON) /* place COMMONs to .bss */
FILL(0x0)
. = ALIGN(0x1000);
end = .; _end = .;
}
}
I think you may not need to specify both virtual and physical addresses. I
specify only the virtual one because the load address is the same by
default. They need to be different if the image is stored at different
location than used (e.g. ROM, from which it later gets copied to RAM).
Alex
Nope, even though I believe it must be something very simple. Did you try
searching for examples of GRUB use?
You could also try to boot a flat image, something very simple made with
e.g. nasm (it can produce an image). And maybe you could spend sometime
trying to repeat after GRUB -- emulate the load and execution (either in
real (tweaking the GRUB's code) or by hand with a fileviewer+disassembler
and calculator).
There's nothing impossbile, especially with the code of this size. I
sometimes dump the stack and other important things to memory or screen (if
possible) if things go wrong and study that along with the disassembly.
Takes time but lets find the problems.
Alex
And if mem_upper is smaller than 3MB, that means your pd and pt are
placed below 4MB, when you identity map the first 4MB, apart from
virtual address 0xffc00000, there is another address can access the pd,
and you may destroy it accidentally.
I don't know if this is the cause of your problem.
Hong