0
$\begingroup$

In a cpu with segmented memory, can an address only be accessed with a segmented address, or can a normal virtual address also be used?

$\endgroup$
2
$\begingroup$

A virtual address is not normal. It is used with paging. "Normal addresses" are direct physical addresses which access main memory directly. Intel's terminology says that virtual address will be translated by the segmentation unit to a linear address. The linear address will then be translated to physical address by the MMU.

With that said the answer to your question is quite complex since it involves understanding operating-system development concepts.

Real mode

x86 CPUs start with the 8086. Today, the 8086 is emulated at startup of every x86 processor for backward compatibility with older software. This is called real mode. In real mode physical addresses are calculated using the address you specify in the assembly code by implicitly adding ds as the segment selector. They are calculated by the formula ds * 16 + offset. For example,

mov ax, 0
mov ds, ax
mov word [0x1234], 0x1234

will move 0x1234 to the physical address 0x0 * 16 + 0x1234 = 0x1234. With ds set as 0, real mode segmentation is ignored because the address you specify is translated to itself.

Protected mode

After real mode the OS will jump to protected mode. In protected mode there is segmentation and paging available for more recent CPUs. Before paging, there was only segmentation. Paging is more modern than segmentation. For an example on how to set up protected mode in NASM assembly look at the following code:

cli

lgdt[gdtr]

mov eax, cr0
or al, 1
mov cr0, eax

jmp 0x08:protectedMode       ;0x08 = 0b1000, 1 for segment selector
                             ;0 for gdt not ldt and 00 for privilege
bits 32
protectedMode:

gdt_start:
        dq 0x0      ;null descriptor
gdt_code:
        dw 0xFFFF   ;limit 0-15
        dw 0x0      ;base 0-15
        db 0x0      ;base 16-23 
        db 10011010b    ;pr, privi (2), s, ex, dc, rw, ac 
        db 11001111b    ;gr, sz, limit 16-19
        db 0x0      ;base 24-31
gdt_data:
        dw 0xFFFF
        dw 0x0
        db 0x0
        db 10010010b
        db 11001111b
        db 0x0  
gdtr:
        dw 24
        dd gdt_start

The code disables interrupts, loads a GDT, enables protected mode by setting a bit in the CR0 control register then jumps to protected mode in segment 0x08. Segment 0x08 is the code segment. Segment 0 being the null descriptor. Here segmentation is enabled with a flat memory model.

The flat memory model is where you specify a base of 0x0 and a limit of 0xFFFFF with a granularity (gr) of 1. The granularity specify if the limit will be in blocks of 1 byte (0) or 4KB (1). If the limit is in blocks of 4KB and the base is 0 then the segment will span the whole 4GB available address space in protected mode (with a limit of 0xFFFFF). The flat memory model is often used in modern operating systems because it simplifies operations in transition modes before long mode is enabled.

Long mode

To enable long mode, you must enable paging and set certain bits in some registers. The following code enables long mode in NASM assembly:


mov eax, cr4            ;enable PAE-paging
or eax, 1 << 5
mov cr4, eax

mov ecx, 0xC0000080     ;set long mode bit in EFER MSR
rdmsr
or eax, 1 << 8
wrmsr

mov eax, cr0            ;enable paging
or eax, 1 << 31
mov cr0, eax

lgdt[gdtr]          ;load a 64 bit gdt (will be ignored afterwards)

jmp 0x08:longMode

bits 64

longMode:

gdt_start:
        dw 0xFFFF
        dw 0
        db 0                        
        db 0
        db 1
        db 0
gdt_code:
        dw 0x1111   ;limit 0-15
        dw 0x0      ;base 0-15
        db 0x0      ;base 16-23 
        db 10011010b    ;pr, privi (2), s, ex, dc, rw, ac 
        db 10101111b    ;gr, sz, limit 16-19
        db 0x0      ;base 24-31
gdt_data:
        dw 0x0000
        dw 0x0
        db 0x0
        db 10010010b
        db 00000000b
        db 0x0  
gdtr:
        dw $ - gdt_start - 1
        dq gdt_start

The 64 bits GDT is necessary because as stated on osdev.org

Notice that we set a 4gb limit for code. This is needed because the processor will make a last limit check before the jump, and having a limit of 0 will cause a #GP (tested in bochs). After that, the limit will be ignored.

So basically, before we jump in the 64 bits code selector to enter long mode, the processor makes a last limit check. After that, the limit is ignored. This is evident since the limit is only 20 bits and, with a granularity of 4KB, this limits the segments to 4GB. In long mode you have access to much more RAM than that.

Once in long mode, you have paging enabled and everything but the CR3 register is translated by the MMU to a physical address. Everything is a virtual address. Segmentation is ignored.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.