Подтвердить что ты не робот

C Указатель не устанавливается

Я создаю своего рода игрушечную ОС, и я пытаюсь настроить пейджинг. Сначала загрузчик сначала отображает ядро ​​в верхнюю половину, а затем я пытаюсь настроить новый каталог страниц.

У меня есть некоторые структуры, определенные следующим образом:

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 независимо от того, к чему я ее установил.

EDIT: здесь код для 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]);
}

EDIT 2: я не знаю, полезно ли это, и мне не очень нравится просто сбрасывать огромное количество кода для людей, чтобы читать, но это весь код персонального вызова. Все вызовы функций, которые не являются частью этого кода, работают независимо друг от друга.

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

virtual.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");
}
4b9b3361

Ответ 1

Смотрите этот вопрос re __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) (и здесь):

#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

[править]
Основываясь на вашем комментарии для использования uint32_t, если вы хотите сохранить возможность доступа к отдельным битам, вы можете сделать structs частью объединения с помощью 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

и используйте or и and с помощью uint32_t для установки и проверки значений с помощью элементов структуры.

Ответ 2

pt - указатель, что означает, что он содержит адрес переменной page_table_t, на который указывает. Почему вы изменяете адрес на 12? Это намеренно?