LGDT

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
Alex Buell

LGDT

Post by Alex Buell »

Does LGDT always depends on DS pointing to the right segment where the GDT table resides?

After setting the GDT with DS = 0x1000, the following code won't print anything

mov bx, 0x0f01
mov eax, 0x0b8000
mov word [ds:eax], bx

But using int 0x10, ah = 0x0e works perfectly.

Any ideas what else I can do?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:LGDT

Post by Brendan »

Hi,

A quick checklist:
  • - enable A20 (optional I guess)
    - create a GDT and pointer to this GDT
    - do LGDT
    - enable protected mode
    - load segment registers with descriptors from the GDT
If using int 0x10, ah = 0x0E works perfectly, then you've skipped the last 2 steps... ;)

If "mov dword [0x000B8000],0x0F01" doesn't work, then you've either messed up one of those steps, your DS segment base isn't 0x00000000 or your video card isn't in text mode (or there's some other problem).

The LGDT instruction is effected by segment override prefixes, and is sensitive to the address size and operand size. For example, if 16 bit operands are being used it'll load the GDT base with a "base AND 0x00FFFFFF", which means the GDT base must be below 16 MB unless an operand size override prefix is used in real mode.


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.
Alex Buell

Re:LGDT

Post by Alex Buell »

'Does DS has to be zero before doing all that? I'm doing unreal mode.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:LGDT

Post by Brendan »

Hi,
Alex Buell wrote:'Does DS has to be zero before doing all that? I'm doing unreal mode.
No - consider these examples (all for real mode/unreal mode, in NASM):

Code: Select all

    cs lgdt [myGDT']   ;DS segment not used in any way

Code: Select all

    fs lgdt [myGDT']   ;DS segment not used in any way

Code: Select all

    mov ax,0x1234
    mov ds,ax
    lgdt [myGDT']   ;DS segment used with base=0x00012340

Code: Select all

    mov ax,0x8000
    mov gs,ax
    o32 lgdt [dword gs:0xFFF80000]   ;GDTR loaded from 0x00000000

Code: Select all

    mov ax,0x2000
    mov gs,ax
    lgdt [dword gs:0xFFF80000]   ;GDTR loaded from 0x00FA0000
Ok - the last two examples won't work in real mode (you'd get a general protection exception due to GS segment limit) and they're a little tricky...


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.
Alex Buell

Re:LGDT

Post by Alex Buell »

Ah! I'm loading the GDT table perfectly - it's writing to the graphic memory at 0x000b8000 that's not working correctly. If I zero out gs and use that as the segment selector, writes to gs:0x000b8000 works. But if I use ds, then I don't see anything at that location; presumably it must be writing elsewhere in memory if that's the case. I wonder if it's actually using DS=0x1000 *and* the segment selector? I'm definitely doing the following when in PM:

mov bx, DATA_SELECTOR
mov ds, bx ; (mov gs, bx)
Alex Buell

Re:LGDT

Post by Alex Buell »

Uses ds not gs. We know using gs works perfectly.

Code: Select all

[BITS 16]

        org     0

; At this point we're loaded into segment 0x1000
start:
        call    enableA20       ; enable the A20 gate

        ; debugging
        mov     ax, 0xb800
        mov     gs, ax
        mov     byte [gs:0], '1'

        ; switch to unreal mode
        cli
        push    ds              ; ds = 0x1000 (set in boot sector before jumping here)

        xor     eax, eax
        mov     ax, ds
        shl     eax, 4
        add     eax, gdt
        mov     [gdt_info + 2], eax

        lgdt    [gdt_info]

        mov     byte [gs:2], '2'

        mov     eax, cr0
        inc     ax
        mov     cr0, eax        ; flip into protected mode

        mov     byte [gs:4], '3'

        mov     bx, DATA_SELECTOR ; select first descriptor
        mov     ds, bx

        mov     byte [gs:6], '4'

        dec     ax
        mov     cr0, eax        ; flip back to real mode

        mov     byte [gs:8], '5'
        ; we're now in unreal mode
        pop     ds
        sti

        call    showProgress

        mov     bx, 0x0f01
        mov     eax, 0x000b8000         ; 32bit!
        mov     word [dword ds:eax], bx
        mov     word [dword ds:eax + 2], bx

        mov     word [dword ds:0x000b8010], 0x0e01

        mov     byte [gs:10], '6'

        call    showProgress

.loop:
        hlt
        jmp     .loop

showProgress:
        mov     ah, 0x0e
        mov     al, '*'
        int     0x10
        retn

enableA20:
; omitted for brevity

gdt:
        dw      0                       ; First GDT entry is always NULL
        dw      0
        db      0
        db      0
        db      0
        db      0
DATA_SELECTOR   equ     $ - gdt 
        dw      0xffff                  ; Second GDT entry; DATA
        dw      0
        db      0
        db      0x92
        db      0xcf
        db      0
gdt_end:

gdt_info:
        dw      gdt_end - gdt - 1       ; last byte in table
        dd      0                       ; beginning of table

        times   512 - ($ - $$) db 0
[/code]
paulbarker

Re:LGDT

Post by paulbarker »

To move into protected mode you need to do a far jump to reload CS. Probably the same going back to real mode.

You should also have 1 segment for code and 1 for data, not 1 for both. Maybe accesses to the code segment work through gs but not ds.

Solve these 2 and you'll be getting somewhere. Real-PM transitions (and back) are a pain and if you can possibly avoid them (by using a bootloader like GRUB) then do so. If you can't avoid them, the best advice I can give is to look at working code.

Here's one of mine from a couple of years ago:

Code: Select all

; Setup code taken from GeekOS, modified for COS by
;   Paul Barker ([email protected]).

; Original file Copyright (c) 2001, David H. Hovemeyer <[email protected]>
; $Revision: 1.16 $

; This is free software.  You are permitted to use,
; redistribute, and modify it as specified in the file "COPYING-GEEKOS".

; A lot of this code is adapted from Kernel Toolkit 0.2
; and Linux version 2.2.x, so the following copyrights apply:

; Copyright (C) 1991, 1992 Linus Torvalds
; modified by Drew Eckhardt
; modified by Bruce Evans (bde)
; adapted for Kernel Toolkit by Luigi Sgro

Code: Select all

   ; Block interrupts, since we can't meaningfully handle them yet
   ; and we no longer need BIOS services.
   cli

   ; Set up IDT and GDT registers
   lidt   [IDT_Pointer]
   lgdt   [GDT_Pointer]

   ; Initialize the interrupt controllers, and enable the
   ; A20 address line
   call   Init_PIC
   call   Enable_A20

   ; Switch to protected mode!
   mov   ax, 0x01
   lmsw   ax

   ; Jump to 32 bit code.
   ; NASM probably has a way to express this using
   ; instruction mnemonics, but I don't know how, so I
   ; just copied this stuff from Linux/KTK :-)
   db 0x66         ; operand size override prefix
   db 0xea         ; jmp opcode
   dd (SETUPSEG<<4) + setup_32 ; want to jump here
   dw KERNEL_CS      ; ...in the kernel code segment

[BITS 32]
setup_32:

   ; set up data segment registers
   mov   ax, KERNEL_DS
   mov   ds, ax
   mov   es, ax
   mov   fs, ax
   mov   gs, ax
   mov   ss, ax

   ; Create the stack for the initial kernel thread, just below the kernel
   mov   esp, (0xFFF0)
continued...
paulbarker

Re:LGDT

Post by paulbarker »

Code: Select all

; ----------------------------------------------------------------------
; The GDT.  Creates flat 32-bit address space for the kernel
; code, data, and stack.  Note that this GDT is just used
; to create an environment where we can start running 32 bit
; code.  The kernel will create and manage its own GDT.
; ----------------------------------------------------------------------
GDT:
   ; Descriptor 0 is not used
   dw 0
   dw 0
   dw 0
   dw 0

   ; Descriptor 1: kernel code segment
   dw 0xFFFF   ; bytes 0 and 1 of segment size
   dw 0x0000   ; bytes 0 and 1 of segment base address
   db 0x00      ; byte 2 of segment base address
   db 0x9A      ; present, DPL=0, non-system, code, non-conforming,
         ;   readable, not accessed
   db 0xCF      ; granularity=page, 32 bit code, upper nibble of size
   db 0x00      ; byte 3 of segment base address

   ; Descriptor 2: kernel data and stack segment
   ; NOTE: what Intel calls an "expand-up" segment
   ; actually means that the stack will grow DOWN,
   ; towards lower memory.  So, we can use this descriptor
   ; for both data and stack references.
   dw 0xFFFF   ; bytes 0 and 1 of segment size
   dw 0x0000   ; bytes 0 and 1 of segment base address
   db 0x00      ; byte 2 of segment base address
   db 0x92      ; present, DPL=0, non-system, data, expand-up,
         ;   writable, not accessed
   db 0xCF      ; granularity=page, big, upper nibble of size
   db 0x00      ; byte 3 of segment base address
Copyright (c) 2001, David H. Hovemeyer <[email protected]>

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Hope you can make some sense out of all that.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:LGDT

Post by Brendan »

Hi,

Your problem is that you set DS to a flat segment in protected mode (base=0, limit=4GB), and then pop the old value off the stack in real mode which sets it's base back to 0x00010000 (and doesn't effect it's limit).

Because of this your "mov word [dword ds:0x000b8010], 0x0e01" is actually being written to 0x000C8010.

There was a few other things...

Code: Select all

[BITS 16]

        org     0

; At this point we're loaded into segment 0x1000

%define BOOT_ADDRESS  0x00010000

start:
        call    enableA20       ; enable the A20 gate

        ; debugging
        mov     ax, 0xb800
        mov     gs, ax
        mov     byte [gs:0], '1'

        ; switch to unreal mode
        cli
;Delete->        push    ds              ; ds = 0x1000 (set in boot sector before jumping here)

        lgdt    [gdt_info]

        mov     byte [gs:2], '2'

        mov     eax, cr0
        inc     ax
        mov     cr0, eax        ; flip into protected mode

        jmp .flush               ;**** Needed for some CPUs ****
.flush:

        mov     byte [gs:4], '3'

        mov     bx, DATA_SELECTOR ; select first descriptor
        mov     ds, bx

        mov     byte [gs:6], '4'

        dec     ax
        mov     cr0, eax        ; flip back to real mode

        mov     byte [gs:8], '5'
        ; we're now in unreal mode
;Delete->        pop     ds
        sti

        call    showProgress

        mov     bx, 0x0f01
        mov     eax, 0x000b8000         ; 32bit!
        mov     word [dword ds:eax], bx
        mov     word [dword ds:eax + 2], bx

        mov     word [dword ds:0x000b8010], 0x0e01

;<-SNIPPED->


gdt_info:
        dw      gdt_end - gdt - 1       ; GDT limit
        dd      gdt + BOOT_ADDRESS      ; **** CHANGED **** (GDT address)

        times   512 - ($ - $$) db 0

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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:LGDT

Post by Brendan »

Hi,
paulbarker wrote: To move into protected mode you need to do a far jump to reload CS. Probably the same going back to real mode.

You should also have 1 segment for code and 1 for data, not 1 for both. Maybe accesses to the code segment work through gs but not ds.
None of that is necessary for initializing "unreal mode"... ;)


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.
Alex Buell

Re:LGDT

Post by Alex Buell »

Brendan wrote: Your problem is that you set DS to a flat segment in protected mode (base=0, limit=4GB), and then pop the old value off the stack in real mode which sets it's base back to 0x00010000 (and doesn't effect it's limit).

Because of this your "mov word [dword ds:0x000b8010], 0x0e01" is actually being written to 0x000C8010.
Ah! With the little changes you outlined, the program now work; and I can see the output on the display.

Thank you, that's excellent!

Now if I could figure out why the 32 bit writes puts the characters near the top right instead of the top left on the screen... Everything else works as expected.
paulbarker

Re:LGDT

Post by paulbarker »

None of that is necessary for initializing "unreal mode"...
He'll want protected mode soon after unreal mode anyway so the advice still stands.

Still, I did jump the gun a bit.
Post Reply