我正在https://github.com/Incarnation-p-lee/excalibur开发一个小型操作系统。最近在启用页面时,访问1280 MB的内存时发现了一种不同的输出。假设遵循代码:

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 ...
?


但是,当我想访问0x4ffffffc时,它触发了页面错误?我期望0x4ffffffc和0x50000000都应该触发页面错误。

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 ...
?


相关代码和结构定义

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配置

# 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

最佳答案

最后,我找到了此问题的根本原因。同意zwol,这是配置gdt表时的一个错误,限制和基数应该配置为4KB。但不以字节为单位。这就是为什么内存大于0x50000000会触发GPF而不触发页面错误的原因。

关于c - 为什么访问大约1280 MB(0x50000000)的内存时os镜像具有不同的行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44801225/

10-11 04:17