VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c@ 101291

Last change on this file since 101291 was 101291, checked in by vboxsync, 14 months ago

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

  • Property svn:eol-style set to native
File size: 17.5 KB
Line 
1/** @file
2 MMU library for RISC-V.
3
4 Copyright (c) 2011-2020, ARM Limited. All rights reserved.
5 Copyright (c) 2016, Linaro Limited. All rights reserved.
6 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
7 Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.<BR>
8
9 SPDX-License-Identifier: BSD-2-Clause-Patent
10
11**/
12
13#include <PiDxe.h>
14#include <Uefi.h>
15#include <Library/BaseLib.h>
16#include <Library/BaseMemoryLib.h>
17#include <Library/BaseRiscVMmuLib.h>
18#include <Library/CacheMaintenanceLib.h>
19#include <Library/DebugLib.h>
20#include <Library/DxeServicesTableLib.h>
21#include <Library/UefiBootServicesTableLib.h>
22#include <Library/MemoryAllocationLib.h>
23#include <Library/PcdLib.h>
24#include <Register/RiscV64/RiscVEncoding.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
44/**
45 Determine if the MMU enabled or not.
46
47 @retval TRUE The MMU already enabled.
48 @retval FALSE The MMU not enabled.
49
50**/
51STATIC
52BOOLEAN
53RiscVMmuEnabled (
54 VOID
55 )
56{
57 return ((RiscVGetSupervisorAddressTranslationRegister () &
58 SATP64_MODE) != (SATP_MODE_OFF << SATP64_MODE_SHIFT));
59}
60
61/**
62 Retrieve the root translate table.
63
64 @return The root translate table.
65
66**/
67STATIC
68UINTN
69RiscVGetRootTranslateTable (
70 VOID
71 )
72{
73 return (RiscVGetSupervisorAddressTranslationRegister () & SATP64_PPN) <<
74 RISCV_MMU_PAGE_SHIFT;
75}
76
77/**
78 Determine if an entry is valid pte.
79
80 @param Entry The entry value.
81
82 @retval TRUE The entry is a valid pte.
83 @retval FALSE The entry is not a valid pte.
84
85**/
86STATIC
87BOOLEAN
88IsValidPte (
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
101/**
102 Set an entry to be a valid pte.
103
104 @param Entry The entry value.
105
106 @return The entry value.
107
108**/
109STATIC
110UINTN
111SetValidPte (
112 IN UINTN Entry
113 )
114{
115 /* Set Valid and Global mapping bits */
116 return Entry | RISCV_PG_G | RISCV_PG_V;
117}
118
119/**
120 Determine if an entry is a block pte.
121
122 @param Entry The entry value.
123
124 @retval TRUE The entry is a block pte.
125 @retval FALSE The entry is not a block pte.
126
127**/
128STATIC
129BOOLEAN
130IsBlockEntry (
131 IN UINTN Entry
132 )
133{
134 return IsValidPte (Entry) &&
135 (Entry & (RISCV_PG_X | RISCV_PG_R));
136}
137
138/**
139 Determine if an entry is a table pte.
140
141 @param Entry The entry value.
142
143 @retval TRUE The entry is a table pte.
144 @retval FALSE The entry is not a table pte.
145
146**/
147STATIC
148BOOLEAN
149IsTableEntry (
150 IN UINTN Entry
151 )
152{
153 return IsValidPte (Entry) &&
154 !IsBlockEntry (Entry);
155}
156
157/**
158 Set an entry to be a table pte.
159
160 @param Entry The entry value.
161
162 @return The entry value.
163
164**/
165STATIC
166UINTN
167SetTableEntry (
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
177/**
178 Replace an existing entry with new value.
179
180 @param Entry The entry pointer.
181 @param Value The new entry value.
182 @param RegionStart The start of region that new value affects.
183 @param IsLiveBlockMapping TRUE if this is live update, FALSE otherwise.
184
185**/
186STATIC
187VOID
188ReplaceTableEntry (
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
202/**
203 Get an ppn value from an entry.
204
205 @param Entry The entry value.
206
207 @return The ppn value.
208
209**/
210STATIC
211UINTN
212GetPpnfromPte (
213 IN UINTN Entry
214 )
215{
216 return ((Entry & PTE_PPN_MASK) >> PTE_PPN_SHIFT);
217}
218
219/**
220 Set an ppn value to a entry.
221
222 @param Entry The entry value.
223 @param Address The address.
224
225 @return The new entry value.
226
227**/
228STATIC
229UINTN
230SetPpnToPte (
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
243/**
244 Free resources of translation table recursively.
245
246 @param TranslationTable The pointer of table.
247 @param Level The current level.
248
249**/
250STATIC
251VOID
252FreePageTablesRecursive (
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])) {
262 FreePageTablesRecursive (
263 (UINTN *)(GetPpnfromPte ((TranslationTable[Index])) <<
264 RISCV_MMU_PAGE_SHIFT),
265 Level + 1
266 );
267 }
268 }
269 }
270
271 FreePages (TranslationTable, 1);
272}
273
274/**
275 Update region mapping recursively.
276
277 @param RegionStart The start address of the region.
278 @param RegionEnd The end address of the region.
279 @param AttributeSetMask The attribute mask to be set.
280 @param AttributeClearMask The attribute mask to be clear.
281 @param PageTable The pointer of current page table.
282 @param Level The current level.
283 @param TableIsLive TRUE if this is live update, FALSE otherwise.
284
285 @retval EFI_OUT_OF_RESOURCES Not enough resource.
286 @retval EFI_SUCCESS The operation succesfully.
287
288**/
289STATIC
290EFI_STATUS
291UpdateRegionMappingRecursive (
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 //
361 Status = UpdateRegionMappingRecursive (
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 //
390 Status = UpdateRegionMappingRecursive (
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);
417 ReplaceTableEntry (
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
448/**
449 Update region mapping at root table.
450
451 @param RegionStart The start address of the region.
452 @param RegionLength The length of the region.
453 @param AttributeSetMask The attribute mask to be set.
454 @param AttributeClearMask The attribute mask to be clear.
455 @param RootTable The pointer of root table.
456 @param TableIsLive TRUE if this is live update, FALSE otherwise.
457
458 @retval EFI_INVALID_PARAMETER The RegionStart or RegionLength was not valid.
459 @retval EFI_OUT_OF_RESOURCES Not enough resource.
460 @retval EFI_SUCCESS The operation succesfully.
461
462**/
463STATIC
464EFI_STATUS
465UpdateRegionMapping (
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
478 return UpdateRegionMappingRecursive (
479 RegionStart,
480 RegionStart + RegionLength,
481 AttributeSetMask,
482 AttributeClearMask,
483 RootTable,
484 0,
485 TableIsLive
486 );
487}
488
489/**
490 Convert GCD attribute to RISC-V page attribute.
491
492 @param GcdAttributes The GCD attribute.
493
494 @return The RISC-V page attribute.
495
496**/
497STATIC
498UINTN
499GcdAttributeToPageAttribute (
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
520/**
521 The API to set a GCD attribute on an memory region.
522
523 @param BaseAddress The base address of the region.
524 @param Length The length of the region.
525 @param Attributes The GCD attributes.
526
527 @retval EFI_INVALID_PARAMETER The BaseAddress or Length was not valid.
528 @retval EFI_OUT_OF_RESOURCES Not enough resource.
529 @retval EFI_SUCCESS The operation succesfully.
530
531**/
532EFI_STATUS
533EFIAPI
534RiscVSetMemoryAttributes (
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,
563 (UINTN *)RiscVGetRootTranslateTable (),
564 TRUE
565 );
566}
567
568/**
569 Set SATP mode.
570
571 @param SatpMode The SATP mode to be set.
572
573 @retval EFI_INVALID_PARAMETER The SATP mode was not valid.
574 @retval EFI_OUT_OF_RESOURCES Not enough resource.
575 @retval EFI_DEVICE_ERROR The SATP mode not supported by HW.
576 @retval EFI_SUCCESS The operation succesfully.
577
578**/
579STATIC
580EFI_STATUS
581RiscVMmuSetSatpMode (
582 UINTN SatpMode
583 )
584{
585 VOID *TranslationTable;
586 UINTN SatpReg;
587 UINTN Ppn;
588 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemoryMap;
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
634 UpdateRegionMapping (
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
644 UpdateRegionMapping (
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 ()) {
658 DisableInterrupts ();
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
682 RiscVLocalTlbFlushAll ();
683
684 if (GetInterruptState ()) {
685 EnableInterrupts ();
686 }
687
688 return Status;
689}
690
691/**
692 The API to configure and enable RISC-V MMU with the highest mode supported.
693
694 @retval EFI_OUT_OF_RESOURCES Not enough resource.
695 @retval EFI_SUCCESS The operation succesfully.
696
697**/
698EFI_STATUS
699EFIAPI
700RiscVConfigureMmu (
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}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette