trying to get into pm and back
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
trying to get into pm and back
hi
i was trying to write some code in nasm that boots up and then enters protected mode, but it doesnt work...would someone please have a look at it..the first file (myboot.asm) is the 512 byte boot code read from the disk..the second file (mykernel1.asm) is the "kernel" and it needs to be placed on the floppy right after the 512 byte boot sector...the boot code reads the kernel from the floppy and jumps there..this works, i know for sure...you can try by commenting out all the switch-to-pm related stuff as described in the comments
the 512 byte boot sector asks the user to press a key and it will display the ascii and scan codes of the key hit; only when the user presses enter will the boot code load the kernel...i compile with:
nasm -f bin myboot.asm -o myboot.bin
nasm -f bin mykernel1.asm -o mykernel1.bin
dd if=mykernel1.bin of=myboot.bin count=1 seek=1
myboot.bin then is the floppy which i load in qemu
thanks for any help!
martin
i was trying to write some code in nasm that boots up and then enters protected mode, but it doesnt work...would someone please have a look at it..the first file (myboot.asm) is the 512 byte boot code read from the disk..the second file (mykernel1.asm) is the "kernel" and it needs to be placed on the floppy right after the 512 byte boot sector...the boot code reads the kernel from the floppy and jumps there..this works, i know for sure...you can try by commenting out all the switch-to-pm related stuff as described in the comments
the 512 byte boot sector asks the user to press a key and it will display the ascii and scan codes of the key hit; only when the user presses enter will the boot code load the kernel...i compile with:
nasm -f bin myboot.asm -o myboot.bin
nasm -f bin mykernel1.asm -o mykernel1.bin
dd if=mykernel1.bin of=myboot.bin count=1 seek=1
myboot.bin then is the floppy which i load in qemu
thanks for any help!
martin
- Attachments
-
- myboot.asm
- (2.51 KiB) Downloaded 27 times
-
- mykernel1.asm
- (3.52 KiB) Downloaded 24 times
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
ive seen examples of how its done before but the problem here is i am manipulationg the gdt data and code descriptors while still in real mode to reflect the exact same addresses as the current code in real mode and im not sure i i have done that correctly...also, i am not *jumping* into pm but calling it for clearing the pipe and i also dont know if theres any problem with that...in short, im stuck, i dont see the problem why my code doesnt work
Hi,
When you use "BITS 32" or "BITS 16" you're only telling the assembler what type of code to generate, but the CPU itself doesn't know anything about it.
You need to make sure that code assembled after a "BITS 32" is executed when the CPU is using a 32-bit code segment, and you also need to make sure that code assembled after a "BITS 16" is executed when the CPU is using a 16-bit code segment.
Also, it's impossible to use a 32-bit code segment without enabling protected mode. This means you'd need a sequence like this:
Cheers,
Brendan
When you use "BITS 32" or "BITS 16" you're only telling the assembler what type of code to generate, but the CPU itself doesn't know anything about it.
You need to make sure that code assembled after a "BITS 32" is executed when the CPU is using a 32-bit code segment, and you also need to make sure that code assembled after a "BITS 16" is executed when the CPU is using a 16-bit code segment.
Also, it's impossible to use a 32-bit code segment without enabling protected mode. This means you'd need a sequence like this:
Code: Select all
BITS 16
**Switch to protected mode**
jmp far 0x10:.foo ;Load a 32-bit code segment
BITS 32
.foo:
**Do 32-bit stuff**
jmp 0x18:.bar ;Load a 16-bit code segment
.bar:
**Do 16-bit protected mode stuff
**Disable protected mode**
**Do 16-bit real mode stuff**
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
you're probably referring to this part of the code, i'll put the rest of my reply as comment into the code ok:
any hints?
ps: do i HAVE to jmp into protected mode or is there a way to call and return (as attempted in this example)???
Code: Select all
start:
pusha
pushf
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov si, txt0 ;tell user that
call printstring ;we're entering protected mode
;if you leave out everything between here and (1), it works well
mov ax, cs
;we are still in real mode; i am using the "BITS 32" directive because i want to calculate the linear address of my real mode code segment and place it into gdt_code in the descriptor table so that my pm code segment has the same base address as my real mode code segment, so i need to make sure that something like "eax" really be assembled into an instruction that refers to the 32-bit register (linear base address of code segment *could* require up to 20 bits)
BITS 32
movzx eax, ax
shl eax, 4 ;eax now contains linear address of real mode cs
mov ebx, eax ;here we
shr ebx, 16 ;write linear
mov [cbase1+1], bl ;address of real mode
mov ebx, eax ;code segment
mov [cbase1+2], bh ;into code segment descriptor
mov [cbase2], bl ;in gdt
;here i am going back to "BITS 16" because i want to make sure that "ax" means the 16-bit register
BITS 16
mov ax, ds
;here i am doing the same as above, but for the data segment, instead of the code segment
BITS 32
movzx eax, ax
shl eax, 4 ;eax now contains linear address of real mode ds
mov ebx, eax ;here we
shr ebx, 16 ;write linear
mov [dbase1+1], bl ;address of real mode
mov ebx, eax ;data segment
mov [dbase1+2], bh ;into data segment descriptor
mov [dbase2], bl ;in gdt
mov ebx, 0ffffffffh ;calculate highest possible value
sub ebx, eax ;for pm stack pointer
mov [stack_pointer], ebx ;and store in [stack_pointer]
BITS 16
;now let's prepare for entry into protected mode
push ds ;save these
push es ;registers
mov bx, ss ;bx=old stack segment ;for future
mov cx, sp ;cx=old stack pointer ;restoration
mov ax, 0ch
mov ds, ax
mov es, ax
mov ss, ax
;here i am using the "BITS 32" directive because i need to make sure that "esp" refers to the 32-bit register and not the 16 bit register
BITS 32
mov esp, [stack_pointer]
cli
lgdt [gdt_desc]
mov eax, cr0 ;enable
or eax, 1 ;protected
mov cr0, eax ;mode
call 08h:enter_pm
mov eax, cr0 ;disable
or eax, 0 ;protected
mov cr0, eax ;mode
sti
BITS 16
mov ss, bx ;fortunately
mov sp, cx ;we saved these registers
pop es ;before entering
pop ds ;protected mode
mov si, txt1 ;tell user that
call printstring ;we're back from protected mode
;(1)
popf
popa
retf
ps: do i HAVE to jmp into protected mode or is there a way to call and return (as attempted in this example)???
brendan gave the correct hints and your code does not use them. Try again . You can call protected mode but then you have to take into account the fact that your push/pop call/ret behaviour changes, so it is better to do it with a jump.
You cannot use BITS 32 before you have done the jump, because the compiler might generate opcode the PC does not understand at that time.
Again use Brendan's framework.
You cannot use BITS 32 before you have done the jump, because the compiler might generate opcode the PC does not understand at that time.
Again use Brendan's framework.
Author of COBOS
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
okay, but when i am in real mode, an instruction like
refers to the 16-bit register ax rather than eax, right?
how can i use the eax register for calculations in real mode?
Code: Select all
mov eax, 12
how can i use the eax register for calculations in real mode?
Wrongsancho1980 wrote:okay, but when i am in real mode, an instruction like
refers to the 16-bit register ax rather than eax, right?Code: Select all
mov eax, 12
Just use the instruction you quoted.how can i use the eax register for calculations in real mode?
While the opcode that means "mov eax, 12" in 32-bit mode will reference ax in 16-bit mode, there is an encoding to use eax instead while in 16-bit mode (using a prefix byte). If "[BITS 16]" is in effect, the assembler will put that prefix before your instruction automatically if you use eax instead of ax.
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
hi
i've tried it out and followed your steps..it still doesnt work for me, please can you have a look at it, mykernel.asm now looks like this (remarks in code):
[edit]id better upload it again, this looks terrible[/edit]
i've tried it out and followed your steps..it still doesnt work for me, please can you have a look at it, mykernel.asm now looks like this (remarks in code):
[edit]id better upload it again, this looks terrible[/edit]
- Attachments
-
- mykernel2.asm
- (4.37 KiB) Downloaded 49 times
Hi,
This code:
Will trash the "type" field of each descriptor. Change to:
For this code:
I have a feeling the offset is wrong - the "dd gdt" may need to be "dd gdt + 0x200" because the boot loader loads the code at 0x0020:0x0000.
If you are loading the kernel code at 0x0020:0x0000 (or 0x00000200) then you're trashing the BIOS's data area while you're using the BIOS. I'd choose somewhere else to load the kernel (then disable IRQs and relocate it when you're finished with the BIOS if it needs to be at 0x00000200).
This code doesn't do anything:
Instead you'd want:
Once you disable protected mode CS will still equal 0x18 (left over from 16-bit protected mode). If an interrrupt occurs the CPU will push CS:IP on the stack (or 0x0018:0x????) and then do the interrupt and try to return to 0x0018:0x????). When it does try to return the 0x18 won't be correct because you're not in protected mode anymore. To fix this you should do something like this:
Also, in your boot loader you should probably change:
To:
I'm not sure if there's other problems, but that should give you something to start with...
Cheers,
Brendan
This code:
Code: Select all
mov [cbase1], bx ;of the real mode cs
shr ebx, 16 ;into the base fields of
mov [cbase2], bx ;the 32-bit code segment in the gdt
mov ebx, eax ;move the linear address
mov [cbase16_1], bx ;of the real mode cs
shr ebx, 16 ;into the base fields of
mov [cbase16_2], bx ;the 16-bit code segment in the gdt
Code: Select all
mov [cbase1], bx ;of the real mode cs
shr ebx, 16 ;into the base fields of
mov [cbase2], bl ;the 32-bit code segment in the gdt
mov ebx, eax ;move the linear address
mov [cbase16_1], bx ;of the real mode cs
shr ebx, 16 ;into the base fields of
mov [cbase16_2], bl ;the 16-bit code segment in the gdt
For this code:
Code: Select all
gdt_desc:
dw gdt_end - gdt - 1 ;limit of the gdt
dd gdt ;base address of the gdt
If you are loading the kernel code at 0x0020:0x0000 (or 0x00000200) then you're trashing the BIOS's data area while you're using the BIOS. I'd choose somewhere else to load the kernel (then disable IRQs and relocate it when you're finished with the BIOS if it needs to be at 0x00000200).
This code doesn't do anything:
Code: Select all
mov eax, cr0 ;disable
or eax, 0 ;protected
mov cr0, eax ;mode
Code: Select all
mov eax, cr0 ;disable
and al, 0xFE ;protected
mov cr0, eax ;mode
Code: Select all
mov eax, cr0 ;disable
and al, 0xFE ;protected
mov cr0, eax ;mode
jmp 0x0020:.here
.here:
sti
Also, in your boot loader you should probably change:
Code: Select all
mov [drive], dl
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0ffffh
Code: Select all
mov ax, cs
mov ds, ax
mov [drive], dl
mov es, ax
cli
mov ss, ax
mov esp, 0fffch
sti
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
thank you so much! it works now, i only had to make very few corrections to what you suggested...seems like this whole mode-switching is pretty subtle! i only have one question: why do i have to disable the interrupts in the boot loader? it works like that! do you have an explanation for that? i mean, your argument sounds reasonable, but still im surprised that it works without the disabling...
thanks
martin
ps: and by the way, the bootloader doesnt really load the kernel at 0x0020:0x0000! the bootloader itself is loaded at 07C0h:0 and it loads the kernel exactly 512 bytes after that
pps: i only now realised that you actually suggested to replace sp with esp and change 0ffffh for 0fffch in
Whats the reason for that?
And also one more question please: when going back into real mode from protected mode, why is it necessary to first load a 16-bit protected mode segment? why can i not jump directly to the 16-bit real mode segment straight after turning off pm??
thanks
martin
ps: and by the way, the bootloader doesnt really load the kernel at 0x0020:0x0000! the bootloader itself is loaded at 07C0h:0 and it loads the kernel exactly 512 bytes after that
pps: i only now realised that you actually suggested to replace sp with esp and change 0ffffh for 0fffch in
Code: Select all
mov esp, 0fffch
And also one more question please: when going back into real mode from protected mode, why is it necessary to first load a 16-bit protected mode segment? why can i not jump directly to the 16-bit real mode segment straight after turning off pm??
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
The stack must be aligned to a (I think) DWORD (4 byte) boundary.sancho1980 wrote:pps: i only now realised that you actually suggested to replace sp with esp and change 0ffffh for 0fffch in
Whats the reason for that?Code: Select all
mov esp, 0fffch
Hi,
Misaligned data slows the CPU down, and everything should have "natural" alignment to improve performance (words with 2 byte alignment, dwords with 4 byte alignment, etc). Setting SP to 0xFFFF means that every push and every pop is misaligned.
I also tend to use 32-bit registers for addresses (in real mode) because the CPU is more flexible with 32-bit addressing modes. For example, you can't do "mov cx,[ax*4+sp]" but you can do "mov cx,[eax*4+esp]". It's also possible to use the same stack in real mode, 16-bit protected mode and 32-bit protected mode, where you change SS but leave ESP to whatever it was (although this can cause alignment problems if you switch from a 16-bit stack to a 32-bit stack).
It also uses the BIOS to read a sector from disk to ES:BX (where ES:BX = 0x0020:0x0000).
This means (for example) if you enable protected mode and set the limit on GS to 4 GB and then disable protected mode, then the limit on GS will still be 4 GB (and not 64 KB) even though you're in real mode, because the CPU doesn't reset the segment limit. Some people (including me) take advantage of this behavour to have real mode with no segment limits, so they can access everything above 1 MB while still using real mode BIOS functions.
This also means that if you're executing 32-bit protected mode code with CS limit set to 4 GB, it may be possible to switch back to real mode and still have 32-bit default address size, 32-bit default operand size and a 4 GB CS limit (which will make 16-bit BIOS code crash).
Cheers,
Brendan
In the boot loader, I'd disable interrupts, then change SS, then change ESP/SP, then enable interrupts again. The reason for this is that it may be possible to get an IRQ after you've changed SS but before you've changed ESP/SP. For example, if SS:SP was left as 0x3000:0x0200 by the BIOS and you're changing it to 0x07C0:0xFFFC, then there's a very small amount of time where SS:SP would be 0x07C0:0x200 (and your code would be trashed if an IRQ occurs). Modern Intel CPUs do actually disable interrupts for one instruction after loading SS so this doesn't happen (mostly because of old buggy software), but there's no guarantee that CPUs from all other manufacturers do this.sancho1980 wrote:thank you so much! it works now, i only had to make very few corrections to what you suggested...seems like this whole mode-switching is pretty subtle! i only have one question: why do i have to disable the interrupts in the boot loader? it works like that! do you have an explanation for that? i mean, your argument sounds reasonable, but still im surprised that it works without the disabling...
Misaligned data slows the CPU down, and everything should have "natural" alignment to improve performance (words with 2 byte alignment, dwords with 4 byte alignment, etc). Setting SP to 0xFFFF means that every push and every pop is misaligned.
I also tend to use 32-bit registers for addresses (in real mode) because the CPU is more flexible with 32-bit addressing modes. For example, you can't do "mov cx,[ax*4+sp]" but you can do "mov cx,[eax*4+esp]". It's also possible to use the same stack in real mode, 16-bit protected mode and 32-bit protected mode, where you change SS but leave ESP to whatever it was (although this can cause alignment problems if you switch from a 16-bit stack to a 32-bit stack).
Are you sure? The boot loader from your first post in this topic has the following:sancho1980 wrote:ps: and by the way, the bootloader doesnt really load the kernel at 0x0020:0x0000! the bootloader itself is loaded at 07C0h:0 and it loads the kernel exactly 512 bytes after that
Code: Select all
call 020h:0 ;call the kernel
Sometimes the CPU does things to improve performance. For segment registers, it keeps track of some details (e.g. the default addressing size, the default operand size, the segment limit and the segment base) so that it doesn't need to constantly look up the corresponding entry in the GDT in protected mode. However, in real mode the only thing that changes is the segment base address, so to speed things up in real mode the CPU doesn't change the other things it keeps track of when a segment register is loaded.sancho1980 wrote:And also one more question please: when going back into real mode from protected mode, why is it necessary to first load a 16-bit protected mode segment? why can i not jump directly to the 16-bit real mode segment straight after turning off pm??
This means (for example) if you enable protected mode and set the limit on GS to 4 GB and then disable protected mode, then the limit on GS will still be 4 GB (and not 64 KB) even though you're in real mode, because the CPU doesn't reset the segment limit. Some people (including me) take advantage of this behavour to have real mode with no segment limits, so they can access everything above 1 MB while still using real mode BIOS functions.
This also means that if you're executing 32-bit protected mode code with CS limit set to 4 GB, it may be possible to switch back to real mode and still have 32-bit default address size, 32-bit default operand size and a 4 GB CS limit (which will make 16-bit BIOS code crash).
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
Is that the reason why you choose 0xFFFC in RM for a ss? Other than that, I'd say it's got to be 0xFFFE, because in RM the stack needs only be aligned on a word boundary, right?Brendan wrote:
Misaligned data slows the CPU down, and everything should have "natural" alignment to improve performance (words with 2 byte alignment, dwords with 4 byte alignment, etc). Setting SP to 0xFFFF means that every push and every pop is misaligned.
I also tend to use 32-bit registers for addresses (in real mode) because the CPU is more flexible with 32-bit addressing modes. For example, you can't do "mov cx,[ax*4+sp]" but you can do "mov cx,[eax*4+esp]". It's also possible to use the same stack in real mode, 16-bit protected mode and 32-bit protected mode, where you change SS but leave ESP to whatever it was (although this can cause alignment problems if you switch from a 16-bit stack to a 32-bit stack).
True, thanks again...I thought I had done it differently...Brendan wrote:Are you sure? The boot loader from your first post in this topic has the following:sancho1980 wrote:ps: and by the way, the bootloader doesnt really load the kernel at 0x0020:0x0000! the bootloader itself is loaded at 07C0h:0 and it loads the kernel exactly 512 bytes after that
It also uses the BIOS to read a sector from disk to ES:BX (where ES:BX = 0x0020:0x0000).Code: Select all
call 020h:0 ;call the kernel