TianoCore EDK2 master
Loading...
Searching...
No Matches
BaseRiscVMmuLib.c
Go to the documentation of this file.
1
13#include <PiDxe.h>
14#include <Uefi.h>
15#include <Library/BaseLib.h>
19#include <Library/DebugLib.h>
23#include <Library/PcdLib.h>
25
26#define RISCV_PG_V BIT0
27#define RISCV_PG_R BIT1
28#define RISCV_PG_W BIT2
29#define RISCV_PG_X BIT3
30#define RISCV_PG_G BIT5
31#define RISCV_PG_A BIT6
32#define RISCV_PG_D BIT7
33#define PTE_ATTRIBUTES_MASK 0xE
34
35#define PTE_PPN_MASK 0x3FFFFFFFFFFC00ULL
36#define PTE_PPN_SHIFT 10
37#define RISCV_MMU_PAGE_SHIFT 12
38
39#define RISCV_CPU_FEATURE_PBMT_BITMASK BIT2
40#define PTE_PBMT_NC BIT61
41#define PTE_PBMT_IO BIT62
42#define PTE_PBMT_MASK (PTE_PBMT_NC | PTE_PBMT_IO)
43
44STATIC UINTN mModeSupport[] = { SATP_MODE_SV57, SATP_MODE_SV48, SATP_MODE_SV39, SATP_MODE_OFF };
45STATIC UINTN mMaxRootTableLevel;
46STATIC UINTN mBitPerLevel;
47STATIC UINTN mTableEntryCount;
48
57BOOLEAN
59 VOID
60 )
61{
62 return ((RiscVGetSupervisorAddressTranslationRegister () &
63 SATP64_MODE) != (SATP_MODE_OFF << SATP64_MODE_SHIFT));
64}
65
73UINT64
75 VOID
76 )
77{
78 return (RiscVGetSupervisorAddressTranslationRegister () & SATP64_PPN) <<
79 RISCV_MMU_PAGE_SHIFT;
80}
81
92BOOLEAN
94 IN UINT64 Entry
95 )
96{
97 if (((Entry & RISCV_PG_V) == 0) ||
98 (((Entry & (RISCV_PG_R | RISCV_PG_W)) == RISCV_PG_W)))
99 {
100 return FALSE;
101 }
102
103 return TRUE;
104}
105
114STATIC
115UINT64
117 IN UINT64 Entry
118 )
119{
120 /* Set Valid and Global mapping bits */
121 return Entry | RISCV_PG_G | RISCV_PG_V;
122}
123
133STATIC
134BOOLEAN
136 IN UINT64 Entry
137 )
138{
139 return IsValidPte (Entry) &&
140 (Entry & (RISCV_PG_X | RISCV_PG_R));
141}
142
152STATIC
153BOOLEAN
155 IN UINT64 Entry
156 )
157{
158 return IsValidPte (Entry) &&
159 !IsBlockEntry (Entry);
160}
161
170STATIC
171UINT64
173 IN UINT64 Entry
174 )
175{
176 Entry = SetValidPte (Entry);
177 Entry &= ~(UINT64)(RISCV_PG_X | RISCV_PG_W | RISCV_PG_R);
178
179 return Entry;
180}
181
191STATIC
192VOID
194 IN UINT64 *Entry,
195 IN UINT64 Value,
196 IN UINT64 RegionStart,
197 IN BOOLEAN IsLiveBlockMapping
198 )
199{
200 *Entry = Value;
201
202 if (IsLiveBlockMapping && RiscVMmuEnabled ()) {
203 RiscVLocalTlbFlush (RegionStart);
204 }
205}
206
215STATIC
216UINT64
218 IN UINT64 Entry
219 )
220{
221 return ((Entry & PTE_PPN_MASK) >> PTE_PPN_SHIFT);
222}
223
233STATIC
234UINT64
236 UINT64 Entry,
237 UINT64 Address
238 )
239{
240 UINT64 Ppn;
241
242 Ppn = ((Address >> RISCV_MMU_PAGE_SHIFT) << PTE_PPN_SHIFT);
243 ASSERT (~(Ppn & ~PTE_PPN_MASK));
244 Entry &= ~PTE_PPN_MASK;
245 return Entry | Ppn;
246}
247
255STATIC
256VOID
258 IN UINT64 *TranslationTable,
259 IN UINTN Level
260 )
261{
262 UINTN Index;
263
264 if (Level < mMaxRootTableLevel - 1) {
265 for (Index = 0; Index < mTableEntryCount; Index++) {
266 if (IsTableEntry (TranslationTable[Index])) {
268 (UINT64 *)(GetPpnfromPte ((TranslationTable[Index])) <<
269 RISCV_MMU_PAGE_SHIFT),
270 Level + 1
271 );
272 }
273 }
274 }
275
276 FreePages (TranslationTable, 1);
277}
278
294STATIC
297 IN UINT64 RegionStart,
298 IN UINT64 RegionEnd,
299 IN UINT64 AttributeSetMask,
300 IN UINT64 AttributeClearMask,
301 IN UINT64 *PageTable,
302 IN UINTN Level,
303 IN BOOLEAN TableIsLive
304 )
305{
306 EFI_STATUS Status;
307 UINT64 BlockShift;
308 UINT64 BlockMask;
309 UINT64 BlockEnd;
310 UINT64 *Entry;
311 UINT64 EntryValue;
312 UINT64 *TranslationTable;
313 BOOLEAN NextTableIsLive;
314
315 ASSERT (Level < mMaxRootTableLevel);
316 ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);
317
318 BlockShift = (mMaxRootTableLevel - Level - 1) * mBitPerLevel + RISCV_MMU_PAGE_SHIFT;
319 BlockMask = MAX_ADDRESS >> (64 - BlockShift);
320
321 DEBUG ((
322 DEBUG_VERBOSE,
323 "%a(%d): %LX - %LX set %LX clr %LX\n",
324 __func__,
325 Level,
326 RegionStart,
327 RegionEnd,
328 AttributeSetMask,
329 AttributeClearMask
330 ));
331
332 for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) {
333 BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);
334 Entry = &PageTable[(RegionStart >> BlockShift) & (mTableEntryCount - 1)];
335
336 //
337 // If RegionStart or BlockEnd is not aligned to the block size at this
338 // level, we will have to create a table mapping in order to map less
339 // than a block, and recurse to create the block or page entries at
340 // the next level. No block mappings are allowed at all at level 0,
341 // so in that case, we have to recurse unconditionally.
342 //
343 if ((Level == 0) ||
344 (((RegionStart | BlockEnd) & BlockMask) != 0) || IsTableEntry (*Entry))
345 {
346 ASSERT (Level < mMaxRootTableLevel - 1);
347 if (!IsTableEntry (*Entry)) {
348 //
349 // No table entry exists yet, so we need to allocate a page table
350 // for the next level.
351 //
352 TranslationTable = AllocatePages (1);
353 if (TranslationTable == NULL) {
354 return EFI_OUT_OF_RESOURCES;
355 }
356
357 ZeroMem (TranslationTable, EFI_PAGE_SIZE);
358
359 if (IsBlockEntry (*Entry)) {
360 //
361 // We are splitting an existing block entry, so we have to populate
362 // the new table with the attributes of the block entry it replaces.
363 //
365 RegionStart & ~BlockMask,
366 (RegionStart | BlockMask) + 1,
367 *Entry & PTE_ATTRIBUTES_MASK,
368 PTE_ATTRIBUTES_MASK,
369 TranslationTable,
370 Level + 1,
371 FALSE
372 );
373 if (EFI_ERROR (Status)) {
374 //
375 // The range we passed to UpdateRegionMappingRecursive () is block
376 // aligned, so it is guaranteed that no further pages were allocated
377 // by it, and so we only have to free the page we allocated here.
378 //
379 FreePages (TranslationTable, 1);
380 return Status;
381 }
382 }
383
384 NextTableIsLive = FALSE;
385 } else {
386 TranslationTable = (UINT64 *)(GetPpnfromPte (*Entry) << RISCV_MMU_PAGE_SHIFT);
387 NextTableIsLive = TableIsLive;
388 }
389
390 //
391 // Recurse to the next level
392 //
394 RegionStart,
395 BlockEnd,
396 AttributeSetMask,
397 AttributeClearMask,
398 TranslationTable,
399 Level + 1,
400 NextTableIsLive
401 );
402 if (EFI_ERROR (Status)) {
403 if (!IsTableEntry (*Entry)) {
404 //
405 // We are creating a new table entry, so on failure, we can free all
406 // allocations we made recursively, given that the whole subhierarchy
407 // has not been wired into the live page tables yet. (This is not
408 // possible for existing table entries, since we cannot revert the
409 // modifications we made to the subhierarchy it represents.)
410 //
411 FreePageTablesRecursive (TranslationTable, Level + 1);
412 }
413
414 return Status;
415 }
416
417 if (!IsTableEntry (*Entry)) {
418 EntryValue = SetPpnToPte (0, (UINT64)TranslationTable);
419 EntryValue = SetTableEntry (EntryValue);
421 Entry,
422 EntryValue,
423 RegionStart,
424 TableIsLive
425 );
426 }
427 } else {
428 EntryValue = (*Entry & ~AttributeClearMask) | AttributeSetMask;
429 //
430 // We don't have page fault exception handler when a virtual page is accessed and
431 // the A bit is clear, or is written and the D bit is clear.
432 // So just set A for read and D for write permission.
433 //
434 if ((AttributeSetMask & RISCV_PG_R) != 0) {
435 EntryValue |= RISCV_PG_A;
436 }
437
438 if ((AttributeSetMask & RISCV_PG_W) != 0) {
439 EntryValue |= RISCV_PG_D;
440 }
441
442 EntryValue = SetPpnToPte (EntryValue, RegionStart);
443 EntryValue = SetValidPte (EntryValue);
444 ReplaceTableEntry (Entry, EntryValue, RegionStart, TableIsLive);
445 }
446 }
447
448 return EFI_SUCCESS;
449}
450
466STATIC
469 IN UINT64 RegionStart,
470 IN UINT64 RegionLength,
471 IN UINT64 AttributeSetMask,
472 IN UINT64 AttributeClearMask,
473 IN UINT64 *RootTable,
474 IN BOOLEAN TableIsLive
475 )
476{
477 if (((RegionStart | RegionLength) & EFI_PAGE_MASK) != 0) {
478 return EFI_INVALID_PARAMETER;
479 }
480
482 RegionStart,
483 RegionStart + RegionLength,
484 AttributeSetMask,
485 AttributeClearMask,
486 RootTable,
487 0,
488 TableIsLive
489 );
490}
491
502STATIC
505 IN UINT64 GcdAttributes,
506 OUT UINT64 *RiscVAttributes
507 )
508{
509 UINT64 CacheTypeMask;
510 BOOLEAN PmbtExtEnabled;
511
512 if (RiscVAttributes == NULL) {
513 return EFI_INVALID_PARAMETER;
514 }
515
516 *RiscVAttributes = RISCV_PG_R | RISCV_PG_W | RISCV_PG_X;
517
518 PmbtExtEnabled = FALSE;
519 if ((PcdGet64 (PcdRiscVFeatureOverride) & RISCV_CPU_FEATURE_PBMT_BITMASK) != 0) {
520 PmbtExtEnabled = TRUE;
521 }
522
523 // Determine protection attributes
524 if ((GcdAttributes & EFI_MEMORY_RO) != 0) {
525 *RiscVAttributes &= ~(UINT64)(RISCV_PG_W);
526 }
527
528 // Process eXecute Never attribute
529 if ((GcdAttributes & EFI_MEMORY_XP) != 0) {
530 *RiscVAttributes &= ~(UINT64)RISCV_PG_X;
531 }
532
533 CacheTypeMask = GcdAttributes & EFI_CACHE_ATTRIBUTE_MASK;
534 if ((CacheTypeMask != 0) &&
535 (((CacheTypeMask - 1) & CacheTypeMask) != 0))
536 {
537 DEBUG ((
538 DEBUG_ERROR,
539 "%a: More than one bit set in cache type mask (0x%LX)\n",
540 __func__,
541 CacheTypeMask
542 ));
543 return EFI_INVALID_PARAMETER;
544 }
545
546 switch (CacheTypeMask) {
547 case EFI_MEMORY_UC:
548 if (PmbtExtEnabled) {
549 *RiscVAttributes |= PTE_PBMT_IO;
550 }
551
552 break;
553 case EFI_MEMORY_WC:
554 if (PmbtExtEnabled) {
555 *RiscVAttributes |= PTE_PBMT_NC;
556 } else {
557 DEBUG ((
558 DEBUG_VERBOSE,
559 "%a: EFI_MEMORY_WC set but Pmbt extension not available\n",
560 __func__
561 ));
562 }
563
564 break;
565 default:
566 // Default PMA mode
567 break;
568 }
569
570 return EFI_SUCCESS;
571}
572
586EFIAPI
588 IN EFI_PHYSICAL_ADDRESS BaseAddress,
589 IN UINT64 Length,
590 IN UINT64 Attributes
591 )
592{
593 UINT64 PageAttributesSet;
594 UINT64 PageAttributesClear;
595 EFI_STATUS Status;
596
597 Status = GcdAttributeToPageAttribute (Attributes, &PageAttributesSet);
598 if (EFI_ERROR (Status)) {
599 return Status;
600 }
601
602 if (!RiscVMmuEnabled ()) {
603 return EFI_SUCCESS;
604 }
605
606 PageAttributesClear = PTE_ATTRIBUTES_MASK;
607 if ((PcdGet64 (PcdRiscVFeatureOverride) & RISCV_CPU_FEATURE_PBMT_BITMASK) != 0) {
608 PageAttributesClear |= PTE_PBMT_MASK;
609 }
610
611 DEBUG ((
612 DEBUG_VERBOSE,
613 "%a: %LX: set attributes 0x%LX, clear attributes 0x%LX\n",
614 __func__,
615 BaseAddress,
616 PageAttributesSet,
617 PageAttributesClear
618 ));
619
620 return UpdateRegionMapping (
621 BaseAddress,
622 Length,
623 PageAttributesSet,
624 PageAttributesClear,
625 (UINT64 *)RiscVGetRootTranslateTable (),
626 TRUE
627 );
628}
629
641STATIC
644 UINTN SatpMode
645 )
646{
647 VOID *TranslationTable;
648 UINT64 SatpReg;
649 UINT64 Ppn;
651 UINTN NumberOfDescriptors;
652 UINTN Index;
653 EFI_STATUS Status;
654
655 if (SatpMode > PcdGet32 (PcdCpuRiscVMmuMaxSatpMode)) {
656 return EFI_DEVICE_ERROR;
657 }
658
659 switch (SatpMode) {
660 case SATP_MODE_OFF:
661 return EFI_SUCCESS;
662 case SATP_MODE_SV39:
663 mMaxRootTableLevel = 3;
664 mBitPerLevel = 9;
665 mTableEntryCount = 512;
666 break;
667 case SATP_MODE_SV48:
668 mMaxRootTableLevel = 4;
669 mBitPerLevel = 9;
670 mTableEntryCount = 512;
671 break;
672 case SATP_MODE_SV57:
673 mMaxRootTableLevel = 5;
674 mBitPerLevel = 9;
675 mTableEntryCount = 512;
676 break;
677 default:
678 return EFI_INVALID_PARAMETER;
679 }
680
681 // Allocate pages for translation table
682 TranslationTable = AllocatePages (1);
683 if (TranslationTable == NULL) {
684 return EFI_OUT_OF_RESOURCES;
685 }
686
687 ZeroMem (TranslationTable, mTableEntryCount * sizeof (UINT64));
688
689 NumberOfDescriptors = 0;
690 MemoryMap = NULL;
691 Status = gDS->GetMemorySpaceMap (
692 &NumberOfDescriptors,
693 &MemoryMap
694 );
695 ASSERT_EFI_ERROR (Status);
696
697 for (Index = 0; Index < NumberOfDescriptors; Index++) {
698 if (MemoryMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
699 // Default Read/Write attribute for memory mapped IO
701 MemoryMap[Index].BaseAddress,
702 MemoryMap[Index].Length,
703 RISCV_PG_R | RISCV_PG_W,
704 PTE_ATTRIBUTES_MASK,
705 TranslationTable,
706 FALSE
707 );
708 } else if (MemoryMap[Index].GcdMemoryType == EfiGcdMemoryTypeSystemMemory) {
709 // Default Read/Write/Execute attribute for system memory
711 MemoryMap[Index].BaseAddress,
712 MemoryMap[Index].Length,
713 RISCV_PG_R | RISCV_PG_W | RISCV_PG_X,
714 PTE_ATTRIBUTES_MASK,
715 TranslationTable,
716 FALSE
717 );
718 }
719 }
720
721 FreePool ((VOID *)MemoryMap);
722
723 if (GetInterruptState ()) {
725 }
726
727 Ppn = (UINT64)TranslationTable >> RISCV_MMU_PAGE_SHIFT;
728 ASSERT (!(Ppn & ~(SATP64_PPN)));
729
730 SatpReg = Ppn;
731 SatpReg |= (SatpMode <<
732 SATP64_MODE_SHIFT) & SATP64_MODE;
733 RiscVSetSupervisorAddressTranslationRegister (SatpReg);
734 /* Check if HW support the setup satp mode */
735 if (SatpReg != RiscVGetSupervisorAddressTranslationRegister ()) {
736 DEBUG ((
737 DEBUG_VERBOSE,
738 "%a: HW does not support SATP mode:%d\n",
739 __func__,
740 SatpMode
741 ));
742 FreePageTablesRecursive (TranslationTable, 0);
743 return EFI_DEVICE_ERROR;
744 }
745
747
748 if (GetInterruptState ()) {
750 }
751
752 return Status;
753}
754
763EFIAPI
765 VOID
766 )
767{
768 EFI_STATUS Status;
769 UINTN Idx;
770
771 Status = EFI_SUCCESS;
772
773 /* Try to setup MMU with highest mode as possible */
774 for (Idx = 0; Idx < ARRAY_SIZE (mModeSupport); Idx++) {
775 Status = RiscVMmuSetSatpMode (mModeSupport[Idx]);
776 if (Status == EFI_DEVICE_ERROR) {
777 continue;
778 } else if (EFI_ERROR (Status)) {
779 return Status;
780 }
781
782 DEBUG ((
783 DEBUG_INFO,
784 "%a: SATP mode %d successfully configured\n",
785 __func__,
786 mModeSupport[Idx]
787 ));
788 break;
789 }
790
791 return Status;
792}
UINT64 UINTN
#define MAX_ADDRESS
BOOLEAN EFIAPI GetInterruptState(VOID)
Definition: CpuBreakpoint.c:86
VOID EFIAPI EnableInterrupts(VOID)
Definition: CpuBreakpoint.c:67
VOID EFIAPI DisableInterrupts(VOID)
Definition: CpuBreakpoint.c:54
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
STATIC BOOLEAN RiscVMmuEnabled(VOID)
STATIC UINT64 SetValidPte(IN UINT64 Entry)
EFI_STATUS EFIAPI RiscVSetMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 Attributes)
STATIC BOOLEAN IsValidPte(IN UINT64 Entry)
STATIC UINT64 GetPpnfromPte(IN UINT64 Entry)
STATIC VOID FreePageTablesRecursive(IN UINT64 *TranslationTable, IN UINTN Level)
STATIC EFI_STATUS UpdateRegionMapping(IN UINT64 RegionStart, IN UINT64 RegionLength, IN UINT64 AttributeSetMask, IN UINT64 AttributeClearMask, IN UINT64 *RootTable, IN BOOLEAN TableIsLive)
STATIC VOID ReplaceTableEntry(IN UINT64 *Entry, IN UINT64 Value, IN UINT64 RegionStart, IN BOOLEAN IsLiveBlockMapping)
STATIC UINT64 SetPpnToPte(UINT64 Entry, UINT64 Address)
STATIC EFI_STATUS GcdAttributeToPageAttribute(IN UINT64 GcdAttributes, OUT UINT64 *RiscVAttributes)
STATIC BOOLEAN IsTableEntry(IN UINT64 Entry)
STATIC EFI_STATUS UpdateRegionMappingRecursive(IN UINT64 RegionStart, IN UINT64 RegionEnd, IN UINT64 AttributeSetMask, IN UINT64 AttributeClearMask, IN UINT64 *PageTable, IN UINTN Level, IN BOOLEAN TableIsLive)
STATIC EFI_STATUS RiscVMmuSetSatpMode(UINTN SatpMode)
STATIC BOOLEAN IsBlockEntry(IN UINT64 Entry)
STATIC UINT64 SetTableEntry(IN UINT64 Entry)
STATIC UINT64 RiscVGetRootTranslateTable(VOID)
EFI_STATUS EFIAPI RiscVConfigureMmu(VOID)
VOID EFIAPI RiscVLocalTlbFlush(UINTN VirtAddr)
VOID EFIAPI RiscVLocalTlbFlushAll(VOID)
EFI_DXE_SERVICES * gDS
VOID EFIAPI FreePages(IN VOID *Buffer, IN UINTN Pages)
VOID EFIAPI FreePool(IN VOID *Buffer)
#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 ARRAY_SIZE(Array)
Definition: Base.h:1393
#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
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
@ EfiGcdMemoryTypeSystemMemory
Definition: PiDxeCis.h:38
@ EfiGcdMemoryTypeMemoryMappedIo
Definition: PiDxeCis.h:44
VOID *EFIAPI AllocatePages(IN UINTN Pages)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112