OSDev http://osdev.su/ |
|
Менеджер виртуальной памяти http://osdev.su/viewtopic.php?f=6&t=3806 |
Страница 1 из 2 |
Автор: | FreeProger [ 12 июл 2019, 18:54 ] |
Заголовок сообщения: | Менеджер виртуальной памяти |
Кто подскажет в чём может быть проблема. Написал простенький менеджер памяти. В загрузчике создаю каталог страниц по адресу 10000h, таблицу страниц описывающую первый мегабайт по адресу 11000h, таблицу описывающую физ. страницы ядра 12000h. Затем монтирую первый мегабайт и ядро и перехожу на ядро Код: KERNEL_DIR_PAGES_PADDR = 10000h ONE_MB_TABLE_PAGES_PADDR = 11000h KERNEL_PAGES_TABLE_PADDR = 12000h TEMP_KERNEL_PADDR = 13000h ; Временный физический адрес ядра KERNEL_PADDR = 100000h ; Физический адрес ядра Код: ; Настраиваем и включаем страную адресацию ; Очищаем каталог страниц mov eax,0 mov edi,KERNEL_DIR_PAGES_PADDR mov ecx,1024 rep stosd ; Монтируем первый мегабайт mov [KERNEL_DIR_PAGES_PADDR], dword ONE_MB_TABLE_PAGES_PADDR+111b ; Вписываем таблицу в каталог ; Заполним первую таблицу страниц mov eax,11b mov ecx,0x100000 / 4096 ; 1MB / page_size = 256 стр. mov edi,ONE_MB_TABLE_PAGES_PADDR @@: stosd add eax,0x1000 loop @b ; Монтируем физ. память начиная с KERNEL_PADDR (код ядра) по вирт. адресу KERNEL_ADDR mov [KERNEL_DIR_PAGES_PADDR+(KERNEL_ADDR shr 22)*4], dword KERNEL_PAGES_TABLE_PADDR+111b ; Вписываем таблицу в каталог ; Заполняем последнюю таблицу mov eax, KERNEL_PADDR+11b mov ecx, 0x400000/4096 ; 4MB / page_size mov edi, KERNEL_PAGES_TABLE_PADDR @@: stosd add eax, 0x1000 loop @b ; Устанавливаем текущий каталог таблиц страниц mov eax, KERNEL_DIR_PAGES_PADDR mov cr3,eax ; Включаем страничную адресацию mov eax,cr0 or eax,80000000h mov cr0,eax ; копируем ядро по вирт. адресу KERNEL_ADDR mov esi,TEMP_KERNEL_PADDR ; Источник mov edi,KERNEL_ADDR ; Назначение mov ecx,KERNEL_SIZE ; Размер rep movsb ; Устанавливаем новый стек mov esp, KERNEL_STECK_ADDR ; Передаем управление ядру jmp KERNEL_ADDR В ядре при инициализации менеджера памяти отображаю каталог страниц по адресу 0xFFFFD000, всё работает до тех пор пока я не пытаюсь отмонтировать первый мегабайт. Код: KERNEL_ADDR = 0xFFC00000 ; Адрес ядра KERNEL_STECK_ADDR = 0xFFFDAFFF ; Адрес стека ядра IDT_ADDR = 0xFFFDB000 MEMORY_BMP_ADDR = 0xFFFDC000 ; Адрес битовой карты памяти (Размер 131072B=32ст.) VIDEO_BUFF_ADDR = 0xFFFFC000 ; Адрес видеобуфера KERNEL_DIR_PAGES_ADDR = 0xFFFFD000 ; Адрес каталога страниц ;KERNEL_PAGES_TABLE_ADDR = 0xFFFFE000 ; Адрес таблицы со страницами ядра TEMP_PAGE_ADDR = 0xFFFFF000 ; Адрес временной страницы KERNEL_SIZE = 4096*2 ; Размер ядра в байтах KERNEL_SEC_ID = BOOT_CONT_SEC_ID+(BOOT_CONT_SIZE/512) ; LBA номер первого сектора ядра Код: ; Маска 20 старших установленных бит MASK_HEIGHT_BITS_20 = 0FFFFF000h ; Маска 10 младших установленных бит MASK_LOW_BITS_10 = 3FFh ; Извлекаем из адреса номер PDE дескриптора TEMP_PAGE_PDE_ID = (TEMP_PAGE_ADDR shr 22) ; Извлекаем из адреса номер PTE дескриптора TEMP_PAGE_PTE_ID = (TEMP_PAGE_ADDR shr 12) and MASK_LOW_BITS_10 kernel_dir_pages dd KERNEL_DIR_PAGES_PADDR macro vmm.init { pmm.init ; Спроецировать физ. адрес каталога страниц по адресу KERNEL_DIR_PAGE_ADDR vmm.map_page KERNEL_DIR_PAGES_PADDR, KERNEL_DIR_PAGES_ADDR mov [kernel_dir_pages],KERNEL_DIR_PAGES_ADDR ; Отмонтировать первый МБ ; vmm.unmount_one_mb ; После этого всё ломается и ребутится } ; Отображение физ. страницы во временную вирт. страницу ; page_paddr - физ. адрес страницы macro vmm.map_temp_page page_paddr { push eax mov eax,page_paddr call vmm_map_temp_page pop eax } ; Отображение физ. страниц на вирт. адреса ; page_paddr - физ. адрес страницы ; page_addr - вирт. адрес страницы macro vmm.map_page page_paddr, page_addr { push ecx esi mov ecx,page_paddr mov esi,page_addr call vmm_map_page pop esi ecx } macro vmm.unmount_one_mb { push eax esi edi vmm.map_temp_page ONE_MB_TABLE_PAGES_PADDR mov eax,0 mov esi,0 mov edi,TEMP_PAGE_ADDR mov ecx,100000h/PMM_PAGE_SIZE @@: mov [edi],dword 0 add edi,4 invlpg [esi] add esi,1000h loop @b ; Устанавливаем текущий каталог таблиц страниц pop edi esi eax } ; Отображение физ. страницы во временную вирт. страницу ; EAX - физ. адрес страницы vmm_map_temp_page: push eax ebx esi mov ebx,[kernel_dir_pages] ; EAX = адрес директории страниц add ebx,TEMP_PAGE_PDE_ID*4 ; Прибавляем к адресу дир. смещение PDE дескриптора mov esi,[ebx] ; Читаем содержимое дескриптора в ESI and esi,MASK_HEIGHT_BITS_20 ; Преобразуем содержимое дескриптора в адрес таблицы страниц add eax,11b ; Добовляем атрибуты к физ. адресу, получаем дескриптор mov [esi+TEMP_PAGE_PTE_ID*4],eax ; Прибавляем к адресу таблицы смещение PTE дескриптора, вписываем дескриптор invlpg [TEMP_PAGE_ADDR] pop esi ebx eax ret ; Отображение физ. страницы на виртуальную ; ECX - Адрес физ. страницы ; ESI - Адрес вирт. страницы vmm_map_page: push eax ecx esi edi invlpg [esi] mov edi,esi shr esi,22 ; Извлекаем из адреса номер PDE дескриптора shl esi,2 ; Умножаем его на 4 и получаем смещение в директории страниц shl edi,10 ; Извлекаем из адреса номер PTE дескриптора shr edi,22 shl edi,2 ; Умножаем его на 4 и получаем смещение в таблице страниц add esi,[kernel_dir_pages] ; Прибавляем к адресу дир. смещение PDE дескриптора mov eax,[esi] ; Прочитаем PDE дескриптор в EAX ; Проверяем присутствие таблицы страниц в каталоге cmp eax,0 ; Если дескриптор пуст то таблица отсутствует, нужно создать ее, jnz @f ; иначе переходим к изменению существующей таблицы ; Создание таблицы pmm.alloc_page ; Выделяем физ. страницу под таблицу add eax,111b ; Добавляем атрибуты и mov [esi],eax ; заносим ее в каталог @@: and eax,MASK_HEIGHT_BITS_20 ; Преобразуем содержимое дескриптора в адрес таблицы страниц call vmm_map_temp_page ; Отображаем таблицу во временную страницу add ecx,11b ; Добовляем атрибуты к физ. адресу, получаем дескриптор mov [TEMP_PAGE_ADDR+edi],ecx ; Прибавляем к адресу таблицы смещение PTE дескриптора, вписываем дескриптор pop edi esi ecx eax ret Если я от монтирую первый мегабайт то при мапировании физ. страниц в виртуальные, при обращении к ним система перезагружаеться. Также система перезагружается при включении прерываний, хотя если первый мегабайт не трогать всё нормально работает. |
Автор: | Himik [ 13 июл 2019, 00:44 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
FreeProger писал(а): Код: KERNEL_ADDR = 0xFFC00000 ; Адрес ядра KERNEL_STECK_ADDR = 0xFFFDAFFF ; Адрес стека ядра IDT_ADDR = 0xFFFDB000 MEMORY_BMP_ADDR = 0xFFFDC000 ; Адрес битовой карты памяти (Размер 131072B=32ст.) VIDEO_BUFF_ADDR = 0xFFFFC000 ; Адрес видеобуфера KERNEL_DIR_PAGES_ADDR = 0xFFFFD000 ; Адрес каталога страниц ;KERNEL_PAGES_TABLE_ADDR = 0xFFFFE000 ; Адрес таблицы со страницами ядра TEMP_PAGE_ADDR = 0xFFFFF000 ; Адрес временной страницы KERNEL_SIZE = 4096*2 ; Размер ядра в байтах KERNEL_SEC_ID = BOOT_CONT_SEC_ID+(BOOT_CONT_SIZE/512) ; LBA номер первого сектора ядра Суть действа и алгоритм сам по себе мало понятен. В комментариях желательно уточнять Адрес - Физ. адрес - Вирт. адрес... Нету особого смысла временно отображать структуры ядра. Можно делать все данные в постоянной доступности. Первый мегабайт уж точно не обязательно выключать, там наверняка лежат части твоей системы. |
Автор: | FreeProger [ 13 июл 2019, 06:55 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
Цитата: В комментариях желательно уточнять Адрес - Физ. адрес - Вирт. адрес... Я попытался включить это в само название канстант и переменных, если заканчивается на PADDR то это физ. Адрес, если просто ADDR то виртуальный. Суть действа в том что бы освободить адресное пространство от нуля. Я если честно пока что сам не уверен что это нужно. Просто считал что с нуля будет адресное пространство процессов. В первом МБ по идеи не должно остаться необходимых структур. В нем находятся таблицы с самим первым МБ, с каталогом страниц и с таблицей страниц ядра. При инициализации менеджера памяти пока ещё первый МБ доступен я отображаю каталог в пространство ядра, потом я отмонтирую мегабайт и могу обращаться к каталогу через отображенный адрес, вроде мне не чего из первого МБ больше не нужно. Но после от монтирования ядро работает а вот мапирование страниц перестаёт работать, не мапируются новые адреса, видимо из за этого не мапируются таблица прерываний. |
Автор: | FreeProger [ 13 июл 2019, 13:31 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
Опытным путём выяснил что проблема возникает лишь тогда когда я отмонтирую страницу с каталогом страниц или страницу с таблицей страниц первого мегабайта. Код: KERNEL_DIR_PAGES_PADDR = 11000h ; Физ. адрес каталога таблиц страниц ONE_MB_TABLE_PAGES_PADDR = 12000h ; Физ. адрес таблицы страниц первого мегабайта Но ведь я до от монтирования отображаю страницу с директорией в пространство ядра и обращаюсь в последующем по новому виртуальному адресу. А таблица с первым мегобайтом мне вроде вообще не нужна в последующем, почему же все ломается? |
Автор: | Himik [ 13 июл 2019, 22:20 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
Небольшая заметка про KERNEL_STECK_ADDR = 0xFFFDAFFF ; Адрес стека ядра Стек по-английски пишется через A и должен быть выровнен хотя бы на 4, а при использовании сопроцессора так вообще на 8 или 16 байт в зависимости от размера чисел. KERNEL_STACK_ADDR = 0xFFFDB000 |
Автор: | FreeProger [ 14 июл 2019, 12:40 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
Да я понакосячил с распределением вирт. адресов. Переделал, но проблема не ушла. Код: KERNEL_ADDR = 0xFFC00000 ; Вирт. адрес ядра
KERNEL_STACK_ADDR = 0xFFFDB000 ; Вирт. адрес стека ядра IDT_ADDR = 0xFFFDC000 ; Вирт. адрес таблицы прерывани VIDEO_BUFF_ADDR = 0xFFFDD000 ; Вирт. адрес видеобуфера KERNEL_DIR_PAGES_ADDR = 0xFFFDE000 ; Вирт. адрес каталога страниц MEMORY_BMP_ADDR = 0xFFFDF000 ; Вирт. адрес битовой карты памяти (Размер 131072B=32ст.) TEMP_PAGE_ADDR = 0xFFFFF000 ; Вирт. адрес временной страницы KERNEL_SIZE = 8192 ; Размер ядра в байтах KERNEL_SEC_ID = BOOT_CONT_SEC_ID+(BOOT_CONT_SIZE/512) ; LBA номер первого сектора ядра |
Автор: | FreeProger [ 15 июл 2019, 16:12 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
Вроде разобрался с менеджером памяти, все правильно работает. После от монтирования первого мегабайта менеджер работает исправно. Но когда я активирую обработчики прерываний, система ребутится. Причём если его не отмонтировать все исправно работает. Либо если отмонтировать а потом примонтировать обратно лишь первую страницу, первые 4К то все норм. Кто может подсказать, для чего прерываниям может понадобиться первые 4К? И ещё вопрос нужно ли вообще отмонтировать первый мегабайт если я хочу что бы адресное пространство процессов начиналось от нуля? |
Автор: | abondarev [ 15 июл 2019, 18:50 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
Цитата: для чего прерываниям может понадобиться первые 4К? А где у Вас лежит таблица (или вектор) прерываний? |
Автор: | Himik [ 15 июл 2019, 19:22 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
FreeProger писал(а): И ещё вопрос нужно ли вообще отмонтировать первый мегабайт если я хочу что бы адресное пространство процессов начиналось от нуля? Вопрос звучит не совсем корректно. Дело в том, что при создании пользовательского процесса надо создавать новый каталог страниц, и новые таблицы страниц, и на этапе создания указываешь для нулевой виртуальной страницы любой свободный физический адрес за пределами первого мегабайта. Отмонтировать ни чего не приходится. Впрочем, нулевую страницу вообще ни куда не монтируют, а оставляют пустой, для отлова ошибок обращения по нулевому (пустому) адресу с помощью исключения Page fault.
|
Автор: | FreeProger [ 15 июл 2019, 20:03 ] |
Заголовок сообщения: | Re: Менеджер виртуальной памяти |
Цитата: А где у Вас лежит таблица (или вектор) прерываний? Место под таблицу выделяется менеджером памяти, он выделяет первую попавшуюся свободную страницу. Проверял, выделяет правильно. Выше первого МБ и не занятые ядром. В данный момент вывел этот адрес 102000h, по адресу 100000h до 102000h у меня лежит ядро. Код: macro int.init
{ push eax mm.alloc_page ; EAX <- Free page paddr mm.map_page eax, IDT_ADDR, PAGE_PRESENT or PAGE_WRITE ci_8259A.init ; Инициализация контроллера прерываний 8259A ; Обработчики исключений (нарушений, ловушек и аварий) int.set_handler 0, exc_0, GATE_P+GATE_TRAP ; #DE Нарушение. Ошибка деления, код ошибки-нет. Вызвано: div, idiv int.set_handler 1, exc_1, GATE_P+GATE_TRAP ; #DB Нарушение/Ловешка. Исключение отладки. Код ошибки-нет. int.set_handler 2, exc_2, GATE_P+GATE_INT ; Немаскируемые прерывания поступающие на порт NMI. (Аппаратный сбой) int.set_handler 3, exc_3, GATE_P+GATE_TRAP ; #BP Ловушка. Программное прерывание служащее для отладки. Вызвано: int 3 int.set_handler 4, exc_4, GATE_P+GATE_TRAP ; #OF Ловушка. Фиксирует арефметическое переполнение(overflow). Код ошибки-нет. Вызвано: into int.set_handler 5, exc_5, GATE_P+GATE_TRAP ; #BR Нарушение. Нарушение границ массива. Код ошибки-нет. Вызвано: bound int.set_handler 6, exc_6, GATE_P+GATE_TRAP ; #UD Нарушение. Недопустимый код команды. Код ошибки-нет. int.set_handler 7, exc_7, GATE_P+GATE_TRAP ; #NM Нарушение. Сопроцессор не доступен. Код ошибки-нет. Вызвано: esc, wait int.set_handler 8, exc_8, GATE_P+GATE_TRAP ; #DF Авария. Двойное нарушение. Код ошибки-да int.set_handler 9, 0, 0 ; Авария. Выход сопроцессора из сегмента. Код ошибки-нет int.set_handler 10, exc_10, GATE_P+GATE_TRAP ; #TS Нарушение. Недопуст. сегмент состояния задачи TSS. Ошибка-да. Выз. jmp, call, iret, прерывание int.set_handler 11, exc_11, GATE_P+GATE_TRAP ; #NP Нарушение. Отсутствие сегмента. Ошибка-да. Выз. команда загрузки сегмент. регистра int.set_handler 12, exc_12, GATE_P+GATE_TRAP ; #SS Нарушение. Ошибка обращения к стеку. Код ошибки-да. Вызвано: командой обращения к стеку int.set_handler 13, exc_13, GATE_P+GATE_TRAP ; #GP Нарушение. Общая защита. Код ошибки-да. Вызвано: команда обращения к памяти int.set_handler 14, exc_14, GATE_P+GATE_TRAP ; #PF Нарушение. Страничное нарушение. Код ошибки-да. Вызвано: команда обращения к памяти int.set_handler 15, 0, 0 ; Не используется int.set_handler 16, exc_16, GATE_P+GATE_TRAP ; #MF Нарушение. Сопроцессор не доступен. Код ошибки-нет. Вызвано: esc, wait int.set_handler 17, exc_17, GATE_P+GATE_TRAP ; #AC Нарушение. Ошибка выравнивания. Код ошибки-да. Вызвано: команда обращения к памяти ; 18-31 Зарезервировано ; Аппаратные прерывания (Hardware interrupts) от ведущего контроллера 8259A int.set_handler 32, int8_handler, GATE_P+GATE_INT ; IRQ0 — программируемый интервальный таймер или высокоточный таймер событий №0 int.set_handler 33, int9_handler, GATE_P+GATE_INT ; IRQ1 — клавиатура PS/2 int.set_handler 34, int_EOI, GATE_P+GATE_INT ; IRQ2 — запрос прерывания от ведомого контроллера прерываний (8259A - Slave) int.set_handler 35, int_EOI, GATE_P+GATE_INT ; IRQ3 — произвольное устройство (в IBM PC/AT — последовательный порт COM2 и COM4) int.set_handler 36, int_EOI, GATE_P+GATE_INT ; IRQ4 — произвольное устройство (в IBM PC/AT — последовательный порт COM1 и COM3) int.set_handler 37, int_EOI, GATE_P+GATE_INT ; IRQ5 — произвольное устройство (в IBM PC/AT — параллельный порт LPT2) int.set_handler 38, int_EOI, GATE_P+GATE_INT ; IRQ6 — произвольное устройство (в IBM PC/AT — контроллер гибких дисков) int.set_handler 39, int_EOI, GATE_P+GATE_INT ; IRQ7 — произвольное устройство (в IBM PC/AT — параллельный порт LPT1) ; Аппаратные прерывания (Hardware interrupts) от ведомого контроллера 8259A (линии шины ISA) int.set_handler 40, int_EOI, GATE_P+GATE_INT ; IRQ8 — часы реального времени или высокоточный таймер событий №1 int.set_handler 41, int_EOI, GATE_P+GATE_INT ; IRQ9 — произвольное устройство int.set_handler 42, int_EOI, GATE_P+GATE_INT ; IRQ10 — произвольное устройство int.set_handler 43, int_EOI, GATE_P+GATE_INT ; IRQ11 — произвольное устройство или высокоточный таймер событий №2 int.set_handler 44, int_EOI, GATE_P+GATE_INT ; IRQ12 — произвольное устройство, обычно мышь PS/2, либо высокоточный таймер событий №3 int.set_handler 45, int_EOI, GATE_P+GATE_INT ; IRQ13 — ошибка арифметического сопроцессора int.set_handler 46, int_EOI, GATE_P+GATE_INT ; IRQ14 — произвольное устройство, обычно первый контроллер ATA (или контроллер Serial ATA в режиме совместимости int.set_handler 47, int_EOI, GATE_P+GATE_INT ; IRQ15 — произвольное устройство, обычно второй контроллер ATA (или контроллер Serial ATA в режиме совместимости) ; Программные прерывания (Software interrupts) int.set_handler 48, syscall_handler, GATE_P+GATE_DPL3+GATE_TRAP ; Ловушка. Печать строки ;--------------------------------------------------------------------------------------------------------------- lidt [IDTR] ; загружаем регистр IDTR. ; разрещаем аппаратные прерывания и NMI in al,70h and al,7Fh out 70h,al ; разрешить NMI прерывания sti ; Разрешить аппаратные прерывания pop eax } IDTR: .size dw (256*8)-1 ; Размер IDT таблицы .addr dd IDT_ADDR ; Адрес IDT таблицы ; Добавить шлюз прерывания в IDT ; int_id - Номер прерывания (1 байт) ; handler - Адресс обработчика прерывания (Смещение в сегменте) (4 байта) ; attr - Атрибуты. Бит присутствия, уровень привилегий, storageSegment, тип шлюза macro int.set_handler int_id, handler, attr { push eax ebx ecx mov eax,int_id mov ebx,handler mov ecx,attr call int_set_handler pop ecx ebx eax } ; Добавить шлюз прерывания в IDT ; eax - Номер прерывания (1 байт) ; ebx - Адресс обработчика прерывания (Смещение в сегменте) (4 байта) ; ecx - Атрибуты. Бит присутствия, уровень привилегий, storageSegment, тип шлюза int_set_handler: push ebx mov word [IDT_ADDR + (eax*8)],bx ; Записываем младшую часть адреса shr ebx, 16 ; Сдвигаем старшую часть адреса в BX mov word [IDT_ADDR + (eax*8) + 6],bx ; Записываем старшую часть адреса mov word [IDT_ADDR + (eax*8) + 2],SL_CODE ; Записываем селектор сегмента mov byte [IDT_ADDR + (eax*8) + 4],0 ; Пустой. Если это шлюз вызова то младшие 5 бит это количество параметров, старшие 3 бита = 0 mov byte [IDT_ADDR + (eax*8) + 5],cl ; Установка параметров шлюза прерывания pop ebx ret |
Страница 1 из 2 | Часовой пояс: UTC + 3 часа |
Powered by phpBB® Forum Software © phpBB Group http://www.phpbb.com/ |