13

我正在创建一种玩具操作系统,并且正在尝试设置分页。引导加载程序首先将内核映射到上半部分,然后我尝试设置一个新的页面目录。

我有一些这样定义的结构:

typedef struct pd_entry pd_entry_t;
struct pd_entry
{
    uint32_t present    : 1;
    uint32_t writable   : 1;
    uint32_t user       : 1;
    uint32_t writethru  : 1;
    uint32_t nocache    : 1;
    uint32_t accessed   : 1;
    uint32_t _reserved  : 1;
    uint32_t size_4mb   : 1;
    uint32_t global     : 1;
    uint32_t available  : 3;
    uint32_t frame      : 20;
}__attribute__((packed));

typedef struct pt_entry pt_entry_t;
struct pt_entry
{
    uint32_t present    : 1;
    uint32_t writable   : 1;
    uint32_t user       : 1;
    uint32_t writethru  : 1;
    uint32_t nocache    : 1;
    uint32_t accessed   : 1;
    uint32_t dirty      : 1;
    uint32_t attr_index : 1;
    uint32_t global     : 1;
    uint32_t available  : 3;
    uint32_t frame      : 20;
} __attribute__((packed));

typedef struct page_dir page_dir_t;
struct page_dir
{
    pd_entry_t entries[TABLES_PER_DIR];
};

typedef struct page_table page_table_t;
struct page_table
{
    pt_entry_t entries[PAGES_PER_TABLE];
};

我使用这段代码来设置条目:

// I store the page directory pointer in the last directory entry
uint32_t map_quick(uint32_t addr)
{
    GET_PTE(QUICKMAP_ADDR)->frame = PAGE_ALIGN(addr) >> 12; // QUICKMAP_ADDR is 0xC0000000
    paging_flush_tlb_entry(QUICKMAP_ADDR); // asm volatile ("invlpg (%0)" :: "r"(addr));
    return QUICKMAP_ADDR;
}

pd_entry_t* get_dir_entry(page_dir_t *dir, uint32_t virt)
{
    dir = (page_dir_t *)map_quick((uint32_t)dir);
    return &dir->entries[PAGE_DIR_INDEX(virt)];
}

void set_dir_entry(page_dir_t *dir, uint32_t virt, page_table_t *pt)
{
    pd_entry_t *pde = get_dir_entry(dir, virt);

    pde->present   = 1;
    pde->writable  = 1;
    pde->user      = 0;
    pde->writethru = 0;
    pde->nocache   = 0;
    pde->accessed  = 0;
    pde->size_4mb  = 0;
    pde->global    = 0;
    pde->frame     = (uint32_t)pt >> 12;
    kprintf("0x%x:0x%x ", (uint32_t)pt >> 12, pde->frame); // Just a 'printf' clone
}

但是,当set_dir_entry被调用时,kprintf打印0xA0:0x0. 我不明白为什么pde->frame没有设置?我没有收到任何页面错误或异常,目录条目总是等于0无论我将其设置为什么。

编辑:这是代码kprintf

size_t kprintf(const char *str, ...)
{
    if (!str) return 0;

    va_list args;
    va_start(args, str);

    unsigned int i;
    for (i = 0; i < strlen(str); i++)
    {
        if (str[i] == '%')
        {
            switch (str[i+1])
            {
                case 'c':
                {
                    char c = va_arg(args, char);
                    kputc(c);
                    i++;
                    break;
                }
                case 's':
                {
                    char *s = va_arg(args, char*);
                    kputs(s);
                    i++;
                    break;
                }
                case 'd':
                case 'i':
                {
                    int c = va_arg(args, int);
                    char s[32] = {0};
                    itoa_s(c, s, 10);
                    kputs(s);
                    i++;
                    break;
                }
                case 'X':
                case 'x':
                {
                    int c = va_arg(args, int);
                    char s[32] = {0};
                    itoa_s(c, s, 16);
                    kputs(s);
                    i++;
                    break;
                }
            }
        }
        else
        {
            kputc(str[i]);
        }
    }

    va_end(args);
    return (size_t)i;
}

void kputc(unsigned char c)
{
    uint16_t attr = color << 8;

    if (c == 0x8 && posx)
        posx--;
    else if (c == 0x9)
        posx = (posx + 8) & ~(8-1);
    else if (c == '\r')
        posx = 0;
    else if (c == '\n')
    {
        posx = 0;
        posy++;
    }
    else if (c >= ' ')
    {
        uint16_t *loc = videomem + (posy*80 + posx);
        *loc = c | attr;
        posx++;
    }

    if (posx >= 80)
    {
        posx = 0;
        posy++;
    }
    if (posy >= 25)
        kscroll();
}

void kputs(const char *str)
{
    if (!str) return;

    unsigned int i;
    for (i = 0; i < strlen(str); i++)
        kputc(str[i]);
}

编辑 2:我不知道这是否有用,而且我真的不喜欢只是转储大量代码供人们阅读,但这是整个分页代码。不属于此代码的所有函数调用都可以独立运行。

虚拟.h

#ifndef _VIRTUAL_H_
#define _VIRTUAL_H_

#include <x86/paging.h>
#include <stdbool.h>

#define VIRTUAL_TO_PHYSICAL(x) ((uint32_t)(x) - 0xC0001000 + 0x00101000)
#define PHYSICAL_TO_VIRTUAL(x) ((uint32_t)(x) + 0xC0001000 - 0x00101000)

#define KERNEL_CODE_VADDRESS 0xC0001000
#define KERNEL_HEAP_VADDRESS 0xD0000000

void virtual_init();

void virtual_create_dir(page_dir_t **dir);
void virtual_destroy_dir(page_dir_t *dir);
void virtual_set_dir(page_dir_t *dir);

void virtual_map_page(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present);
void virtual_map_kernel(page_dir_t *dir);

#endif

虚拟的.c

#include <system/memory/physical.h>
#include <x86/idt.h>
#include <utils/kernio.h>
#include <utils/kernpanic.h>
#include <string.h>
#include "virtual.h"

#define QUICKMAP_ADDR 0xC0000000

#define GET_PDE(x) ((pd_entry_t *)(0xFFFFF000 + ((x) >> 22) * 4))
#define GET_PTE(x) ((pt_entry_t *)(0xFFC00000 + ((x) >> 12) * 4))

extern uint32_t __kernel_start, __kernel_end;

page_dir_t *kern_dir;

pd_entry_t* get_dir_entry(page_dir_t *dir, uint32_t virt);
void set_dir_entry(page_dir_t *dir, uint32_t virt, page_table_t *pt);
pt_entry_t* get_table_entry(page_dir_t *dir, uint32_t virt);
void set_table_entry(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present);

uint32_t map_quick(uint32_t addr);

void interrupt page_fault(registers_t *regs);

void virtual_init()
{
    int_enable(14, page_fault);

    virtual_create_dir(&kern_dir);

    uint32_t phys, virt;
    for (phys = 0; phys < 0x400000; phys += PAGE_SIZE)
        virtual_map_page(kern_dir, phys, phys, true);

    phys = VIRTUAL_TO_PHYSICAL(&__kernel_start);
    virt = (uint32_t)&__kernel_start;

    uint32_t end = VIRTUAL_TO_PHYSICAL(&__kernel_end) + physical_get_bitmap_size();
    for (; phys < end; phys += PAGE_SIZE, virt += PAGE_SIZE)
        virtual_map_page(kern_dir, phys, virt, true);

    virtual_map_page(kern_dir, 0x0, QUICKMAP_ADDR, true);

    virtual_set_dir(kern_dir);
}

void virtual_create_dir(page_dir_t **dir)
{
    *dir = (page_dir_t *)physical_alloc_block();
    page_dir_t *virt_dir = (page_dir_t *)map_quick((uint32_t)*dir);
    memset(virt_dir, 0, sizeof(page_dir_t));

    pd_entry_t *last_pde = &virt_dir->entries[TABLES_PER_DIR - 1];
    last_pde->present = 1;
    last_pde->writable = 1;
    last_pde->frame = (uint32_t)*dir >> 12;
}

void virtual_destroy_dir(page_dir_t *dir)
{
    page_dir_t *virt_dir = (page_dir_t *)map_quick((uint32_t)dir);

    unsigned int i, j;
    for (i = 1; i < KERNEL_CODE_VADDRESS / (PAGE_SIZE * TABLES_PER_DIR); i++)
    {
        pd_entry_t *pde = &virt_dir->entries[i];
        page_table_t *table = (page_table_t *)(pde->frame << 12);

        if (table != NULL && pde->present)
        {
            page_table_t *virt_table = (page_table_t *)map_quick((uint32_t)table);
            for (j = 0; j < PAGES_PER_TABLE; j++)
            {
                pt_entry_t *pte = &virt_table->entries[j];
                uint32_t phys = pte->frame << 12;
                if (phys != NULL && pte->present)
                    physical_free_block((void *)phys);
            }

            physical_free_block(table);
        }
    }

    physical_free_block(dir);
}

void virtual_set_dir(page_dir_t *dir)
{
    paging_load_pdbr((uint32_t)dir);
    paging_enable(true);
}

void virtual_map_page(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present)
{
    set_table_entry(dir, phys, virt, present);
}

void virtual_map_kernel(page_dir_t *dir)
{
    pd_entry_t *pde;

    pde = get_dir_entry(kern_dir, 0x0);
    set_dir_entry(dir, 0x0, (page_table_t *)(pde->frame << 12));

    pde = get_dir_entry(kern_dir, KERNEL_CODE_VADDRESS);
    set_dir_entry(dir, KERNEL_CODE_VADDRESS, (page_table_t *)(pde->frame << 12));
}

pd_entry_t* get_dir_entry(page_dir_t *dir, uint32_t virt)
{
    dir = (page_dir_t *)map_quick((uint32_t)dir);
    pd_entry_t *e = &dir->entries[PAGE_DIR_INDEX(virt)];
    return &dir->entries[PAGE_DIR_INDEX(virt)];
}

void set_dir_entry(page_dir_t *dir, uint32_t virt, page_table_t *pt)
{
    pd_entry_t *pde = get_dir_entry(dir, virt);

    pde->present = 1;
    pde->writable = 1;
    pde->user = 0;
    pde->writethru = 0;
    pde->nocache = 0;
    pde->accessed = 0;
    pde->size_4mb = 0;
    pde->global = 0;
    pde->frame = (uint32_t)pt >> 12;
}

pt_entry_t* get_table_entry(page_dir_t *dir, uint32_t virt)
{
    pd_entry_t *pde = get_dir_entry(dir, virt);
    page_table_t *table = (page_table_t *)(pde->frame << 12);

    if (table == NULL)
    {
        table = (page_table_t *)physical_alloc_block();
        set_dir_entry(dir, virt, table);

        unsigned int i;
        for (i = 0; i < PAGES_PER_TABLE; i++)
            set_table_entry(dir, 0x0, virt + (i * PAGE_SIZE), false);
    }

    table = (page_table_t *)map_quick((uint32_t)table);
    return (pt_entry_t *)&table->entries[PAGE_TABLE_INDEX(virt)];
}

void set_table_entry(page_dir_t *dir, uint32_t phys, uint32_t virt, bool present)
{
    pt_entry_t *pte = get_table_entry(dir, PAGE_ALIGN(virt));

    pte->present = present;
    pte->writable = 1;
    pte->user = 0;
    pte->writethru = 0;
    pte->nocache = 0;
    pte->dirty = 0;
    pte->attr_index = 0;
    pte->global = 0;
    pte->frame = PAGE_ALIGN(phys) >> 12;
}

uint32_t map_quick(uint32_t addr)
{
    GET_PTE(QUICKMAP_ADDR)->frame = PAGE_ALIGN(addr) >> 12;
    paging_flush_tlb_entry(QUICKMAP_ADDR);
    return QUICKMAP_ADDR;
}

void interrupt page_fault(registers_t *regs)
{
    uint32_t virt;
    asm volatile ("movl %%cr2, %0" : "=r" (virt));
    kprintf("\nError accessing address 0x%x", virt);
    kpanic("Page Fault");
}
4

2 回答 2

1

请参阅此问题重新 引用:__attribute__(packed)

... __attribute__((packed)) is potentially unsafe on some systems ... There have also been systems where a misaligned access quietly ignores the low-order bits of the address, causing it to access the wrong chunk of memory.

尝试使用pragma (pack)( and here ) 代替:

#pragma pack(1) // exact fit - no padding; placed at top of pragma pack stack
    struct pd_entry
    {
        ...
    };
    struct pt_entry
    {
        ...
    };
#pragma pack(pop) //restore previous pragma pack stack

[编辑]
根据您对使用 a 的评论uint32_t,如果您想保留访问各个位的能力,您可以使结构成为联合的一部分uint31_t

#pragma pack(1) // exact fit - no padding; placed at top of pragma pack stack
union
{
    uint32_t ui32;
    struct pd_entry
    {
        ...
    };
 }
    ...
#pragma pack(pop) //restore previous pragma pack stack

并使用orandanduint32_t设置,并使用结构的成员检查值。

于 2012-10-27T10:32:49.450 回答
0

pt 是一个指针,这意味着它保存了它所指向的 page_table_t 变量的地址。为什么要将地址右移 12 位?这是故意的吗?

于 2012-10-27T09:49:41.263 回答