TianoCore EDK2 master
Loading...
Searching...
No Matches
CpuMmu.c
Go to the documentation of this file.
1
14#include <Uefi.h>
15#include <Library/BaseLib.h>
18#include <Library/CpuMmuLib.h>
19#include <Library/DebugLib.h>
23#include "TlbInvalid.h"
24#include "TlbExceptionHandle.h"
25#include "Page.h"
26
36BOOLEAN
38 VOID
39 )
40{
41 if (CsrRead (LOONGARCH_CSR_PGDL) != 0) {
42 return TRUE;
43 }
44
45 return FALSE;
46}
47
57BOOLEAN
59 VOID
60 )
61{
62 if ((CsrRead (LOONGARCH_CSR_CRMD) & BIT4) != 0) {
63 return TRUE;
64 }
65
66 return FALSE;
67}
68
79BOOLEAN
81 IN UINTN Entry
82 )
83{
84 if (Entry != INVALID_PAGE) {
85 return TRUE;
86 } else {
87 return FALSE;
88 }
89}
90
100STATIC
101BOOLEAN
103 IN UINTN Entry
104 )
105{
106 if ((Entry & (PAGE_HGLOBAL | PAGE_HUGE)) == (PAGE_HGLOBAL | PAGE_HUGE)) {
107 return TRUE;
108 } else {
109 return FALSE;
110 }
111}
112
121STATIC
122UINTN
124 IN UINTN Entry
125 )
126{
127 /* Set Valid and Global mapping bits */
128 return Entry | PAGE_GLOBAL | PAGE_VALID;
129}
130
141STATIC
142UINTN
144 IN UINT64 PageWalkCfg
145 )
146{
147 UINT32 Pwctl0;
148 UINT32 Pwctl1;
149
150 Pwctl0 = PageWalkCfg & MAX_UINT32;
151 Pwctl1 = (PageWalkCfg >> 32) & MAX_UINT32;
152
153 if (((Pwctl1 >> 18) & 0x3F) != 0x0) {
154 return LEVEL5;
155 } else if (((Pwctl1 >> 6) & 0x3F) != 0x0) {
156 return LEVEL4;
157 } else if (((Pwctl0 >> 25) & 0x3F) != 0x0) {
158 return LEVEL3;
159 }
160
161 return 0;
162}
163
174STATIC
175UINTN
177 IN UINT64 PageWalkCfg
178 )
179{
180 //
181 // PTwidth
182 //
183 return ((PageWalkCfg >> 5) & 0x1F);
184}
185
197STATIC
198BOOLEAN
200 IN UINTN Entry,
201 IN UINTN Level,
202 IN UINT64 PageWalkCfg
203 )
204{
205 if (Level == (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
206 return ((Entry & PAGE_VALID) == PAGE_VALID);
207 }
208
209 return IsValidHugePage (Entry);
210}
211
223STATIC
224BOOLEAN
226 IN UINTN Entry,
227 IN UINTN Level,
228 IN UINT64 PageWalkCfg
229 )
230{
231 if (Level == (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
232 //
233 // The last level is PAGE rather than Table.
234 //
235 return FALSE;
236 }
237
238 //
239 // Is DIR4 or DIR3 or DIR2 a Huge Page ?
240 //
241 return (!IsValidHugePage (Entry)) && (IsValidPte (Entry));
242}
243
253STATIC
254VOID
256 IN UINTN *Entry,
257 IN UINTN Value,
258 IN UINTN RegionStart,
259 IN BOOLEAN IsLiveBlockMapping
260 )
261{
262 *Entry = Value;
263
264 if (IsLiveBlockMapping && MmuIsInit ()) {
265 InvalidTlb (RegionStart);
266 }
267}
268
277STATIC
278UINTN
280 IN UINTN Entry
281 )
282{
283 return ((Entry & PTE_PPN_MASK) >> PTE_PPN_SHIFT);
284}
285
295STATIC
296UINTN
298 UINTN Entry,
299 UINTN Address
300 )
301{
302 UINTN Ppn;
303
304 Ppn = ((Address >> LOONGARCH_MMU_PAGE_SHIFT) << PTE_PPN_SHIFT);
305 ASSERT (~(Ppn & ~PTE_PPN_MASK));
306 Entry &= ~PTE_PPN_MASK;
307 return Entry | Ppn;
308}
309
318STATIC
319VOID
321 IN UINTN *TranslationTable,
322 IN UINT64 PageWalkCfg,
323 IN UINTN Level
324 )
325{
326 UINTN Index;
327 UINTN TableEntryNum;
328
329 TableEntryNum = (1 << ParsePageTableBitWidth (PageWalkCfg));
330
331 if (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
332 for (Index = 0; Index < TableEntryNum; Index++) {
333 if (IsTableEntry (TranslationTable[Index], Level, PageWalkCfg)) {
335 (UINTN *)(GetPpnfromPte ((TranslationTable[Index])) <<
336 LOONGARCH_MMU_PAGE_SHIFT),
337 PageWalkCfg,
338 Level + 1
339 );
340 }
341 }
342 }
343
344 FreePages (TranslationTable, 1);
345}
346
363STATIC
366 IN UINTN RegionStart,
367 IN UINTN RegionEnd,
368 IN UINTN AttributeSetMask,
369 IN UINTN AttributeClearMask,
370 IN UINTN *PageTable,
371 IN UINTN Level,
372 IN UINT64 PageWalkCfg,
373 IN BOOLEAN TableIsLive
374 )
375{
376 EFI_STATUS Status;
377 UINTN BlockShift;
378 UINTN BlockMask;
379 UINTN BlockEnd;
380 UINTN *Entry;
381 UINTN EntryValue;
382 UINTN *TranslationTable;
383 UINTN TableEntryNum;
384 UINTN TableBitWidth;
385 BOOLEAN NextTableIsLive;
386
387 ASSERT (Level < ParseMaxPageTableLevel (PageWalkCfg));
388 ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);
389
390 TableBitWidth = ParsePageTableBitWidth (PageWalkCfg);
391 BlockShift = (ParseMaxPageTableLevel (PageWalkCfg) - Level - 1) * TableBitWidth + LOONGARCH_MMU_PAGE_SHIFT;
392 BlockMask = MAX_ADDRESS >> (64 - BlockShift);
393
394 DEBUG (
395 (
396 DEBUG_VERBOSE,
397 "%a(%d): %llx - %llx set %lx clr %lx\n",
398 __func__,
399 Level,
400 RegionStart,
401 RegionEnd,
402 AttributeSetMask,
403 AttributeClearMask
404 )
405 );
406
407 TableEntryNum = (1 << TableBitWidth);
408 for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) {
409 BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);
410 Entry = &PageTable[(RegionStart >> BlockShift) & (TableEntryNum - 1)];
411
412 //
413 // If RegionStart or BlockEnd is not aligned to the block size at this
414 // level, we will have to create a table mapping in order to map less
415 // than a block, and recurse to create the block or page entries at
416 // the next level. No block mappings are allowed at all at level 2,
417 // so in that case, we have to recurse unconditionally.
418 //
419 if ((Level < 2) ||
420 (((RegionStart | BlockEnd) & BlockMask) != 0) || IsTableEntry (*Entry, Level, PageWalkCfg))
421 {
422 ASSERT (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1));
423 if (!IsTableEntry (*Entry, Level, PageWalkCfg)) {
424 //
425 // No table entry exists yet, so we need to allocate a page table
426 // for the next level.
427 //
428 TranslationTable = AllocatePages (1);
429 if (TranslationTable == NULL) {
430 return EFI_OUT_OF_RESOURCES;
431 }
432
433 ZeroMem (TranslationTable, EFI_PAGE_SIZE);
434
435 if (IsBlockEntry (*Entry, Level, PageWalkCfg)) {
436 //
437 // We are splitting an existing block entry, so we have to populate
438 // the new table with the attributes of the block entry it replaces.
439 //
441 RegionStart & ~BlockMask,
442 (RegionStart | BlockMask) + 1,
443 *Entry & PTE_ATTRIBUTES_MASK,
444 PTE_ATTRIBUTES_MASK,
445 TranslationTable,
446 Level + 1,
447 PageWalkCfg,
448 FALSE
449 );
450 if (EFI_ERROR (Status)) {
451 //
452 // The range we passed to UpdateRegionMappingRecursive () is block
453 // aligned, so it is guaranteed that no further pages were allocated
454 // by it, and so we only have to free the page we allocated here.
455 //
456 FreePages (TranslationTable, 1);
457 return Status;
458 }
459 }
460
461 NextTableIsLive = FALSE;
462 } else {
463 TranslationTable = (UINTN *)(GetPpnfromPte (*Entry) << LOONGARCH_MMU_PAGE_SHIFT);
464 NextTableIsLive = TableIsLive;
465 }
466
467 //
468 // Recurse to the next level
469 //
471 RegionStart,
472 BlockEnd,
473 AttributeSetMask,
474 AttributeClearMask,
475 TranslationTable,
476 Level + 1,
477 PageWalkCfg,
478 NextTableIsLive
479 );
480 if (EFI_ERROR (Status)) {
481 if (!IsTableEntry (*Entry, Level, PageWalkCfg)) {
482 //
483 // We are creating a new table entry, so on failure, we can free all
484 // allocations we made recursively, given that the whole subhierarchy
485 // has not been wired into the live page tables yet. (This is not
486 // possible for existing table entries, since we cannot revert the
487 // modifications we made to the subhierarchy it represents.)
488 //
489 FreePageTablesRecursive (TranslationTable, PageWalkCfg, Level + 1);
490 }
491
492 return Status;
493 }
494
495 if (!IsTableEntry (*Entry, Level, PageWalkCfg)) {
496 EntryValue = SetPpnToPte (0, (UINTN)TranslationTable);
498 Entry,
499 EntryValue,
500 RegionStart,
501 TableIsLive
502 );
503 }
504 } else {
505 EntryValue = (*Entry & ~AttributeClearMask) | AttributeSetMask;
506
507 EntryValue = SetPpnToPte (EntryValue, RegionStart);
508 EntryValue = SetValidPte (EntryValue);
509
510 if (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
511 EntryValue |= (PAGE_HGLOBAL | PAGE_HUGE | PAGE_VALID);
512 } else {
513 EntryValue |= PAGE_GLOBAL | PAGE_VALID;
514 }
515
516 ReplaceTableEntry (Entry, EntryValue, RegionStart, TableIsLive);
517 }
518 }
519
520 return EFI_SUCCESS;
521}
522
541 IN UINTN RegionStart,
542 IN UINTN RegionLength,
543 IN UINT64 PageWalkCfg,
544 IN UINTN AttributeSetMask,
545 IN UINTN AttributeClearMask,
546 IN UINTN *RootTable,
547 IN BOOLEAN TableIsLive
548 )
549{
550 if (((RegionStart | RegionLength) & EFI_PAGE_MASK) != 0) {
551 return EFI_INVALID_PARAMETER;
552 }
553
555 RegionStart,
556 RegionStart + RegionLength,
557 AttributeSetMask,
558 AttributeClearMask,
559 RootTable,
560 0,
561 PageWalkCfg,
562 TableIsLive
563 );
564}
565
573UINT64
574EFIAPI
576 IN UINT64 EfiAttributes
577 )
578{
579 UINT64 LoongArchAttributes;
580
581 LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
582
583 switch (EfiAttributes & EFI_CACHE_ATTRIBUTE_MASK) {
584 case EFI_MEMORY_UC:
585 LoongArchAttributes |= CACHE_SUC;
586 break;
587 case EFI_MEMORY_WC:
588 LoongArchAttributes |= CACHE_WUC;
589 break;
590 case EFI_MEMORY_WT:
591 case EFI_MEMORY_WB:
592 LoongArchAttributes |= CACHE_CC;
593 break;
594 case EFI_MEMORY_WP:
595 LoongArchAttributes &= ~PAGE_DIRTY;
596 break;
597 default:
598 break;
599 }
600
601 // Write protection attributes
602 switch (EfiAttributes & EFI_MEMORY_ACCESS_MASK) {
603 case EFI_MEMORY_RP:
604 LoongArchAttributes |= PAGE_NO_READ;
605 break;
606 case EFI_MEMORY_XP:
607 LoongArchAttributes |= PAGE_NO_EXEC;
608 break;
609 case EFI_MEMORY_RO:
610 LoongArchAttributes &= ~PAGE_DIRTY;
611 break;
612 default:
613 break;
614 }
615
616 return LoongArchAttributes;
617}
618
629 VOID
630 )
631{
632 UINTN Length;
633 UINTN TlbReEntry;
634 UINTN TlbReEntryOffset;
635 UINTN Remaining;
636
637 //
638 // Set TLB exception handler
639 //
647 TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
648 Remaining = TlbReEntryOffset % SIZE_4KB;
649 if (Remaining != 0x0) {
650 TlbReEntryOffset += (SIZE_4KB - Remaining);
651 }
652
653 TlbReEntry = PcdGet64 (PcdLoongArchExceptionVectorBaseAddress) + TlbReEntryOffset;
654 if ((TlbReEntryOffset + Length) > SIZE_64KB) {
655 return EFI_UNSUPPORTED;
656 }
657
658 //
659 // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
660 //
661 if (TlbReEntry & (SIZE_4KB - 1)) {
662 return EFI_UNSUPPORTED;
663 }
664
665 CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
667
668 //
669 // Set the address of TLB refill exception handler
670 //
671 SetTlbRebaseAddress ((UINTN)TlbReEntry);
672
673 return EFI_SUCCESS;
674}
675
693EFIAPI
695 IN OUT UINTN *PageTable OPTIONAL,
696 IN UINT64 PageWalkCfg,
697 IN EFI_PHYSICAL_ADDRESS BaseAddress,
698 IN UINT64 Length,
699 IN UINT64 Attributes,
700 IN UINT64 AttributeMask
701 )
702{
703 EFI_STATUS Status;
704 UINT64 LoongArchAttributes;
705 BOOLEAN Initialization;
706 BOOLEAN CreateNew;
707 UINTN PageTableBitWidth;
708 UINTN MaxLevel;
709 UINTN PgdSize;
710
711 if ((PageTable == NULL) || (PageWalkCfg == 0)) {
712 return EFI_INVALID_PARAMETER;
713 }
714
715 PageTableBitWidth = ParsePageTableBitWidth (PageWalkCfg);
716 MaxLevel = ParseMaxPageTableLevel (PageWalkCfg);
717
718 if ((!PageTableBitWidth && !MaxLevel) || (PageTableBitWidth > 0x1F) || (MaxLevel > LEVEL5)) {
719 return EFI_INVALID_PARAMETER;
720 }
721
722 Initialization = FALSE;
723 CreateNew = FALSE;
724
725 //
726 // *PageTable is NULL, create a new and return.
727 //
728 if (*PageTable == 0) {
729 CreateNew = TRUE;
730 //
731 // If the MMU has not been configured yet, configure it later.
732 //
733 if (!MmuIsInit ()) {
734 Initialization = TRUE;
735 }
736 }
737
738 if (Length == 0) {
739 return EFI_SUCCESS;
740 }
741
742 if (Initialization == TRUE) {
743 Status = TlbRefillHandlerConfigure ();
744 ASSERT_EFI_ERROR (Status);
745 }
746
747 if (CreateNew == TRUE) {
748 //
749 // Create a new page table.
750 //
751 PgdSize = (1 << PageTableBitWidth) * sizeof (UINTN);
752 *PageTable = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PgdSize));
753 ZeroMem ((VOID *)*PageTable, PgdSize);
754
755 if ((VOID *)*PageTable == NULL) {
756 return EFI_UNSUPPORTED;
757 }
758 }
759
760 LoongArchAttributes = EfiAttributeConverse (Attributes);
761
762 //
763 // Update the page table attributes.
764 //
765 // If the MMU has been configured and *PageTable == CSR_PGDL, the page table in use will update.
766 //
767 // If *PageTable != CSR_PGDL, only the page table structure in memory is update, but some TLB
768 // region may be invalidated during the mapping process. So at this time the caller must ensure
769 // that the execution environment must be safe. It is recommended to use the DA mode!
770 //
771 Status = UpdateRegionMapping (
772 BaseAddress,
773 Length,
774 PageWalkCfg,
775 LoongArchAttributes,
776 PTE_ATTRIBUTES_MASK,
777 (UINTN *)(*PageTable),
778 (MmuIsEnabled () && !CreateNew)
779 );
780
781 ASSERT_EFI_ERROR (Status);
782
783 return Status;
784}
UINT64 UINTN
#define MAX_ADDRESS
VOID *EFIAPI InvalidateInstructionCacheRange(IN VOID *Address, IN UINTN Length)
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
EFI_STATUS TlbRefillHandlerConfigure(VOID)
Definition: CpuMmu.c:628
STATIC BOOLEAN MmuIsInit(VOID)
Definition: CpuMmu.c:37
EFI_STATUS EFIAPI MemoryRegionMap(IN OUT UINTN *PageTable OPTIONAL, IN UINT64 PageWalkCfg, IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 Attributes, IN UINT64 AttributeMask)
Definition: CpuMmu.c:694
UINT64 EFIAPI EfiAttributeConverse(IN UINT64 EfiAttributes)
Definition: CpuMmu.c:575
STATIC EFI_STATUS UpdateRegionMappingRecursive(IN UINTN RegionStart, IN UINTN RegionEnd, IN UINTN AttributeSetMask, IN UINTN AttributeClearMask, IN UINTN *PageTable, IN UINTN Level, IN UINT64 PageWalkCfg, IN BOOLEAN TableIsLive)
Definition: CpuMmu.c:365
STATIC BOOLEAN IsBlockEntry(IN UINTN Entry, IN UINTN Level, IN UINT64 PageWalkCfg)
Definition: CpuMmu.c:199
STATIC BOOLEAN IsValidPte(IN UINTN Entry)
Definition: CpuMmu.c:80
STATIC UINTN SetPpnToPte(UINTN Entry, UINTN Address)
Definition: CpuMmu.c:297
STATIC UINTN GetPpnfromPte(IN UINTN Entry)
Definition: CpuMmu.c:279
STATIC BOOLEAN IsValidHugePage(IN UINTN Entry)
Definition: CpuMmu.c:102
STATIC UINTN SetValidPte(IN UINTN Entry)
Definition: CpuMmu.c:123
STATIC VOID FreePageTablesRecursive(IN UINTN *TranslationTable, IN UINT64 PageWalkCfg, IN UINTN Level)
Definition: CpuMmu.c:320
STATIC UINTN ParseMaxPageTableLevel(IN UINT64 PageWalkCfg)
Definition: CpuMmu.c:143
STATIC BOOLEAN IsTableEntry(IN UINTN Entry, IN UINTN Level, IN UINT64 PageWalkCfg)
Definition: CpuMmu.c:225
EFI_STATUS UpdateRegionMapping(IN UINTN RegionStart, IN UINTN RegionLength, IN UINT64 PageWalkCfg, IN UINTN AttributeSetMask, IN UINTN AttributeClearMask, IN UINTN *RootTable, IN BOOLEAN TableIsLive)
Definition: CpuMmu.c:540
STATIC VOID ReplaceTableEntry(IN UINTN *Entry, IN UINTN Value, IN UINTN RegionStart, IN BOOLEAN IsLiveBlockMapping)
Definition: CpuMmu.c:255
STATIC BOOLEAN MmuIsEnabled(VOID)
Definition: CpuMmu.c:58
STATIC UINTN ParsePageTableBitWidth(IN UINT64 PageWalkCfg)
Definition: CpuMmu.c:176
UINTN EFIAPI CsrRead(IN UINT16 Select)
Definition: Csr.c:37
#define PAGE_VALID
Definition: Csr.h:90
VOID EFIAPI FreePages(IN VOID *Buffer, IN UINTN Pages)
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define MIN(a, b)
Definition: Base.h:1007
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
#define PcdGet64(TokenName)
Definition: PcdLib.h:375
VOID *EFIAPI AllocatePages(IN UINTN Pages)
VOID HandleTlbRefillEnd(VOID)
VOID HandleTlbRefillStart(VOID)
VOID InvalidTlb(UINTN Address)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SIZE_TO_PAGES(Size)
Definition: UefiBaseType.h:200
#define EFI_SUCCESS
Definition: UefiBaseType.h:112