//\r
// The Library classes this module consumes\r
//\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/CpuLib.h>\r
#include <Library/DebugLib.h>\r
#include <Library/HobLib.h>\r
+#include <Library/LocalApicLib.h>\r
#include <Library/MemoryAllocationLib.h>\r
#include <Library/PcdLib.h>\r
+#include <Library/SafeIntLib.h>\r
#include <Guid/XenInfo.h>\r
#include <IndustryStandard/E820.h>\r
#include <Library/ResourcePublicationLib.h>\r
#include <Library/MtrrLib.h>\r
+#include <IndustryStandard/PageTable.h>\r
#include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>\r
#include <Library/XenHypercallLib.h>\r
#include <IndustryStandard/Xen/memory.h>\r
#include "Platform.h"\r
#include "Xen.h"\r
\r
-STATIC UINT32 mXenLeaf = 0;\r
+STATIC UINT32 mXenLeaf = 0;\r
\r
-EFI_XEN_INFO mXenInfo;\r
+EFI_XEN_INFO mXenInfo;\r
\r
//\r
// Location of the firmware info struct setup by hvmloader.\r
// Only the E820 table is used by OVMF.\r
//\r
-EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;\r
-STATIC EFI_E820_ENTRY64 mE820Entries[128];\r
-STATIC UINT32 mE820EntriesCount;\r
+EFI_XEN_OVMF_INFO *mXenHvmloaderInfo;\r
+STATIC EFI_E820_ENTRY64 mE820Entries[128];\r
+STATIC UINT32 mE820EntriesCount;\r
\r
/**\r
Returns E820 map provided by Xen\r
**/\r
EFI_STATUS\r
XenGetE820Map (\r
- EFI_E820_ENTRY64 **Entries,\r
- UINT32 *Count\r
+ EFI_E820_ENTRY64 **Entries,\r
+ UINT32 *Count\r
)\r
{\r
- INTN ReturnCode;\r
- xen_memory_map_t Parameters;\r
- UINTN LoopIndex;\r
- UINTN Index;\r
- EFI_E820_ENTRY64 TmpEntry;\r
+ INTN ReturnCode;\r
+ xen_memory_map_t Parameters;\r
+ UINTN LoopIndex;\r
+ UINTN Index;\r
+ EFI_E820_ENTRY64 TmpEntry;\r
\r
//\r
// Get E820 produced by hvmloader\r
//\r
if (mXenHvmloaderInfo != NULL) {\r
ASSERT (mXenHvmloaderInfo->E820 < MAX_ADDRESS);\r
- *Entries = (EFI_E820_ENTRY64 *)(UINTN) mXenHvmloaderInfo->E820;\r
- *Count = mXenHvmloaderInfo->E820EntriesCount;\r
+ *Entries = (EFI_E820_ENTRY64 *)(UINTN)mXenHvmloaderInfo->E820;\r
+ *Count = mXenHvmloaderInfo->E820EntriesCount;\r
\r
return EFI_SUCCESS;\r
}\r
\r
if (mE820EntriesCount > 0) {\r
*Entries = mE820Entries;\r
- *Count = mE820EntriesCount;\r
+ *Count = mE820EntriesCount;\r
return EFI_SUCCESS;\r
}\r
\r
for (LoopIndex = 1; LoopIndex < mE820EntriesCount; LoopIndex++) {\r
for (Index = LoopIndex; Index < mE820EntriesCount; Index++) {\r
if (mE820Entries[Index - 1].BaseAddr > mE820Entries[Index].BaseAddr) {\r
- TmpEntry = mE820Entries[Index];\r
- mE820Entries[Index] = mE820Entries[Index - 1];\r
+ TmpEntry = mE820Entries[Index];\r
+ mE820Entries[Index] = mE820Entries[Index - 1];\r
mE820Entries[Index - 1] = TmpEntry;\r
}\r
}\r
}\r
\r
- *Count = mE820EntriesCount;\r
+ *Count = mE820EntriesCount;\r
*Entries = mE820Entries;\r
\r
return EFI_SUCCESS;\r
XenConnect (\r
)\r
{\r
- UINT32 Index;\r
- UINT32 TransferReg;\r
- UINT32 TransferPages;\r
- UINT32 XenVersion;\r
- EFI_XEN_OVMF_INFO *Info;\r
- CHAR8 Sig[sizeof (Info->Signature) + 1];\r
- UINT32 *PVHResetVectorData;\r
- RETURN_STATUS Status;\r
+ UINT32 Index;\r
+ UINT32 TransferReg;\r
+ UINT32 TransferPages;\r
+ UINT32 XenVersion;\r
+ EFI_XEN_OVMF_INFO *Info;\r
+ CHAR8 Sig[sizeof (Info->Signature) + 1];\r
+ UINT32 *PVHResetVectorData;\r
+ RETURN_STATUS Status;\r
\r
ASSERT (mXenLeaf != 0);\r
\r
}\r
\r
for (Index = 0; Index < TransferPages; Index++) {\r
- AsmWriteMsr64 (TransferReg,\r
- (UINTN) mXenInfo.HyperPages +\r
- (Index << EFI_PAGE_SHIFT) + Index);\r
+ AsmWriteMsr64 (\r
+ TransferReg,\r
+ (UINTN)mXenInfo.HyperPages +\r
+ (Index << EFI_PAGE_SHIFT) + Index\r
+ );\r
}\r
\r
//\r
//\r
\r
AsmCpuid (mXenLeaf + 1, &XenVersion, NULL, NULL, NULL);\r
- DEBUG ((DEBUG_ERROR, "Detected Xen version %d.%d\n",\r
- XenVersion >> 16, XenVersion & 0xFFFF));\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "Detected Xen version %d.%d\n",\r
+ XenVersion >> 16,\r
+ XenVersion & 0xFFFF\r
+ ));\r
mXenInfo.VersionMajor = (UINT16)(XenVersion >> 16);\r
mXenInfo.VersionMinor = (UINT16)(XenVersion & 0xFFFF);\r
\r
// Check if there are information left by hvmloader\r
//\r
\r
- Info = (EFI_XEN_OVMF_INFO *)(UINTN) OVMF_INFO_PHYSICAL_ADDRESS;\r
+ Info = (EFI_XEN_OVMF_INFO *)(UINTN)OVMF_INFO_PHYSICAL_ADDRESS;\r
//\r
// Copy the signature, and make it null-terminated.\r
//\r
- AsciiStrnCpyS (Sig, sizeof (Sig), (CHAR8 *) &Info->Signature,\r
- sizeof (Info->Signature));\r
+ AsciiStrnCpyS (\r
+ Sig,\r
+ sizeof (Sig),\r
+ (CHAR8 *)&Info->Signature,\r
+ sizeof (Info->Signature)\r
+ );\r
if (AsciiStrCmp (Sig, "XenHVMOVMF") == 0) {\r
mXenHvmloaderInfo = Info;\r
} else {\r
// booted via the PVH entry point.\r
//\r
\r
- PVHResetVectorData = (VOID *)(UINTN) PcdGet32 (PcdXenPvhStartOfDayStructPtr);\r
+ PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);\r
//\r
// That magic value is written in XenResetVector/Ia32/XenPVHMain.asm\r
//\r
if (PVHResetVectorData[1] == SIGNATURE_32 ('X', 'P', 'V', 'H')) {\r
- struct hvm_start_info *HVMStartInfo;\r
+ struct hvm_start_info *HVMStartInfo;\r
\r
- HVMStartInfo = (VOID *)(UINTN) PVHResetVectorData[0];\r
+ HVMStartInfo = (VOID *)(UINTN)PVHResetVectorData[0];\r
if (HVMStartInfo->magic == XEN_HVM_START_MAGIC_VALUE) {\r
ASSERT (HVMStartInfo->rsdp_paddr != 0);\r
if (HVMStartInfo->rsdp_paddr != 0) {\r
BuildGuidDataHob (\r
&gEfiXenInfoGuid,\r
&mXenInfo,\r
- sizeof(mXenInfo)\r
+ sizeof (mXenInfo)\r
);\r
\r
//\r
VOID\r
)\r
{\r
- UINT8 Signature[13];\r
+ UINT8 Signature[13];\r
\r
if (mXenLeaf != 0) {\r
return TRUE;\r
\r
Signature[12] = '\0';\r
for (mXenLeaf = 0x40000000; mXenLeaf < 0x40010000; mXenLeaf += 0x100) {\r
- AsmCpuid (mXenLeaf,\r
- NULL,\r
- (UINT32 *) &Signature[0],\r
- (UINT32 *) &Signature[4],\r
- (UINT32 *) &Signature[8]);\r
-\r
- if (!AsciiStrCmp ((CHAR8 *) Signature, "XenVMMXenVMM")) {\r
+ AsmCpuid (\r
+ mXenLeaf,\r
+ NULL,\r
+ (UINT32 *)&Signature[0],\r
+ (UINT32 *)&Signature[4],\r
+ (UINT32 *)&Signature[8]\r
+ );\r
+\r
+ if (!AsciiStrCmp ((CHAR8 *)Signature, "XenVMMXenVMM")) {\r
return TRUE;\r
}\r
}\r
EFI_E820_ENTRY64 *E820Map;\r
UINT32 E820EntriesCount;\r
EFI_STATUS Status;\r
+ EFI_E820_ENTRY64 *Entry;\r
+ UINTN Index;\r
+ UINT64 LapicBase;\r
+ UINT64 LapicEnd;\r
\r
DEBUG ((DEBUG_INFO, "Using memory map provided by Xen\n"));\r
\r
// Parse RAM in E820 map\r
//\r
E820EntriesCount = 0;\r
- Status = XenGetE820Map (&E820Map, &E820EntriesCount);\r
-\r
+ Status = XenGetE820Map (&E820Map, &E820EntriesCount);\r
ASSERT_EFI_ERROR (Status);\r
\r
- if (E820EntriesCount > 0) {\r
- EFI_E820_ENTRY64 *Entry;\r
- UINT32 Loop;\r
+ AddMemoryBaseSizeHob (0, 0xA0000);\r
+ //\r
+ // Video memory + Legacy BIOS region, to allow Linux to boot.\r
+ //\r
+ AddReservedMemoryBaseSizeHob (0xA0000, BASE_1MB - 0xA0000, TRUE);\r
+\r
+ LapicBase = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
+ LapicEnd = LapicBase + SIZE_1MB;\r
+ AddIoMemoryRangeHob (LapicBase, LapicEnd);\r
+\r
+ for (Index = 0; Index < E820EntriesCount; Index++) {\r
+ UINT64 Base;\r
+ UINT64 End;\r
+ UINT64 ReservedBase;\r
+ UINT64 ReservedEnd;\r
+\r
+ Entry = &E820Map[Index];\r
+\r
+ //\r
+ // Round up the start address, and round down the end address.\r
+ //\r
+ Base = ALIGN_VALUE (Entry->BaseAddr, (UINT64)EFI_PAGE_SIZE);\r
+ End = (Entry->BaseAddr + Entry->Length) & ~(UINT64)EFI_PAGE_MASK;\r
+\r
+ //\r
+ // Ignore the first 1MB, this is handled before the loop.\r
+ //\r
+ if (Base < BASE_1MB) {\r
+ Base = BASE_1MB;\r
+ }\r
\r
- for (Loop = 0; Loop < E820EntriesCount; Loop++) {\r
- Entry = E820Map + Loop;\r
+ if (Base >= End) {\r
+ continue;\r
+ }\r
\r
- //\r
- // Only care about RAM\r
- //\r
- if (Entry->Type != EfiAcpiAddressRangeMemory) {\r
- continue;\r
- }\r
+ switch (Entry->Type) {\r
+ case EfiAcpiAddressRangeMemory:\r
+ AddMemoryRangeHob (Base, End);\r
+ break;\r
+ case EfiAcpiAddressRangeACPI:\r
+ AddReservedMemoryRangeHob (Base, End, FALSE);\r
+ break;\r
+ case EfiAcpiAddressRangeReserved:\r
+ //\r
+ // hvmloader marks a range that overlaps with the local APIC memory\r
+ // mapped region as reserved, but CpuDxe wants it as mapped IO. We\r
+ // have already added it as mapped IO, so skip it here.\r
+ //\r
+\r
+ //\r
+ // add LAPIC predecessor range, if any\r
+ //\r
+ ReservedBase = Base;\r
+ ReservedEnd = MIN (End, LapicBase);\r
+ if (ReservedBase < ReservedEnd) {\r
+ AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);\r
+ }\r
+\r
+ //\r
+ // add LAPIC successor range, if any\r
+ //\r
+ ReservedBase = MAX (Base, LapicEnd);\r
+ ReservedEnd = End;\r
+ if (ReservedBase < ReservedEnd) {\r
+ AddReservedMemoryRangeHob (ReservedBase, ReservedEnd, FALSE);\r
+ }\r
+\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+EFI_STATUS\r
+PhysicalAddressIdentityMapping (\r
+ IN EFI_PHYSICAL_ADDRESS AddressToMap\r
+ )\r
+{\r
+ INTN Index;\r
+ PAGE_MAP_AND_DIRECTORY_POINTER *L4, *L3;\r
+ PAGE_TABLE_ENTRY *PageTable;\r
\r
- AddMemoryBaseSizeHob (Entry->BaseAddr, Entry->Length);\r
+ DEBUG ((DEBUG_INFO, "Mapping 1:1 of address 0x%lx\n", (UINT64)AddressToMap));\r
\r
- MtrrSetMemoryAttribute (Entry->BaseAddr, Entry->Length, CacheWriteBack);\r
+ // L4 / Top level Page Directory Pointers\r
+\r
+ L4 = (VOID *)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase);\r
+ Index = PML4_OFFSET (AddressToMap);\r
+\r
+ if (!L4[Index].Bits.Present) {\r
+ L3 = AllocatePages (1);\r
+ if (L3 == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
}\r
+\r
+ ZeroMem (L3, EFI_PAGE_SIZE);\r
+\r
+ L4[Index].Bits.ReadWrite = 1;\r
+ L4[Index].Bits.Accessed = 1;\r
+ L4[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)L3 >> 12;\r
+ L4[Index].Bits.Present = 1;\r
}\r
-}\r
\r
+ // L3 / Next level Page Directory Pointers\r
\r
-/**\r
- Perform Xen PEI initialization.\r
+ L3 = (VOID *)(EFI_PHYSICAL_ADDRESS)(L4[Index].Bits.PageTableBaseAddress << 12);\r
+ Index = PDP_OFFSET (AddressToMap);\r
\r
- @return EFI_SUCCESS Xen initialized successfully\r
- @return EFI_NOT_FOUND Not running under Xen\r
+ if (!L3[Index].Bits.Present) {\r
+ PageTable = AllocatePages (1);\r
+ if (PageTable == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
\r
-**/\r
+ ZeroMem (PageTable, EFI_PAGE_SIZE);\r
+\r
+ L3[Index].Bits.ReadWrite = 1;\r
+ L3[Index].Bits.Accessed = 1;\r
+ L3[Index].Bits.PageTableBaseAddress = (EFI_PHYSICAL_ADDRESS)PageTable >> 12;\r
+ L3[Index].Bits.Present = 1;\r
+ }\r
+\r
+ // L2 / Page Table Entries\r
+\r
+ PageTable = (VOID *)(EFI_PHYSICAL_ADDRESS)(L3[Index].Bits.PageTableBaseAddress << 12);\r
+ Index = PDE_OFFSET (AddressToMap);\r
+\r
+ if (!PageTable[Index].Bits.Present) {\r
+ PageTable[Index].Bits.ReadWrite = 1;\r
+ PageTable[Index].Bits.Accessed = 1;\r
+ PageTable[Index].Bits.Dirty = 1;\r
+ PageTable[Index].Bits.MustBe1 = 1;\r
+ PageTable[Index].Bits.PageTableBaseAddress = AddressToMap >> 21;\r
+ PageTable[Index].Bits.Present = 1;\r
+ }\r
+\r
+ CpuFlushTlb ();\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
EFI_STATUS\r
-InitializeXen (\r
- VOID\r
+MapSharedInfoPage (\r
+ IN VOID *PagePtr\r
+ )\r
+{\r
+ xen_add_to_physmap_t Parameters;\r
+ INTN ReturnCode;\r
+\r
+ Parameters.domid = DOMID_SELF;\r
+ Parameters.space = XENMAPSPACE_shared_info;\r
+ Parameters.idx = 0;\r
+ Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;\r
+ ReturnCode = XenHypercallMemoryOp (XENMEM_add_to_physmap, &Parameters);\r
+ if (ReturnCode != 0) {\r
+ return EFI_NO_MAPPING;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC\r
+VOID\r
+UnmapXenPage (\r
+ IN VOID *PagePtr\r
+ )\r
+{\r
+ xen_remove_from_physmap_t Parameters;\r
+ INTN ReturnCode;\r
+\r
+ Parameters.domid = DOMID_SELF;\r
+ Parameters.gpfn = (UINTN)PagePtr >> EFI_PAGE_SHIFT;\r
+ ReturnCode = XenHypercallMemoryOp (XENMEM_remove_from_physmap, &Parameters);\r
+ ASSERT (ReturnCode == 0);\r
+}\r
+\r
+STATIC\r
+UINT64\r
+GetCpuFreq (\r
+ IN XEN_VCPU_TIME_INFO *VcpuTime\r
+ )\r
+{\r
+ UINT32 Version;\r
+ UINT32 TscToSystemMultiplier;\r
+ INT8 TscShift;\r
+ UINT64 CpuFreq;\r
+\r
+ do {\r
+ Version = VcpuTime->Version;\r
+ MemoryFence ();\r
+ TscToSystemMultiplier = VcpuTime->TscToSystemMultiplier;\r
+ TscShift = VcpuTime->TscShift;\r
+ MemoryFence ();\r
+ } while (((Version & 1) != 0) && (Version != VcpuTime->Version));\r
+\r
+ CpuFreq = DivU64x32 (LShiftU64 (1000000000ULL, 32), TscToSystemMultiplier);\r
+ if (TscShift >= 0) {\r
+ CpuFreq = RShiftU64 (CpuFreq, TscShift);\r
+ } else {\r
+ CpuFreq = LShiftU64 (CpuFreq, -TscShift);\r
+ }\r
+\r
+ return CpuFreq;\r
+}\r
+\r
+STATIC\r
+VOID\r
+XenDelay (\r
+ IN XEN_VCPU_TIME_INFO *VcpuTimeInfo,\r
+ IN UINT64 DelayNs\r
)\r
{\r
- RETURN_STATUS PcdStatus;\r
+ UINT64 Tick;\r
+ UINT64 CpuFreq;\r
+ UINT64 Delay;\r
+ UINT64 DelayTick;\r
+ UINT64 NewTick;\r
+ RETURN_STATUS Status;\r
+\r
+ Tick = AsmReadTsc ();\r
+\r
+ CpuFreq = GetCpuFreq (VcpuTimeInfo);\r
+ Status = SafeUint64Mult (DelayNs, CpuFreq, &Delay);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "XenDelay (%lu ns): delay too big in relation to CPU freq %lu Hz\n",\r
+ DelayNs,\r
+ CpuFreq\r
+ ));\r
+ ASSERT_EFI_ERROR (Status);\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ DelayTick = DivU64x32 (Delay, 1000000000);\r
+\r
+ NewTick = Tick + DelayTick;\r
\r
//\r
- // Reserve away HVMLOADER reserved memory [0xFC000000,0xFD000000).\r
- // This needs to match HVMLOADER RESERVED_MEMBASE/RESERVED_MEMSIZE.\r
+ // Check for overflow\r
//\r
- AddReservedMemoryBaseSizeHob (0xFC000000, 0x1000000, FALSE);\r
+ if (NewTick < Tick) {\r
+ //\r
+ // Overflow, wait for TSC to also overflow\r
+ //\r
+ while (AsmReadTsc () >= Tick) {\r
+ CpuPause ();\r
+ }\r
+ }\r
\r
- PcdStatus = PcdSetBoolS (PcdPciDisableBusEnumeration, TRUE);\r
- ASSERT_RETURN_ERROR (PcdStatus);\r
+ while (AsmReadTsc () <= NewTick) {\r
+ CpuPause ();\r
+ }\r
+}\r
\r
- return EFI_SUCCESS;\r
+/**\r
+ Calculate the frequency of the Local Apic Timer\r
+**/\r
+VOID\r
+CalibrateLapicTimer (\r
+ VOID\r
+ )\r
+{\r
+ XEN_SHARED_INFO *SharedInfo;\r
+ XEN_VCPU_TIME_INFO *VcpuTimeInfo;\r
+ UINT32 TimerTick, TimerTick2, DiffTimer;\r
+ UINT64 TscTick, TscTick2;\r
+ UINT64 Freq;\r
+ UINT64 Dividend;\r
+ EFI_STATUS Status;\r
+\r
+ SharedInfo = (VOID *)((UINTN)PcdGet32 (PcdCpuLocalApicBaseAddress) + SIZE_1MB);\r
+ Status = PhysicalAddressIdentityMapping ((EFI_PHYSICAL_ADDRESS)SharedInfo);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "Failed to add page table entry for Xen shared info page: %r\n",\r
+ Status\r
+ ));\r
+ ASSERT_EFI_ERROR (Status);\r
+ return;\r
+ }\r
+\r
+ Status = MapSharedInfoPage (SharedInfo);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "Failed to map Xen's shared info page: %r\n",\r
+ Status\r
+ ));\r
+ ASSERT_EFI_ERROR (Status);\r
+ return;\r
+ }\r
+\r
+ VcpuTimeInfo = &SharedInfo->VcpuInfo[0].Time;\r
+\r
+ InitializeApicTimer (1, MAX_UINT32, TRUE, 0);\r
+ DisableApicTimerInterrupt ();\r
+\r
+ TimerTick = GetApicTimerCurrentCount ();\r
+ TscTick = AsmReadTsc ();\r
+ XenDelay (VcpuTimeInfo, 1000000ULL);\r
+ TimerTick2 = GetApicTimerCurrentCount ();\r
+ TscTick2 = AsmReadTsc ();\r
+\r
+ DiffTimer = TimerTick - TimerTick2;\r
+ Status = SafeUint64Mult (GetCpuFreq (VcpuTimeInfo), DiffTimer, &Dividend);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "overflow while calculating APIC frequency\n"));\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "CPU freq: %lu Hz; APIC timer tick count for 1 ms: %u\n",\r
+ GetCpuFreq (VcpuTimeInfo),\r
+ DiffTimer\r
+ ));\r
+ ASSERT_EFI_ERROR (Status);\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ Freq = DivU64x64Remainder (Dividend, TscTick2 - TscTick, NULL);\r
+ DEBUG ((DEBUG_INFO, "APIC Freq % 8lu Hz\n", Freq));\r
+\r
+ ASSERT (Freq <= MAX_UINT32);\r
+ Status = PcdSet32S (PcdFSBClock, (UINT32)Freq);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ UnmapXenPage (SharedInfo);\r
}\r