rookie step #1: Accessing high memory using BIOS pmode
Posted: Fri Jun 28, 2002 1:43 pm
This is interresting, for those who have large (>1Mb) kernel/datas to load in memory before their OS enters the protected mode, to be able to load datas directly above 0x00100000 in memory. To do this, remember your first must enable the A20 gate using your favourite method (see baby step #7 - here).
Once this is done, you can do your high-level load using INT 15h, svc 88h to move blocks between the temporary place where INT 13h put your datas (somewhere between 1000h:0000h and 9000h:ffffh seems a nice idea)
and there definitive place. Let's see this ...
---------------------------------------------------
gdt:
.dummy: dd 0,0 ; reserved by Intel, unused: keep it null.
.gdt: dd 0,0 ; set up by the BIOS interrupt for internal use. don't touch.
.start: descriptor_data(from_buffer, size=64K)
.dest: descriptor_data(target_linear_address, size=64K)
.bios_cs: dd 0,0 ; used by BIOS to access its own code. don't touch
.stack: dd 0,0 ; used by BIOS for its own stack. don't touch.
/** i'll expect you've filled the gdt.start and gdt.dest descriptors according
*** to yourneeds and respecting the GDT descriptor format by intel described here by CrazyBuddah
*** The amount of words to be copied (should be even) is in ecx.
***/
himem_copy:
push es
push ax
shr ecx,1 ; BIOS wants an amount of WORDS
push ds
pop es
mov si,GDT
mov ah, 87h
int 15h
pop ax
pop es
ret
--------------------------------------------------
How to use this ? look at this pseudo-code
char buffer[512];
long target=0x00200000; // for the example, load at 2Mb.
while (!done) {
bios_read_sector(lba2chs(i++),buffer);
hi_mem_cpy(src=buffer, dst=target, size=512);
target+=512;
}
---------------------------------------------------
and finally the wrapping code. I assume you generated the address of buffer
as a linear address from offset buffer + DS*16.
; *** void hi_mem_cpy(long src, long dst, long size)
hi_mem_cpy:
push bp
mov bp,sp
mov eax,[bp+4] ; skip bp & ip and access first dword of arguments.
mov edx,[bp+8] ; get dst
mov ecx,[bp+12] ; get size
mov [gdt.start],cx ; set source size
mov [gdt.start+2],ax ; set source address [0..16]
shr eax,16
mov [gdt.start+4],al ; set source address [16..23]
mov [gdt.start+5],90 ; read-only data segment.
mov word [gdt.start+6],0 ; we're below 1Mb so addr [24..32]=0.
mov [gdt.dest],cx ; set source size
mov [gdt.dest+2],dx ; set dest address [0..16]
shr ebx,16
mov [gdt.dest+4],dl ; set dest address [16..23]
mov [gdt.dest+5],92 ; read-write data segment.
mov byte [gdt.dest+6],0
mov [gdt.dest+7],dh
call hi_memcopy
pop bp
ret
--------------------------------------------------
I suggest you check it works with your fav' realmode debugger in a small DOS program before putting it blindly in a boot sector.
Once this is done, you can do your high-level load using INT 15h, svc 88h to move blocks between the temporary place where INT 13h put your datas (somewhere between 1000h:0000h and 9000h:ffffh seems a nice idea)
and there definitive place. Let's see this ...
---------------------------------------------------
gdt:
.dummy: dd 0,0 ; reserved by Intel, unused: keep it null.
.gdt: dd 0,0 ; set up by the BIOS interrupt for internal use. don't touch.
.start: descriptor_data(from_buffer, size=64K)
.dest: descriptor_data(target_linear_address, size=64K)
.bios_cs: dd 0,0 ; used by BIOS to access its own code. don't touch
.stack: dd 0,0 ; used by BIOS for its own stack. don't touch.
/** i'll expect you've filled the gdt.start and gdt.dest descriptors according
*** to yourneeds and respecting the GDT descriptor format by intel described here by CrazyBuddah
*** The amount of words to be copied (should be even) is in ecx.
***/
himem_copy:
push es
push ax
shr ecx,1 ; BIOS wants an amount of WORDS
push ds
pop es
mov si,GDT
mov ah, 87h
int 15h
pop ax
pop es
ret
--------------------------------------------------
How to use this ? look at this pseudo-code
char buffer[512];
long target=0x00200000; // for the example, load at 2Mb.
while (!done) {
bios_read_sector(lba2chs(i++),buffer);
hi_mem_cpy(src=buffer, dst=target, size=512);
target+=512;
}
---------------------------------------------------
and finally the wrapping code. I assume you generated the address of buffer
as a linear address from offset buffer + DS*16.
; *** void hi_mem_cpy(long src, long dst, long size)
hi_mem_cpy:
push bp
mov bp,sp
mov eax,[bp+4] ; skip bp & ip and access first dword of arguments.
mov edx,[bp+8] ; get dst
mov ecx,[bp+12] ; get size
mov [gdt.start],cx ; set source size
mov [gdt.start+2],ax ; set source address [0..16]
shr eax,16
mov [gdt.start+4],al ; set source address [16..23]
mov [gdt.start+5],90 ; read-only data segment.
mov word [gdt.start+6],0 ; we're below 1Mb so addr [24..32]=0.
mov [gdt.dest],cx ; set source size
mov [gdt.dest+2],dx ; set dest address [0..16]
shr ebx,16
mov [gdt.dest+4],dl ; set dest address [16..23]
mov [gdt.dest+5],92 ; read-write data segment.
mov byte [gdt.dest+6],0
mov [gdt.dest+7],dh
call hi_memcopy
pop bp
ret
--------------------------------------------------
I suggest you check it works with your fav' realmode debugger in a small DOS program before putting it blindly in a boot sector.