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
39STATIC UINTN mModeSupport[] = { SATP_MODE_SV57, SATP_MODE_SV48, SATP_MODE_SV39 };
40STATIC UINTN mMaxRootTableLevel;
41STATIC UINTN mBitPerLevel;
42STATIC UINTN mTableEntryCount;
43
52BOOLEAN
54 VOID
55 )
56{
57 return ((RiscVGetSupervisorAddressTranslationRegister () &
58 SATP64_MODE) != (SATP_MODE_OFF << SATP64_MODE_SHIFT));
59}
60
70 VOID
71 )
72{
73 return (RiscVGetSupervisorAddressTranslationRegister () & SATP64_PPN) <<
74 RISCV_MMU_PAGE_SHIFT;
75}
76
87BOOLEAN
89 IN UINTN Entry
90 )
91{
92 if (((Entry & RISCV_PG_V) == 0) ||
93 (((Entry & (RISCV_PG_R | RISCV_PG_W)) == RISCV_PG_W)))
94 {
95 return FALSE;
96 }
97
98 return TRUE;
99}
100
109STATIC
110UINTN
112 IN UINTN Entry
113 )
114{
115 /* Set Valid and Global mapping bits */
116 return Entry | RISCV_PG_G | RISCV_PG_V;
117}
118
128STATIC
129BOOLEAN
131 IN UINTN Entry
132 )
133{
134 return IsValidPte (Entry) &&
135 (Entry & (RISCV_PG_X | RISCV_PG_R));
136}
137
147STATIC
148BOOLEAN
150 IN UINTN Entry
151 )
152{
153 return IsValidPte (Entry) &&
154 !IsBlockEntry (Entry);
155}
156
165STATIC
166UINTN
168 IN UINTN Entry
169 )
170{
171 Entry = SetValidPte (Entry);
172 Entry &= ~(RISCV_PG_X | RISCV_PG_W | RISCV_PG_R);
173
174 return Entry;
175}
176
186STATIC
187VOID
189 IN UINTN *Entry,
190 IN UINTN Value,
191 IN UINTN RegionStart,
192 IN BOOLEAN IsLiveBlockMapping
193 )
194{
195 *Entry = Value;
196
197 if (IsLiveBlockMapping && RiscVMmuEnabled ()) {
198 RiscVLocalTlbFlush (RegionStart);
199 }
200}
201
210STATIC
211UINTN
213 IN UINTN Entry
214 )
215{
216 return ((Entry & PTE_PPN_MASK) >> PTE_PPN_SHIFT);
217}
218
228STATIC
229UINTN
231 UINTN Entry,
232 UINTN Address
233 )
234{
235 UINTN Ppn;
236
237 Ppn = ((Address >> RISCV_MMU_PAGE_SHIFT) << PTE_PPN_SHIFT);
238 ASSERT (~(Ppn & ~PTE_PPN_MASK));
239 Entry &= ~PTE_PPN_MASK;
240 return Entry | Ppn;
241}
242
250STATIC
251VOID
253 IN UINTN *TranslationTable,
254 IN UINTN Level
255 )
256{
257 UINTN Index;
258
259 if (Level < mMaxRootTableLevel - 1) {
260 for (Index = 0; Index < mTableEntryCount; Index++) {
261 if (IsTableEntry (TranslationTable[Index])) {
263 (UINTN *)(GetPpnfromPte ((TranslationTable[Index])) <<
264 RISCV_MMU_PAGE_SHIFT),
265 Level + 1
266 );
267 }
268 }
269 }
270
271 FreePages (TranslationTable, 1);
272}
273
289STATIC
292 IN UINTN RegionStart,
293 IN UINTN RegionEnd,
294 IN UINTN AttributeSetMask,
295 IN UINTN AttributeClearMask,
296 IN UINTN *PageTable,
297 IN UINTN Level,
298 IN BOOLEAN TableIsLive
299 )
300{
301 EFI_STATUS Status;
302 UINTN BlockShift;
303 UINTN BlockMask;
304 UINTN BlockEnd;
305 UINTN *Entry;
306 UINTN EntryValue;
307 UINTN *TranslationTable;
308 BOOLEAN NextTableIsLive;
309
310 ASSERT (Level < mMaxRootTableLevel);
311 ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);
312
313 BlockShift = (mMaxRootTableLevel - Level - 1) * mBitPerLevel + RISCV_MMU_PAGE_SHIFT;
314 BlockMask = MAX_ADDRESS >> (64 - BlockShift);
315
316 DEBUG (
317 (
318 DEBUG_VERBOSE,
319 "%a(%d): %llx - %llx set %lx clr %lx\n",
320 __func__,
321 Level,
322 RegionStart,
323 RegionEnd,
324 AttributeSetMask,
325 AttributeClearMask
326 )
327 );
328
329 for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) {
330 BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);
331 Entry = &PageTable[(RegionStart >> BlockShift) & (mTableEntryCount - 1)];
332
333 //
334 // If RegionStart or BlockEnd is not aligned to the block size at this
335 // level, we will have to create a table mapping in order to map less
336 // than a block, and recurse to create the block or page entries at
337 // the next level. No block mappings are allowed at all at level 0,
338 // so in that case, we have to recurse unconditionally.
339 //
340 if ((Level == 0) ||
341 (((RegionStart | BlockEnd) & BlockMask) != 0) || IsTableEntry (*Entry))
342 {
343 ASSERT (Level < mMaxRootTableLevel - 1);
344 if (!IsTableEntry (*Entry)) {
345 //
346 // No table entry exists yet, so we need to allocate a page table
347 // for the next level.
348 //
349 TranslationTable = AllocatePages (1);
350 if (TranslationTable == NULL) {
351 return EFI_OUT_OF_RESOURCES;
352 }
353
354 ZeroMem (TranslationTable, EFI_PAGE_SIZE);
355
356 if (IsBlockEntry (*Entry)) {
357 //
358 // We are splitting an existing block entry, so we have to populate
359 // the new table with the attributes of the block entry it replaces.
360 //
362 RegionStart & ~BlockMask,
363 (RegionStart | BlockMask) + 1,
364 *Entry & PTE_ATTRIBUTES_MASK,
365 PTE_ATTRIBUTES_MASK,
366 TranslationTable,
367 Level + 1,
368 FALSE
369 );
370 if (EFI_ERROR (Status)) {
371 //
372 // The range we passed to UpdateRegionMappingRecursive () is block
373 // aligned, so it is guaranteed that no further pages were allocated
374 // by it, and so we only have to free the page we allocated here.
375 //
376 FreePages (TranslationTable, 1);
377 return Status;
378 }
379 }
380
381 NextTableIsLive = FALSE;
382 } else {
383 TranslationTable = (UINTN *)(GetPpnfromPte (*Entry) << RISCV_MMU_PAGE_SHIFT);
384 NextTableIsLive = TableIsLive;
385 }
386
387 //
388 // Recurse to the next level
389 //
391 RegionStart,
392 BlockEnd,
393 AttributeSetMask,
394 AttributeClearMask,
395 TranslationTable,
396 Level + 1,
397 NextTableIsLive
398 );
399 if (EFI_ERROR (Status)) {
400 if (!IsTableEntry (*Entry)) {
401 //
402 // We are creating a new table entry, so on failure, we can free all
403 // allocations we made recursively, given that the whole subhierarchy
404 // has not been wired into the live page tables yet. (This is not
405 // possible for existing table entries, since we cannot revert the
406 // modifications we made to the subhierarchy it represents.)
407 //
408 FreePageTablesRecursive (TranslationTable, Level + 1);
409 }
410
411 return Status;
412 }
413
414 if (!IsTableEntry (*Entry)) {
415 EntryValue = SetPpnToPte (0, (UINTN)TranslationTable);
416 EntryValue = SetTableEntry (EntryValue);
418 Entry,
419 EntryValue,
420 RegionStart,
421 TableIsLive
422 );
423 }
424 } else {
425 EntryValue = (*Entry & ~AttributeClearMask) | AttributeSetMask;
426 //
427 // We don't have page fault exception handler when a virtual page is accessed and
428 // the A bit is clear, or is written and the D bit is clear.
429 // So just set A for read and D for write permission.
430 //
431 if ((AttributeSetMask & RISCV_PG_R) != 0) {
432 EntryValue |= RISCV_PG_A;
433 }
434
435 if ((AttributeSetMask & RISCV_PG_W) != 0) {
436 EntryValue |= RISCV_PG_D;
437 }
438
439 EntryValue = SetPpnToPte (EntryValue, RegionStart);
440 EntryValue = SetValidPte (EntryValue);
441 ReplaceTableEntry (Entry, EntryValue, RegionStart, TableIsLive);
442 }
443 }
444
445 return EFI_SUCCESS;
446}
447
463STATIC
466 IN UINTN RegionStart,
467 IN UINTN RegionLength,
468 IN UINTN AttributeSetMask,
469 IN UINTN AttributeClearMask,
470 IN UINTN *RootTable,
471 IN BOOLEAN TableIsLive
472 )
473{
474 if (((RegionStart | RegionLength) & EFI_PAGE_MASK) != 0) {
475 return EFI_INVALID_PARAMETER;
476 }
477
479 RegionStart,
480 RegionStart + RegionLength,
481 AttributeSetMask,
482 AttributeClearMask,
483 RootTable,
484 0,
485 TableIsLive
486 );
487}
488
497STATIC
498UINTN
500 IN UINTN GcdAttributes
501 )
502{
503 UINTN RiscVAttributes;
504
505 RiscVAttributes = RISCV_PG_R | RISCV_PG_W | RISCV_PG_X;
506
507 // Determine protection attributes
508 if ((GcdAttributes & EFI_MEMORY_RO) != 0) {
509 RiscVAttributes &= ~(RISCV_PG_W);
510 }
511
512 // Process eXecute Never attribute
513 if ((GcdAttributes & EFI_MEMORY_XP) != 0) {
514 RiscVAttributes &= ~RISCV_PG_X;
515 }
516
517 return RiscVAttributes;
518}
519
533EFIAPI
535 IN EFI_PHYSICAL_ADDRESS BaseAddress,
536 IN UINTN Length,
537 IN UINTN Attributes
538 )
539{
540 UINTN PageAttributesSet;
541
542 PageAttributesSet = GcdAttributeToPageAttribute (Attributes);
543
544 if (!RiscVMmuEnabled ()) {
545 return EFI_SUCCESS;
546 }
547
548 DEBUG (
549 (
550 DEBUG_VERBOSE,
551 "%a: Set %llX page attribute 0x%X\n",
552 __func__,
553 BaseAddress,
554 PageAttributesSet
555 )
556 );
557
558 return UpdateRegionMapping (
559 BaseAddress,
560 Length,
561 PageAttributesSet,
562 PTE_ATTRIBUTES_MASK,
564 TRUE
565 );
566}
567
579STATIC
582 UINTN SatpMode
583 )
584{
585 VOID *TranslationTable;
586 UINTN SatpReg;
587 UINTN Ppn;
589 UINTN NumberOfDescriptors;
590 UINTN Index;
591 EFI_STATUS Status;
592
593 switch (SatpMode) {
594 case SATP_MODE_OFF:
595 return EFI_SUCCESS;
596 case SATP_MODE_SV39:
597 mMaxRootTableLevel = 3;
598 mBitPerLevel = 9;
599 mTableEntryCount = 512;
600 break;
601 case SATP_MODE_SV48:
602 mMaxRootTableLevel = 4;
603 mBitPerLevel = 9;
604 mTableEntryCount = 512;
605 break;
606 case SATP_MODE_SV57:
607 mMaxRootTableLevel = 5;
608 mBitPerLevel = 9;
609 mTableEntryCount = 512;
610 break;
611 default:
612 return EFI_INVALID_PARAMETER;
613 }
614
615 // Allocate pages for translation table
616 TranslationTable = AllocatePages (1);
617 if (TranslationTable == NULL) {
618 return EFI_OUT_OF_RESOURCES;
619 }
620
621 ZeroMem (TranslationTable, mTableEntryCount * sizeof (UINTN));
622
623 NumberOfDescriptors = 0;
624 MemoryMap = NULL;
625 Status = gDS->GetMemorySpaceMap (
626 &NumberOfDescriptors,
627 &MemoryMap
628 );
629 ASSERT_EFI_ERROR (Status);
630
631 for (Index = 0; Index < NumberOfDescriptors; Index++) {
632 if (MemoryMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
633 // Default Read/Write attribute for memory mapped IO
635 MemoryMap[Index].BaseAddress,
636 MemoryMap[Index].Length,
637 RISCV_PG_R | RISCV_PG_W,
638 PTE_ATTRIBUTES_MASK,
639 TranslationTable,
640 FALSE
641 );
642 } else if (MemoryMap[Index].GcdMemoryType == EfiGcdMemoryTypeSystemMemory) {
643 // Default Read/Write/Execute attribute for system memory
645 MemoryMap[Index].BaseAddress,
646 MemoryMap[Index].Length,
647 RISCV_PG_R | RISCV_PG_W | RISCV_PG_X,
648 PTE_ATTRIBUTES_MASK,
649 TranslationTable,
650 FALSE
651 );
652 }
653 }
654
655 FreePool ((VOID *)MemoryMap);
656
657 if (GetInterruptState ()) {
659 }
660
661 Ppn = (UINTN)TranslationTable >> RISCV_MMU_PAGE_SHIFT;
662 ASSERT (!(Ppn & ~(SATP64_PPN)));
663
664 SatpReg = Ppn;
665 SatpReg |= (SatpMode <<
666 SATP64_MODE_SHIFT) & SATP64_MODE;
667 RiscVSetSupervisorAddressTranslationRegister (SatpReg);
668 /* Check if HW support the setup satp mode */
669 if (SatpReg != RiscVGetSupervisorAddressTranslationRegister ()) {
670 DEBUG (
671 (
672 DEBUG_VERBOSE,
673 "%a: HW does not support SATP mode:%d\n",
674 __func__,
675 SatpMode
676 )
677 );
678 FreePageTablesRecursive (TranslationTable, 0);
679 return EFI_DEVICE_ERROR;
680 }
681
683
684 if (GetInterruptState ()) {
686 }
687
688 return Status;
689}
690
699EFIAPI
701 VOID
702 )
703{
704 EFI_STATUS Status;
705 INTN Idx;
706
707 Status = EFI_SUCCESS;
708
709 /* Try to setup MMU with highest mode as possible */
710 for (Idx = 0; Idx < ARRAY_SIZE (mModeSupport); Idx++) {
711 Status = RiscVMmuSetSatpMode (mModeSupport[Idx]);
712 if (Status == EFI_DEVICE_ERROR) {
713 continue;
714 } else if (EFI_ERROR (Status)) {
715 return Status;
716 }
717
718 DEBUG (
719 (
720 DEBUG_INFO,
721 "%a: SATP mode %d successfully configured\n",
722 __func__,
723 mModeSupport[Idx]
724 )
725 );
726 break;
727 }
728
729 return Status;
730}
UINT64 UINTN
INT64 INTN
#define MAX_ADDRESS
#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
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 EFI_STATUS UpdateRegionMapping(IN UINTN RegionStart, IN UINTN RegionLength, IN UINTN AttributeSetMask, IN UINTN AttributeClearMask, IN UINTN *RootTable, IN BOOLEAN TableIsLive)
STATIC BOOLEAN RiscVMmuEnabled(VOID)
STATIC BOOLEAN IsTableEntry(IN UINTN Entry)
STATIC UINTN SetTableEntry(IN UINTN Entry)
STATIC BOOLEAN IsValidPte(IN UINTN Entry)
STATIC UINTN SetPpnToPte(UINTN Entry, UINTN Address)
STATIC UINTN GetPpnfromPte(IN UINTN Entry)
STATIC UINTN GcdAttributeToPageAttribute(IN UINTN GcdAttributes)
STATIC UINTN RiscVGetRootTranslateTable(VOID)
STATIC UINTN SetValidPte(IN UINTN Entry)
STATIC VOID FreePageTablesRecursive(IN UINTN *TranslationTable, IN UINTN Level)
STATIC EFI_STATUS RiscVMmuSetSatpMode(UINTN SatpMode)
STATIC VOID ReplaceTableEntry(IN UINTN *Entry, IN UINTN Value, IN UINTN RegionStart, IN BOOLEAN IsLiveBlockMapping)
STATIC EFI_STATUS UpdateRegionMappingRecursive(IN UINTN RegionStart, IN UINTN RegionEnd, IN UINTN AttributeSetMask, IN UINTN AttributeClearMask, IN UINTN *PageTable, IN UINTN Level, IN BOOLEAN TableIsLive)
STATIC BOOLEAN IsBlockEntry(IN UINTN Entry)
EFI_STATUS EFIAPI RiscVSetMemoryAttributes(IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINTN Length, IN UINTN Attributes)
EFI_STATUS EFIAPI RiscVConfigureMmu(VOID)
VOID EFIAPI RiscVLocalTlbFlush(UINTN VirtAddr)
VOID EFIAPI RiscVLocalTlbFlushAll(VOID)
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:445
#define DEBUG(Expression)
Definition: DebugLib.h:422
EFI_DXE_SERVICES * gDS
VOID EFIAPI FreePages(IN VOID *Buffer, IN UINTN Pages)
VOID EFIAPI FreePool(IN VOID *Buffer)
@ 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