Phoenix
Object-oriented orthogonally persistent operating system
|
00001 /* 00002 * /phoenix/kernel/sys/arch/x86_64/vm_md.h 00003 * 00004 * This file is a part of Phoenix operating system. 00005 * Copyright (c) 2011-2012, Artyom Lebedev <artyom.lebedev@gmail.com> 00006 * All rights reserved. 00007 * See COPYING file for copyright details. 00008 */ 00009 00010 #ifndef MD_VM_H_ 00011 #define MD_VM_H_ 00012 00017 namespace vm { 00018 00019 enum { 00021 PAGE_SHIFT = 12, 00022 00024 NUM_LAT_TABLES = 4, 00025 }; 00026 00028 typedef u64 PageIdx; 00029 00031 typedef u32 LatEntryIdx; 00032 00034 typedef u32 ProcCtxId; 00035 00040 class VaddrDecoder { 00041 public: 00043 inline VaddrDecoder(vaddr_t va = 0) { _va.va = va; } 00044 00051 static inline u32 GetTableSize(u32 UNUSED tableLvl) { 00052 ASSERT(tableLvl < NUM_LAT_TABLES); 00053 /* 512 entries in eachLatEntrytable. */ 00054 return 512; 00055 } 00056 00063 inline LatEntryIdx GetEntryIndex(u32 tableLvl) { 00064 switch (tableLvl) { 00065 case 0: 00066 return _va.fields.tblIdx0; 00067 case 1: 00068 return _va.fields.tblIdx1; 00069 case 2: 00070 return _va.fields.tblIdx2; 00071 case 3: 00072 return _va.fields.tblIdx3; 00073 } 00074 FAULT("Table index is out of range: %d", tableLvl); 00075 return 0; 00076 } 00077 00079 inline vaddr_t GetPageOffset() { 00080 return _va.fields.pageOffset; 00081 } 00082 00083 private: 00084 struct VaFields { 00085 vaddr_t pageOffset:PAGE_SHIFT, 00086 tblIdx0:9, /* Page table */ 00087 tblIdx1:9, /* Page directory */ 00088 tblIdx2:9, /* Page directory pointers table */ 00089 tblIdx3:9, /* PML4 */ 00090 :16; 00091 }; 00092 00093 volatile union { 00094 vaddr_t va; 00095 VaFields fields; 00096 } _va; 00097 }; 00098 00100 class LatEntry { 00101 public: 00102 inline LatEntry() { 00103 _ptr.raw = 0; 00104 _tableLvl = 0; 00105 } 00106 00114 inline LatEntry(vaddr_t va, void *table, u32 tableLvl = 0) { 00115 Set(va, table, tableLvl); 00116 } 00117 00123 inline LatEntry(void *entry, u32 tableLvl = 0) { 00124 Set(entry, tableLvl); 00125 } 00126 00134 inline void Set(vaddr_t va, void *table, u32 tableLvl = 0) { 00135 ASSERT(tableLvl <= NUM_LAT_TABLES); 00136 _tableLvl = tableLvl; 00137 _ptr.ptr = table; 00138 VaddrDecoder dec(va); 00139 _ptr.raw += dec.GetEntryIndex(tableLvl); 00140 } 00141 00147 inline void Set(void *entry, u32 tableLvl = 0) { 00148 ASSERT(tableLvl <= NUM_LAT_TABLES); 00149 _tableLvl = tableLvl; 00150 _ptr.ptr = entry; 00151 } 00152 00157 inline bool IsAccessed() { 00158 if (_tableLvl == NUM_LAT_TABLES) { 00159 /* CR3 does not have the flag. */ 00160 return false; 00161 } 00162 return _ptr.entryPage->accessed; 00163 } 00164 00170 inline bool SetAccessed(bool flag = true) { 00171 if (_tableLvl == NUM_LAT_TABLES) { 00172 /* CR3 does not have the flag. */ 00173 return false; 00174 } 00175 bool curFlag = _ptr.entryPage->accessed; 00176 _ptr.entryPage->accessed = flag; 00177 return curFlag; 00178 } 00179 00184 inline bool IsDirty() { 00185 if (_tableLvl == NUM_LAT_TABLES) { 00186 /* CR3 does not have the flag. */ 00187 return false; 00188 } 00189 return _ptr.entryPage->dirty; 00190 } 00191 00197 inline bool SetDirty(bool flag = true) { 00198 if (_tableLvl == NUM_LAT_TABLES) { 00199 /* CR3 does not have the flag. */ 00200 return false; 00201 } 00202 bool curFlag = _ptr.entryPage->dirty; 00203 _ptr.entryPage->dirty = flag; 00204 return curFlag; 00205 } 00206 00211 bool CheckFlag(LatEntryFlags flag) { 00212 ASSERT(_tableLvl <= NUM_LAT_TABLES); 00213 if (_tableLvl == NUM_LAT_TABLES) { 00214 switch (flag) { 00215 case LAT_EF_WRITE_THROUGH: 00216 return _ptr.cr3->pwt; 00217 case LAT_EF_CACHE_DISABLE: 00218 return _ptr.cr3->pcd; 00219 default: 00220 return false; 00221 } 00222 } 00223 switch (flag) { 00224 case LAT_EF_PRESENT: 00225 return _ptr.entryPage->present; 00226 case LAT_EF_WRITE: 00227 return _ptr.entryPage->write; 00228 case LAT_EF_USER: 00229 return _ptr.entryPage->user; 00230 case LAT_EF_WRITE_THROUGH: 00231 return _ptr.entryPage->writeThrough; 00232 case LAT_EF_CACHE_DISABLE: 00233 return _ptr.entryPage->cacheDisable; 00234 case LAT_EF_EXECUTE: 00235 return !_ptr.entryPage->executeDisable; 00236 case LAT_EF_GLOBAL: 00237 return _ptr.entryPage->global; 00238 } 00239 FAULT("Invalid flag specified: %d", flag); 00240 return false; 00241 } 00242 00250 bool SetFlag(LatEntryFlags flag, bool setIt = true) { 00251 bool prev; 00252 ASSERT(_tableLvl <= NUM_LAT_TABLES); 00253 if (_tableLvl == NUM_LAT_TABLES) { 00254 switch (flag) { 00255 case LAT_EF_WRITE_THROUGH: 00256 prev = _ptr.cr3->pwt; 00257 _ptr.cr3->pwt = setIt ? 1 : 0; 00258 break; 00259 case LAT_EF_CACHE_DISABLE: 00260 prev = _ptr.cr3->pcd; 00261 _ptr.cr3->pcd = setIt ? 1 : 0; 00262 break; 00263 default: 00264 prev = false; 00265 break; 00266 } 00267 return prev; 00268 } 00269 switch (flag) { 00270 case LAT_EF_PRESENT: 00271 prev = _ptr.entryPage->present; 00272 _ptr.entryPage->present = setIt ? 1 : 0; 00273 break; 00274 case LAT_EF_WRITE: 00275 prev = _ptr.entryPage->write; 00276 _ptr.entryPage->write = setIt ? 1 : 0; 00277 break; 00278 case LAT_EF_USER: 00279 prev = _ptr.entryPage->user; 00280 _ptr.entryPage->user = setIt ? 1 : 0; 00281 break; 00282 case LAT_EF_WRITE_THROUGH: 00283 prev = _ptr.entryPage->writeThrough; 00284 _ptr.entryPage->writeThrough = setIt ? 1 : 0; 00285 break; 00286 case LAT_EF_CACHE_DISABLE: 00287 prev = _ptr.entryPage->cacheDisable; 00288 _ptr.entryPage->cacheDisable = setIt ? 1 : 0; 00289 break; 00290 case LAT_EF_EXECUTE: 00291 prev = !_ptr.entryPage->executeDisable; 00292 if (vmCaps.IsValid() && vmCaps.nx) { 00293 _ptr.entryPage->executeDisable = setIt ? 0 : 1; 00294 } 00295 break; 00296 case LAT_EF_GLOBAL: 00297 prev = _ptr.entryPage->global; 00298 if (_tableLvl == 0 && vmCaps.IsValid() && vmCaps.pge) { 00299 _ptr.entryPage->global = setIt ? 1 : 0; 00300 } 00301 break; 00302 default: 00303 FAULT("Invalid flag specified: %d", flag); 00304 break; 00305 } 00306 return prev; 00307 } 00308 00316 long SetFlags(long flags) { 00317 static const LatEntryFlags allFlags[] = { 00318 LAT_EF_PRESENT, 00319 LAT_EF_WRITE, 00320 LAT_EF_USER, 00321 LAT_EF_WRITE_THROUGH, 00322 LAT_EF_CACHE_DISABLE, 00323 LAT_EF_EXECUTE, 00324 LAT_EF_GLOBAL 00325 }; 00326 long prev; 00327 00328 for (auto flag: allFlags) { 00329 if (SetFlag(flag, flags & flag)) { 00330 prev |= flag; 00331 } 00332 } 00333 return prev; 00334 } 00335 00337 inline paddr_t GetAddress() { 00338 return _ptr.entryPage->pa << PAGE_SHIFT; 00339 } 00340 00345 inline paddr_t SetAddress(paddr_t pa) { 00346 ASSERT((pa & ((1 << PAGE_SHIFT) - 1)) == 0); 00347 paddr_t prevPa = _ptr.entryPage->pa << PAGE_SHIFT; 00348 _ptr.entryPage->pa = pa >> PAGE_SHIFT; 00349 return prevPa; 00350 } 00351 00353 inline void Clear() { *_ptr.raw = 0; } 00354 00358 inline operator paddr_t() { return GetAddress(); } 00359 00365 inline LatEntry &operator=(paddr_t pa) { SetAddress(pa); return *this; } 00366 00370 inline operator void *() { return _ptr.ptr; } 00371 00377 inline ProcCtxId GetProcCtxId() { 00378 ENSURE(_tableLvl == NUM_LAT_TABLES); 00379 return _ptr.cr3->pcid; 00380 } 00381 00387 inline ProcCtxId SetProcCtxId(ProcCtxId pcid) { 00388 ENSURE(_tableLvl == NUM_LAT_TABLES); 00389 ProcCtxId oldPcid = _ptr.cr3->pcid; 00390 if (vmCaps.IsValid() && vmCaps.pcid) { 00391 _ptr.cr3->pcid = pcid; 00392 } 00393 return oldPcid; 00394 } 00395 00399 inline void Activate() { 00400 ENSURE(_tableLvl == NUM_LAT_TABLES); 00401 cpu::wcr3(*_ptr.raw); 00402 } 00403 00404 private: 00405 00407 struct EntryCr3 { 00408 union { 00409 /* CR4.PCIDE = 0 */ 00410 paddr_t :3, 00411 pwt:1, 00412 pcd:1, 00413 :7, 00414 pa:40, 00415 :12; 00416 /* CR4.PCIDE = 1 */ 00417 paddr_t pcid:12; 00418 }; 00419 }; 00420 00422 struct EntryPage { 00423 paddr_t 00424 present:1, 00426 write:1, 00428 user:1, 00430 writeThrough:1, 00432 cacheDisable:1, 00436 accessed:1, 00440 dirty:1, 00442 pat:1, 00446 global:1, 00447 :3, 00449 pa:40, 00450 :11, 00452 executeDisable:1; 00453 }; 00454 00456 struct EntryTable { 00457 paddr_t 00458 present:1, 00460 write:1, 00462 user:1, 00464 writeThrough:1, 00466 cacheDisable:1, 00470 accessed:1, 00471 :1, 00479 pageSize:1, 00480 :4, 00482 pa:40, 00483 :11, 00485 executeDisable:1; 00486 }; 00487 00488 volatile union EntryPtr { 00489 void *ptr; 00490 paddr_t *raw; 00491 EntryCr3 *cr3; 00492 EntryPage *entryPage; 00493 EntryTable *entryTable; 00494 } _ptr; 00495 00496 u32 _tableLvl; 00497 }; 00498 00503 inline void 00504 InvalidateVaddr(vaddr_t va) 00505 { 00506 cpu::invlpg(va); 00507 } 00508 00510 inline void 00511 InitPaging(bool enablePaging) 00512 { 00513 if (enablePaging) { 00514 u64 cr0 = cpu::rcr0(); 00515 if (!(cr0 & cpu_reg::CR0_PG)) { 00516 cpu::wcr0(cr0 | cpu_reg::CR0_PG); 00517 } 00518 } else { 00519 cpu::CpuCaps caps; 00520 00521 /* Enable "execute-disable" feature if available. */ 00522 if (caps.GetCapability(cpu::CPU_CAP_PG_NX)) { 00523 cpu::wrmsr(cpu_reg::MSR_IA32_EFER, 00524 cpu::rdmsr(cpu_reg::MSR_IA32_EFER) | cpu_reg::IA32_EFER_NXE); 00525 } 00526 00527 u64 features = cpu::rcr4(); 00528 /* Enable global pages if available. */ 00529 if (caps.GetCapability(cpu::CPU_CAP_PG_PGE)) { 00530 features |= cpu_reg::CR4_PGE; 00531 } 00532 /* Enable process context identification if available. */ 00533 if (caps.GetCapability(cpu::CPU_CAP_PG_PCID)) { 00534 features |= cpu_reg::CR4_PCDIE; 00535 } 00536 cpu::wcr4(features); 00537 } 00538 } 00539 00540 } /* namespace vm */ 00541 00542 #endif /* MD_VM_H_ */