0から作るソフトウェア開発
日々勉強中。。。 |
Follow @Nina_Petipa |
0から作るOS開発 ページングその2 仮想メモリ管理 |
PDEとPTEとページサイズを表すことができるビット数 | ||||
---|---|---|---|---|
エントリ | エントリの数 | 16進数 | 2進数 | 必要なビット数 |
PDE | 0-1023 | 0x000-0x3FF | 0000000000b-1111111111b | 10ビット |
PTE | 0-1023 | 0x000-0x3FF | 0000000000b-1111111111b | 10ビット |
ページサイズ | 0-4095 | 0x000-0xFFF | 000000000000b-111111111111b | 12ビット |
仮想メモリ管理の実装 |
/* ================================================================================== Description : Virtual Memory ================================================================================== */
#define DEF_MM_PAGE_SIZE 4096 #define DEF_MM_KERNEL_START 0x00010000 #define DEF_MM_PTE_INDEX_MASK 0x000003FF #define DEF_MM_PTE_INDEX_SHIFT 12 #define DEF_MM_PDE_INDEX_MASK 0x000003FF #define DEF_MM_PDE_INDEX_SHIFT 22 typedef PAGE_TABLE_ENTRY PAGE_TABLE typedef PAGE_DIRECTORY_ENTRY PAGE_DIRECTORY #define DEF_MM_NUM_PTE 1024 #define DEF_MM_PAGE_TABLE_SIZE ( sizeof( PAGE_TABLE_ENTRY ) * DEF_MM_NUM_PTE ) #define DEF_MM_NUM_PDE 1024 #define DEF_MM_PAGE_DIRECTORY_SIZE ( sizeof( PAGE_DIRECTORY_ENTRY ) * DEF_MM_NUM_PDE )
/* ================================================================================== Description : Virtual Memory Status ================================================================================== */
#define DEF_MM_OK 0 #define DEF_MM_ERROR (-1)
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :allocPage Input :PAGE_TABLE_ENTRY* entry < PTE to be allocated > Output :void Return :PAGE_TABLE_ENTRY* < allocated pte > Description :allocate a page _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC PAGE_TABLE_ENTRY* allocPage( PAGE_TABLE_ENTRY* entry ) { VOID *physical_address; physical_address = allocSingleMemoryBlock( ); if( physical_address == Null ) { return( ( PAGE_TABLE_ENTRY* )physical_address ) ); } setPtePageFrameAddress( entry, ( unsigned long )physical_address ); setPteFlags( entry, DEF_PTE_FLAGS_P ); return( ( PAGE_TABLE_ENTRY* )physical_address ) ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :freePage Input :PAGE_TABLE_ENTRY* entry < PTE to be freed > Output :void Return :void Description :free a page _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC VOID freePage( PAGE_TABLE_ENTRY* entry ) { VOID *physical_address; physical_address = ( VOID* )getPtePageFrameAddress( entry ); if( physical_address == Null ) { return; } freeSingleMemoryBlock( ( VOID* )physical_address ); clearPteFlags( entry, DEF_PTE_FLAGS_P ); }
/* ================================================================================== Funtion :getPteIndex Input :unsigned long virtual_address < virtual address > Output :void Return :unsigned long < pte index > Description :get pte index by virtual address ================================================================================== */
PRIVATE INLINE unsigned long getPteIndex( unsigned long virtual_address ) { unsigned long index; index = ( virtual_address >> DEF_MEMORY_PTE_INDEX_SHIFT ) & DEF_MEMORY_PTE_INDEX_MASK return( index ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :getPTE Input :PAGE_TABLE* table < page table > unsigned long virtual_address < virtual address > Output :void Return :PAGE_TABLE_ENTRY* < indexed pte > Description :get page table entry indexed by virtual address _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE PAGE_TABLE_ENTRY* getPTE( PAGE_TABLE* table, unsigned long virtual_address ) { PAGE_TABLE_ENTRY *entry; if( table == Null ) { return( ( PAGE_TABLE_ENTRY* )Null ); }
/* ------------------------------------------------------------------------ */ /* get first pte in the talbe */ /* ------------------------------------------------------------------------ */
entry = ( PAGE_TABLE_ENTRY* )table; return( &entry[ getPteIndex( virtual_address ) ] ); }
/* ================================================================================== Funtion :getPdeIndex Input :unsigned long virtual_address < virtual address > Output :void Return :unsigned long < pde index > Description :get pde index by virtual address ================================================================================== */
PRIVATE INLINE unsigned long getPdeIndex( unsigned long virtual_address ) { unsigned long index; index = ( virtual_address >> DEF_MM_PDE_INDEX_SHIFT ) & DEF_MM_PDE_INDEX_MASK return( index ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :getPDE Input :PAGE_DIRECTORY* directory < page directory > unsigned long virtual_address < virtual address > Output :void Return :PAGE_DIRECTORY_ENTRY* < indexed pde > Description :get page directory entry indexed by virtual address _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE PAGE_DIRECTORY_ENTRY* getPDE( PAGE_DIRECTORY* directory, unsigned long virtual_address ) { PAGE_DIRECTORY_ENTRY *entry; if( table == Null ) { return( ( PAGE_DIRECTORY* )Null ); }
/* ------------------------------------------------------------------------ */ /* get first pde in the director */ /* ------------------------------------------------------------------------ */
entry = ( PAGE_DIRECTORY_ENTRY* )directory; return( &entry[ getPdeIndex( virtual_address ) ] ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :writeCPUCR3 Input :Unsigned long value < value to write > Output :void Return :void Description :write to CR3 register _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE VOID writeCPUCR3( unsigned long value ) { __asm__ __volatile__( "mov %0, %%cr3" : : "r"( value ) ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :switchNewPDE Input : PAGE_DIRECTORY *directory < page directory > Output :void Return :void Description :switch page direcotry from current to new one _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE VOID switchNewPDE( PAGE_DIRECTORY *directory ) { if( directory != Null ) {
/* ------------------------------------------------------------------- */ /* switch new directory */ /* ------------------------------------------------------------------- */
writeCPUCR3( ( unsigned long )directory ); } } PRIVATE PAGE_DIRECTORY *current_pd;
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :getCurrentPageDirectory Input :void Output :void Return :PAGE_DIRECTORY *directory < current page directory > Description :get current page diretory _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE PAGE_DIRECTORY* getCurrentPageDirectory( VOID ) { return( current_pd ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :invlpg Input :unsigned long virtual_address < virtual address > Output :void Return :void Description :invoke invlpg instruction _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE VOID invlpg( unsigned long virtual_address ) { __asm__ __volatile__( "cli" ); __asm__ __volatile__( "invlpg %0" : : "m"( virtual_address ) : "memory" ); __asm__ __volatile__( "sti" ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :flushTLB Input :unsigned long virtual_address < virtual address to be flushed > Output :void Return :void Description :flush tlb _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE VOID flushTLB( unsigned long virtual_address ) { invlpg( virtual_address ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :mapPage Input :unsigned long physical_address < physical address to map page > unsigned long viertual_address < virtual address which page describes > Output :void Return :STATUS < memory management status > Description :map page to physical address _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC STATUS mapPage( unsigned long physical_address, unsigned long virtual_address ) { PAGE_DIRECTORY *page_directory; PAGE_DIRECTORY_ENTRY *pde; PAGE_TABLE *page_table; PAGE_TABLE_ENTRY *pte;
/* ------------------------------------------------------------------------ */ /* get pages */ /* ------------------------------------------------------------------------ */
page_directory = getCurrentPageDirectory( ); if( page_directory == Null ) { return( DEF_MM_ERROR ); } pde = getPDE( page_directory, virtual_address );
if( !isPdePresent( pde ) ) { page_table = ( PAGE_TABLE* )allocSingleMemoryBlock( ); if( page_table == Null ) { return( DEF_MM_ERROR ); }
/* -------------------------------------------------------------------- */ /* clear page table */ /* -------------------------------------------------------------------- */
kmemset( ( VOID* )page_table, 0x00, DEF_MM_PAGE_TABLE_SIZE );
/* -------------------------------------------------------------------- */ /* create new pde */ /* -------------------------------------------------------------------- */
setPdeFlags( pde, DEF_PDE_FLAGS_P | DEF_PDE_FLAGS_RW ); setPdePageFrameAddress( pde, ( unsigned long )page_table ); } else { /* -------------------------------------------------------------------- */ /* get page table from page directory entry */ /* -------------------------------------------------------------------- */
page_table = ( PAGE_TABLE* )getPdePageTableAddress( pde ); }
/* ------------------------------------------------------------------------ */ /* set up pte */ /* ------------------------------------------------------------------------ */
pte = getPTE( page_table, virtual_address ); setPteFlags( pte, DEF_PTE_FLAGS_P | DEF_PTE_FLAGS_RW ); setPtePageFrameAddress( pte, physical_address ); return( DEF_MM_OK ); }
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :initVirtualMemoryManagement Input :void Output :void Return :STATUS < memory management status > Description :initialize virtual memory management _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC STATUS initVirtualMemoryManagement( VOID ) { PAGE_TABLE *table_low; PAGE_TABLE *table_high; PAGE_TABLE_ENTRY *pte; PAGE_DIRECTORY *directory; PAGE_DIRECTORY_ENTRY *pde int i; unsigned long frame; unsigned long virtual_address; table_low = ( PAGE_TABLE* )allocSingleMemoryBlock( ); if( table_low == Null ) { return( DEF_MM_ERROR ); } table_high = ( PAGE_TABLE* )allocSingleMemoryBlock( ); if( table_high == Null ) { return( DEF_MM_ERROR ); }
/* ------------------------------------------------------------------------ */ /* clear page table */ /* ------------------------------------------------------------------------ */
kmemset( ( VOID* )table_low, 0x00, DEF_MM_PAGE_TABLE_SIZE ); kmemset( ( VOID* )table_high, 0x00, DEF_MM_PAGE_TABLE_SIZE );
/* ------------------------------------------------------------------------ */ /* set up pages 0x00000000 - 0x003FF000 */ /* ------------------------------------------------------------------------ */
for( i = 0, frame = 0x00000000, virtual_address = 0x00000000 ; i < DEF_MM_NUM_PTE ; frame += DEF_MM_PAGE_SIZE, virtual_address += DEF_MM_PAGE_SIZE ) { pte = getPTE( table_low, virtual_address ); setPteFlags( pte, DEF_PTE_FLAGS_P | DEF_PTE_FLAGS_RW ); setPtePageFrameAddress( pte, frame ); }
/* ------------------------------------------------------------------------ */ /* set up pages 0xC0000000 - 0xC03FF000 */ /* ------------------------------------------------------------------------ */
for( i = 0, frame = 0x00100000, virtual_address = 0xC0000000 ; i < DEF_MM_NUM_PTE ; frame += DEF_MM_PAGE_SIZE, virtual_address += DEF_MM_PAGE_SIZE ) { pte = getPTE( table_high, virtual_address ); setPteFlags( pte, DEF_PTE_FLAGS_P | DEF_PTE_FLAGS_RW ); setPtePageFrameAddress( pte, frame ); }
directory = ( PAGE_DIRECTORY* )allocSingleMemoryBlock( ); if( directory == Null ) { return( DEF_MM_ERROR ); }
/* ------------------------------------------------------------------------ */ /* set up pdes 0x00000000 - 0x003FF000 */ /* ------------------------------------------------------------------------ */
pde = getPDE( directory, 0x00000000 ); setPdeFlags( pde, DEF_PDE_FLAGS_P | DEF_PDE_FLAGS_RW ); setPdePageFrameAddress( pde, ( unsigned long )table_low );
/* ------------------------------------------------------------------------ */ /* set up pdes 0xC0000000 - 0xC03FF000 */ /* ------------------------------------------------------------------------ */
pde = getPDE( directory, 0x00000000 ); setPdeFlags( pde, DEF_PDE_FLAGS_P | DEF_PDE_FLAGS_RW ); setPdePageFrameAddress( pde, ( unsigned long )table_high );
/* ------------------------------------------------------------------------ */
/* switch to the new pdes */
/* ------------------------------------------------------------------------ */
switchNewPDE( directory );
/* ------------------------------------------------------------------------ */
/* entering the paging */
/* ------------------------------------------------------------------------ */
pagingOn( );
CR0レジスタ | ||
---|---|---|
ビット | ビット名称 | 説明 |
0 | PE | Protection Enableビット。このビットをONにするとプロテクティッドモードへ移行します |
1 | MP |
Monitor Co-Processorビット。モニタ・コプロセッサビット WAIT命令を実行したときの動作を変更することができます 0:TSビットが0でも1でも関係なく無視する 1:TSビットが1であればコプロセッサ使用不可能例外を発生させる |
2 | EM |
Emulationビット。エミュレーションビット 浮動小数点演算(x87 FPU)命令を実行したときの動作を変更することができます 0:x87 FPUを持っているので命令実行可能 1:命令実行時x87 FPUを持っていないのでコプロセッサ使用不可例外が発生する ソフトウェアによってエミュレーションを行う ※細かい条件はIntelのCPU仕様書をご確認ください |
3 | TS |
Task Switchビット。タスクスイッチビット CPUはタスクスイッチする時にこのビットを1にします ※EM、MPビットの設定によっては影響があります |
4 | ET |
Extended Typeビット。拡張タイプビット 0:80287以前のCPU 1:80387以後のCPU |
5 | NE |
数値演算エラービット 0:x87 FPUエラーレポート無効 1:x87 FPUエラーレポート有効 |
16 | WP |
Write Protectビット。書き込み保護ビット 0:リング0のプログラムが読み取り専用のユーザ空間に書き込むことができる 1:リング0のプログラムが読み取り専用のユーザ空間に書き込むことを禁止 |
18 | AM |
Alignment Maskビット。アライメントマスクビット 0:自動アライメントチェック無効 1:自動アライメントチェック有効 |
29 | NW |
Not Write throughビット。ノットライトスルービット 0:CDビットが0の場合キャッシュをライトバックまたはライトスルーが有効 1:CDビットとNWビットの組み合わせはCPUのマニュアル10-17ページを参照ください |
30 | CD |
Cache Disableビット。キャッシュ無効ビット 0:NWビットが0の場合キャッシュ操作が有効 1:CDビットとNWビットの組み合わせはCPUのマニュアル10-17ページを参照ください |
31 | PG |
PaGingビット。ページングビット 0:ページング無効 1:ページング有効 |
/* _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ Funtion :pagingOn Input :void Output :void Return :void Description :set page enable bit _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ */
PUBLIC INLINE void pagingOn( void ) { /* ------------------------------------------------------------------------- */ /* disable interrupt */ /* ------------------------------------------------------------------------- */ disableInterrupt( ); /* ------------------------------------------------------------------------- */ /* paging on */ /* ------------------------------------------------------------------------- */ __asm__ __volatile__( "PUSH %eax" ); __asm__ __volatile__( "MOV %eax, %cr0 " ); __asm__ __volatile__( "OR %eax, 0x80000000" ); __asm__ __volatile__( "MOV %cr0, %eax" ); __asm__ __volatile__( "POP %eax" ); }
ページフォルトのエラーコード | ||
---|---|---|
ビット | ビット名称 | 説明 |
0 | P |
0:ページが物理メモリに存在しいないので、ページフォルトが発生しました 1:ページのアクセス特権レベル以下の特権でアクセスしたので、ページフォルトが発生しました |
1 | W/R |
0:ページフォルトが発生した原因となったアクセスは、読み込みです 1:ページフォルトが発生した原因となったアクセスは、書き込みです |
2 | U/S |
0:ページフォルトが発生した原因となったアクセスは、特権レベルで行われました 1:ページフォルトが発生した原因となったアクセスは、ユーザ特権レベルで行われました |
3 | RSVD |
0:ページフォルトが発生した原因は、PDEの予約ビットが1にセットされたからではありません 1:ページフォルトが発生した原因は、PDEの予約ビットが1にセットされたからです |