I am developing one tiny OS at https://github.com/Incarnation-p-lee/excalibur. Recently when enable page, I found one difference output when accessing memory across 1280 MB. Assume follow code:
static inline void
test_paging(void)
{
uint32 *ptr;
// ptr = (void *)0x800000;
// *ptr = 0xdeadbeaf;
// ptr = (void *)0x4ffffffc;
ptr = (void *)0x50000000;
*ptr = 0xdeadbeaf;
}
[0x00000000] Boot loader magic -> 0x2badb002.
[0x00000000] In Protect Mode.
[0x00000000] Paging disabled.
[0x00000000] OS image start -> 0x00100000
[0x00000000] OS image end -> 0x00109000
[0x00000000] Stack base 0x00100fb4.
[0x00000000] Physical memory lower -> 0000000636 KB.
[0x00000000] Physical memory upper -> 0000261056 KB.
[0x00000000] GDT table initialized.
[0x00000000] IDT table initialized.
[0x00000000] IRQ timer initialized.
[0x00000003] Page enabled from 0x00000000 -> 0x0011c004.
[0x0000000b] Page initialized.
[0x0000000b] In Protect Mode.
[0x0000000b] Paging enabled.
[0x0000000b] Divide by zero at eip -> 0x00101071.
[0x0000000b] Breakpoint at eip -> 0x00101072.
[0x0000000b] Divide by zero at eip -> 0x00101074.
[0x0000000b] Unsupported isq 0000000013.
Assertion: Unsupported ISR
fail.
at function isr_handler_main
in file src/interrupt/isr/isr_handler.c:0000000014
Enter KERNEL PANIC T.T ...
?
But when I want to access 0x4ffffffc, it triggered page fault ? It expected both 0x4ffffffc and 0x50000000 should trigger page fault in my option.
static inline void
test_paging(void)
{
uint32 *ptr;
// ptr = (void *)0x800000;
// *ptr = 0xdeadbeaf;
ptr = (void *)0x4ffffffc;
// ptr = (void *)0x50000000;
*ptr = 0xdeadbeaf;
}
[0x00000000] In Protect Mode.
[0x00000000] Paging disabled.
[0x00000000] OS image start -> 0x00100000
[0x00000000] OS image end -> 0x00109000
[0x00000000] Stack base 0x00100fb4.
[0x00000000] Physical memory lower -> 0000000636 KB.
[0x00000000] Physical memory upper -> 0000261056 KB.
[0x00000000] GDT table initialized.
[0x00000000] IDT table initialized.
[0x00000000] IRQ timer initialized.
[0x00000003] Page enabled from 0x00000000 -> 0x0011c004.
[0x00000006] Page initialized.
[0x00000006] In Protect Mode.
[0x00000006] Paging enabled.
[0x00000006] Divide by zero at eip -> 0x00101071.
[0x00000006] Breakpoint at eip -> 0x00101072.
[0x00000006] Divide by zero at eip -> 0x00101074.
Page is not present.
Page is Read-Only.
Page Fault at address 0x4ffffffc.
Assertion: Page Fault fail.
at function isr_14_paging_fault_handler
in file src/interrupt/isr/isr_handler.c:0000000076
Enter KERNEL PANIC T.T ...
?
Related code and structure definition
void
descriptor_table_gdt_initialize(void)
{
gdt_reg.limit = sizeof(gdt) - 1;
gdt_reg.base = (uint32)&gdt;
gdt_entry_set(0, 0, 0, 0, 0);
gdt_entry_set(1, CODE_SEG_BASE, CODE_SEG_LMT, CODE_SEG_ACC, CODE_SEG_FLAG);
gdt_entry_set(2, DATA_SEG_BASE, DATA_SEG_LMT, DATA_SEG_ACC, DATA_SEG_FLAG);
gdt_entry_set(3, 0, USR_CODE_SEG_LMT, USR_CODE_SEG_ACC, USR_CODE_SEG_FLAG);
gdt_entry_set(4, 0, USR_DATA_SEG_LMT, USR_DATA_SEG_ACC, USR_DATA_SEG_FLAG);
gdt_table_flush((uint32)&gdt_reg);
printf_vga_tk("GDT table initialized.\n");
}
static inline void
gdt_entry_set(uint32 i, uint32 base, uint32 limit, uint16 acc, uint8 flags)
{
kassert(i < GDT_ENTRY_CNT);
gdt[i].base_l = U32_BITS(base, 0, 24);
gdt[i].base_h = (uint8)U32_BITS(base, 24, 8);
gdt[i].lmt_l = (uint16)U32_BITS(limit, 0, 16);
gdt[i].flags.lmt_h = (uint8)U32_BITS(limit, 16, 4);
gdt[i].access.acc = (uint8)U32_BIT(acc, ACC_AC_IDX);
gdt[i].access.rw = (uint8)U32_BIT(acc, ACC_RW_IDX);
gdt[i].access.dc = (uint8)U32_BIT(acc, ACC_DC_IDX);
gdt[i].access.ex = (uint8)U32_BIT(acc, ACC_EX_IDX);
gdt[i].access.dt = (uint8)U32_BIT(acc, ACC_DT_IDX);
gdt[i].access.dpl = (uint8)U32_BITS(acc, ACC_DPL_IDX, ACC_DPL_LEN);
gdt[i].access.p = (uint8)U32_BIT(acc, ACC_P_IDX);
gdt[i].flags.avl = (uint8)U32_BIT(flags, FLAG_A_IDX);
gdt[i].flags.pack = 0;
gdt[i].flags.db = (uint8)U32_BIT(flags, FLAG_DB_IDX);
gdt[i].flags.g = (uint8)U32_BIT(flags, FLAG_G_IDX);
}
#define U32_BIT(x, idx) ((uint32)(x) >> (idx) & 0x1)
#define U32_BITS(x, s, l) (((uint32)(x) >> (s)) & ((0x1 << (l)) - 1))
#define CODE_SEG_BASE 0x0
#define CODE_SEG_LMT 0xffffffff
#define DATA_SEG_BASE 0x0
#define DATA_SEG_LMT 0xffffffff
#define STACK_SEG_BASE 0x300000
#define STACK_SEG_LMT 0xfffff
#define USR_CODE_SEG_LMT 0xffffffff
#define USR_DATA_SEG_LMT 0xffffffff
static s_gdt_entry_t gdt[GDT_ENTRY_CNT];
static s_gdt_register_t gdt_reg;
extern void gdt_table_flush(uint32);
[GLOBAL gdt_table_flush]
gdt_table_flush:
mov eax, [esp + 4]
lgdt [eax]
mov ax, 0x10 ; data segment selector
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x8: .flush ; will change cs register implicitly
.flush:
ret
/*
* Global Descriptor Table Register
* 47 16 15 0
* +----------------------------+-------------------+
* | 32-bit Linear Base Address | 16-bit Table Limit|
* +----------------------------+-------------------+
*/
struct gdt_register {
uint16 limit;
uint32 base;
} __attribute__((packed));
/*
* Descriptor Attribute
* 15 14 13 12 11 8 7 6 5 4 3 2 1 0
* +---+-----+---+-----+----------------+---+-----+----+----+----+----+----+
* | G | D/B | 0 | AVL | Seg limit high | P | DPL | DT | EX | DC | RW | AC |
* +---+-----+---+-----+----------------+---+-----+----+----+----+----+----+
*/
struct gdt_attribute_access {
uint8 acc:1; // Segment has been accessed or not
uint8 rw:1; // Read-only or Read/Write
uint8 dc:1; // Direction for data segment, 0 grow up, 1 grow down or
// Execution for code segment, 0 indicate DPL and more DPL
// 1 indicate only the DPL specify
uint8 ex:1; // Segment can be executed or not.
uint8 dt:1; // Descriptor Type, always 1 for GDT
uint8 dpl:2; // Descriptor privilege level, ring 0-3
uint8 p:1; // Segment is present or not
} __attribute__((packed));
struct gdt_attribute_flags {
uint8 avl:1;
uint8 pack:1;
uint8 db:1; // Operand size, 0 16-bit 1 32-bit
uint8 g:1; // Granularity which defines the limit unit in byte or 4KB
uint8 lmt_h:4; // High 4 bit of limit, bit <16, 19> of limit
} __attribute__((packed));
/*
* Global Descriptor Table Entry (Global Descriptor)
* Each represent a segment in GDT.
* 63 55 51 47 39 15 0
* +-----------+-------+------------+--------+----------+-----------+
* | base high | flags | limit high | access | base low | limit low |
* +-----------+-------+------------+--------+----------+-----------+
* base contains 32-bit
* limit contains 20-bit
*/
struct gdt_entry {
uint16 lmt_l;
uint32 base_l:24;
struct gdt_attribute_access access;
struct gdt_attribute_flags flags;
uint8 base_h;
} __attribute__((packed));
Bochs config
# configuration file generated by Bochs
plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1, gameport=1
config_interface: win32config
display_library: win32
memory: host=256, guest=256
romimage: file="C:\Program Files (x86)\Bochs-2.6.8/BIOS-bochs-latest"
vgaromimage: file="C:\Program Files (x86)\Bochs-2.6.8/VGABIOS-lgpl-latest"
boot: floppy
floppy_bootsig_check: disabled=0
floppya: type=1_44, 1_44="C:\Users\pli\Desktop\workspace\bochs\floppy.img", status=inserted, write_protected=0
# no floppyb
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=none
ata0-slave: type=none
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata1-master: type=none
ata1-slave: type=none
ata2: enabled=0
ata3: enabled=0
pci: enabled=1, chipset=i440fx
vga: extension=vbe, update_freq=5, realtime=1
cpu: count=1, ips=1000000, model=bx_generic, reset_on_triple_fault=0, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU "
cpuid: mmx=1, apic=xapic, simd=sse2, sse4a=0, misaligned_sse=0, sep=1, movbe=0, adx=0
cpuid: aes=0, sha=0, xsave=0, xsaveopt=0, x86_64=1, 1g_pages=0, pcid=0, fsgsbase=0
cpuid: smep=0, smap=0, mwait=1, vmx=1
print_timestamps: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=realtime, time0=local, rtc_sync=0
# no cmosimage
# no loader
log: bochsout.txt
logprefix: %t%e%d
debug: action=ignore
info: action=report
error: action=report
panic: action=ask
keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none
mouse: type=ps2, enabled=0, toggle=ctrl+mbutton
sound: waveoutdrv=win, waveout=none, waveindrv=win, wavein=none, midioutdrv=win, midiout=none
speaker: enabled=1, mode=sound
parport1: enabled=1, file=none
parport2: enabled=0
com1: enabled=1, mode=null
com2: enabled=0
com3: enabled=0
com4: enabled=0
Edit: I'm very sorry,I confused the order of segmentation and paging when translating the address...
Finally, I found the root cause of this issue. Agree with zwol, it is a bug when config the gdt table, the limit and base should be config with 4KB in count. but not in bytes. That is why memory large than 0x50000000 triggered GPF but not page fault.
User contributions licensed under CC BY-SA 3.0