memory.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. /*++
  2. Copyright (c) 2014 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. memory.c
  9. Abstract:
  10. This module implements UEFI-specific memory management support.
  11. Author:
  12. Evan Green 11-Feb-2014
  13. Environment:
  14. Boot
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include <minoca/uefi/uefi.h>
  21. #include "firmware.h"
  22. #include <minoca/lib/basevid.h>
  23. #include "bootlib.h"
  24. #include "efisup.h"
  25. #include "paging.h"
  26. //
  27. // ---------------------------------------------------------------- Definitions
  28. //
  29. #define EFI_EXIT_BOOT_SERVICES_TRY_COUNT 4
  30. //
  31. // ------------------------------------------------------ Data Type Definitions
  32. //
  33. //
  34. // ----------------------------------------------- Internal Function Prototypes
  35. //
  36. KSTATUS
  37. BopEfiMapInitialDescriptorAllocation (
  38. VOID
  39. );
  40. KSTATUS
  41. BopEfiMapRuntimeServices (
  42. UINTN *MemoryMapSize,
  43. UINTN *DescriptorSize,
  44. UINT32 *DescriptorVersion,
  45. EFI_MEMORY_DESCRIPTOR **VirtualMap
  46. );
  47. KSTATUS
  48. BopMapRamDisk (
  49. PHYSICAL_ADDRESS Base,
  50. ULONGLONG Size,
  51. PKERNEL_INITIALIZATION_BLOCK Parameters
  52. );
  53. //
  54. // -------------------------------------------------------------------- Globals
  55. //
  56. //
  57. // ------------------------------------------------------------------ Functions
  58. //
  59. KSTATUS
  60. BoFwMapKnownRegions (
  61. ULONG Phase,
  62. PKERNEL_INITIALIZATION_BLOCK Parameters
  63. )
  64. /*++
  65. Routine Description:
  66. This routine maps known ARM regions of memory.
  67. Arguments:
  68. Phase - Supplies the phase number, as this routine is called twice: once
  69. before any other mappings have been established (0), and once near the
  70. end of the loader (1).
  71. Parameters - Supplies a pointer to the kernel's initialization parameters.
  72. Return Value:
  73. Status code.
  74. --*/
  75. {
  76. PSYSTEM_RESOURCE_FRAME_BUFFER FrameBufferResource;
  77. ULONG RamDiskCount;
  78. ULONG RamDiskIndex;
  79. PBOOT_RAM_DISK RamDisks;
  80. KSTATUS Status;
  81. RamDisks = NULL;
  82. FrameBufferResource = NULL;
  83. if (Phase == 0) {
  84. Status = BopEfiMapInitialDescriptorAllocation();
  85. if (!KSUCCESS(Status)) {
  86. goto MapKnownRegionsEnd;
  87. }
  88. } else {
  89. ASSERT(Phase == 1);
  90. //
  91. // Map the frame buffer. It's okay if the frame buffer doesn't exist.
  92. //
  93. FrameBufferResource =
  94. BoAllocateMemory(sizeof(SYSTEM_RESOURCE_FRAME_BUFFER));
  95. if (FrameBufferResource == NULL) {
  96. Status = STATUS_NO_MEMORY;
  97. goto MapKnownRegionsEnd;
  98. }
  99. RtlZeroMemory(FrameBufferResource,
  100. sizeof(SYSTEM_RESOURCE_FRAME_BUFFER));
  101. FrameBufferResource->Header.Type = SystemResourceFrameBuffer;
  102. Status = BopEfiGetVideoInformation(
  103. &(FrameBufferResource->Width),
  104. &(FrameBufferResource->Height),
  105. &(FrameBufferResource->PixelsPerScanLine),
  106. &(FrameBufferResource->BitsPerPixel),
  107. &(FrameBufferResource->RedMask),
  108. &(FrameBufferResource->GreenMask),
  109. &(FrameBufferResource->BlueMask),
  110. &(FrameBufferResource->Header.PhysicalAddress),
  111. &(FrameBufferResource->Header.Size));
  112. if (KSUCCESS(Status)) {
  113. FrameBufferResource->Mode = BaseVideoModeFrameBuffer;
  114. FrameBufferResource->Header.VirtualAddress = (PVOID)-1;
  115. Status = BoMapPhysicalAddress(
  116. &(FrameBufferResource->Header.VirtualAddress),
  117. FrameBufferResource->Header.PhysicalAddress,
  118. FrameBufferResource->Header.Size,
  119. MAP_FLAG_WRITE_THROUGH | MAP_FLAG_GLOBAL,
  120. MemoryTypeLoaderPermanent);
  121. if (!KSUCCESS(Status)) {
  122. goto MapKnownRegionsEnd;
  123. }
  124. INSERT_BEFORE(&(FrameBufferResource->Header.ListEntry),
  125. &(Parameters->SystemResourceListHead));
  126. } else {
  127. BoFreeMemory(FrameBufferResource);
  128. }
  129. FrameBufferResource = NULL;
  130. //
  131. // Get a list of all the RAM disks, and map them.
  132. //
  133. Status = FwGetRamDisks(&RamDisks, &RamDiskCount);
  134. if (KSUCCESS(Status)) {
  135. for (RamDiskIndex = 0;
  136. RamDiskIndex < RamDiskCount;
  137. RamDiskIndex += 1) {
  138. BopMapRamDisk(RamDisks[RamDiskIndex].Base,
  139. RamDisks[RamDiskIndex].Size,
  140. Parameters);
  141. }
  142. }
  143. }
  144. Status = STATUS_SUCCESS;
  145. MapKnownRegionsEnd:
  146. if (RamDisks != NULL) {
  147. BoFreeMemory(RamDisks);
  148. }
  149. if (FrameBufferResource != NULL) {
  150. BoFreeMemory(FrameBufferResource);
  151. }
  152. return Status;
  153. }
  154. KSTATUS
  155. BoFwPrepareForKernelLaunch (
  156. PKERNEL_INITIALIZATION_BLOCK Parameters
  157. )
  158. /*++
  159. Routine Description:
  160. This routine coordinates with the firmware to end boot services and
  161. prepare for the operating system to take over. Translation is still
  162. disabled (or identity mapped) at this point.
  163. Arguments:
  164. Parameters - Supplies a pointer to the kernel initialization block.
  165. Return Value:
  166. Status code.
  167. --*/
  168. {
  169. UINTN EfiDescriptorSize;
  170. UINT32 EfiDescriptorVersion;
  171. EFI_MEMORY_DESCRIPTOR *EfiMap;
  172. UINTN EfiMapKey;
  173. UINTN EfiMapSize;
  174. EFI_STATUS EfiStatus;
  175. KSTATUS Status;
  176. ULONG Try;
  177. Parameters->FirmwareType = SystemFirmwareEfi;
  178. //
  179. // Stop the debugger from using stall.
  180. //
  181. KdSetConnectionTimeout(MAX_ULONG);
  182. //
  183. // Create mappings for all runtime services.
  184. //
  185. Status = BopEfiMapRuntimeServices(&EfiMapSize,
  186. &EfiDescriptorSize,
  187. &EfiDescriptorVersion,
  188. &EfiMap);
  189. if (!KSUCCESS(Status)) {
  190. goto PrepareForKernelLaunchEnd;
  191. }
  192. //
  193. // Loop attempting to synchronize the memory map, and exit boot services.
  194. // This can fail if the EFI memory map changes in between getting it and
  195. // exiting, though that should be rare.
  196. //
  197. for (Try = 0; Try < EFI_EXIT_BOOT_SERVICES_TRY_COUNT; Try += 1) {
  198. Status = BopEfiSynchronizeMemoryMap(&EfiMapKey);
  199. if (!KSUCCESS(Status)) {
  200. goto PrepareForKernelLaunchEnd;
  201. }
  202. EfiStatus = BopEfiExitBootServices(BoEfiImageHandle, EfiMapKey);
  203. Status = BopEfiStatusToKStatus(EfiStatus);
  204. if (!EFI_ERROR(EfiStatus)) {
  205. break;
  206. }
  207. }
  208. if (!KSUCCESS(Status)) {
  209. goto PrepareForKernelLaunchEnd;
  210. }
  211. //
  212. // Boot services are no longer available.
  213. //
  214. BoEfiBootServices = NULL;
  215. //
  216. // Virtualize the runtime services.
  217. //
  218. Status = BopEfiVirtualizeFirmwareServices(EfiMapSize,
  219. EfiDescriptorSize,
  220. EfiDescriptorVersion,
  221. EfiMap);
  222. if (!KSUCCESS(Status)) {
  223. goto PrepareForKernelLaunchEnd;
  224. }
  225. //
  226. // Save the system table for the kernel.
  227. //
  228. Parameters->EfiRuntimeServices = BoEfiSystemTable->RuntimeServices;
  229. PrepareForKernelLaunchEnd:
  230. if (EfiMap != NULL) {
  231. BoFreeMemory(EfiMap);
  232. }
  233. return Status;
  234. }
  235. //
  236. // --------------------------------------------------------- Internal Functions
  237. //
  238. KSTATUS
  239. BopEfiMapInitialDescriptorAllocation (
  240. VOID
  241. )
  242. /*++
  243. Routine Description:
  244. This routine maps the initial memory descriptor allocation.
  245. Arguments:
  246. None.
  247. Return Value:
  248. Status code.
  249. --*/
  250. {
  251. KSTATUS Status;
  252. PVOID VirtualAddress;
  253. ASSERT((UINTN)BoEfiDescriptorAllocation == BoEfiDescriptorAllocation);
  254. VirtualAddress = (PVOID)(UINTN)BoEfiDescriptorAllocation;
  255. Status = BoMapPhysicalAddress(
  256. &VirtualAddress,
  257. BoEfiDescriptorAllocation,
  258. BoEfiDescriptorAllocationPageCount << EFI_PAGE_SHIFT,
  259. 0,
  260. MemoryTypeLoaderTemporary);
  261. if (!KSUCCESS(Status)) {
  262. goto EfiMapInitialDescriptorAllocationEnd;
  263. }
  264. Status = STATUS_SUCCESS;
  265. EfiMapInitialDescriptorAllocationEnd:
  266. return Status;
  267. }
  268. KSTATUS
  269. BopEfiMapRuntimeServices (
  270. UINTN *MemoryMapSize,
  271. UINTN *DescriptorSize,
  272. UINT32 *DescriptorVersion,
  273. EFI_MEMORY_DESCRIPTOR **VirtualMap
  274. )
  275. /*++
  276. Routine Description:
  277. This routine maps any runtime services code or data into the kernel VA
  278. space.
  279. Arguments:
  280. MemoryMapSize - Supplies a pointer where the memory map size will be
  281. returned on success.
  282. DescriptorSize - Supplies a pointer where the size of an
  283. EFI_MEMORY_DESCRIPTOR will be returned on success.
  284. DescriptorVersion - Supplies a pointer where the descriptor version will
  285. be returned on success.
  286. VirtualMap - Supplies a pointer where a pointer to the the virtual map will
  287. be returned on success.
  288. Return Value:
  289. Status code.
  290. --*/
  291. {
  292. ULONG Attributes;
  293. UINTN DescriptorIndex;
  294. ULONGLONG EfiAttributes;
  295. EFI_MEMORY_DESCRIPTOR *EfiDescriptor;
  296. EFI_MEMORY_DESCRIPTOR *EfiMap;
  297. UINTN EfiMapKey;
  298. ULONGLONG Size;
  299. KSTATUS Status;
  300. PVOID VirtualAddress;
  301. EfiMap = NULL;
  302. Status = BopEfiGetAllocatedMemoryMap(MemoryMapSize,
  303. &EfiMap,
  304. &EfiMapKey,
  305. DescriptorSize,
  306. DescriptorVersion);
  307. if (!KSUCCESS(Status)) {
  308. goto EfiMapRuntimeServicesEnd;
  309. }
  310. //
  311. // Loop through each descriptor in the memory map.
  312. //
  313. DescriptorIndex = 0;
  314. while ((DescriptorIndex + 1) * *DescriptorSize <= *MemoryMapSize) {
  315. EfiDescriptor = (EFI_MEMORY_DESCRIPTOR *)((PVOID)EfiMap +
  316. (DescriptorIndex * *DescriptorSize));
  317. DescriptorIndex += 1;
  318. EfiAttributes = EfiDescriptor->Attribute;
  319. //
  320. // Skip it if it's not a runtime descriptor.
  321. //
  322. if ((EfiAttributes & EFI_MEMORY_RUNTIME) == 0) {
  323. continue;
  324. }
  325. Attributes = MAP_FLAG_GLOBAL | MAP_FLAG_EXECUTE;
  326. if ((EfiAttributes &
  327. (EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC)) != 0) {
  328. Attributes |= MAP_FLAG_CACHE_DISABLE;
  329. }
  330. if ((EfiAttributes & EFI_MEMORY_WT) != 0) {
  331. Attributes |= MAP_FLAG_WRITE_THROUGH;
  332. }
  333. if ((EfiAttributes & EFI_MEMORY_WP) != 0) {
  334. Attributes |= MAP_FLAG_READ_ONLY;
  335. }
  336. Size = EfiDescriptor->NumberOfPages << EFI_PAGE_SHIFT;
  337. ASSERT((UINTN)Size == Size);
  338. VirtualAddress = (PVOID)-1;
  339. Status = BoMapPhysicalAddress(&VirtualAddress,
  340. EfiDescriptor->PhysicalStart,
  341. Size,
  342. Attributes,
  343. MemoryTypeFirmwarePermanent);
  344. if (!KSUCCESS(Status)) {
  345. goto EfiMapRuntimeServicesEnd;
  346. }
  347. EfiDescriptor->VirtualStart = (UINTN)VirtualAddress;
  348. }
  349. Status = STATUS_SUCCESS;
  350. EfiMapRuntimeServicesEnd:
  351. if (!KSUCCESS(Status)) {
  352. if (EfiMap != NULL) {
  353. BoFreeMemory(EfiMap);
  354. EfiMap = NULL;
  355. }
  356. }
  357. *VirtualMap = EfiMap;
  358. return Status;
  359. }
  360. KSTATUS
  361. BopMapRamDisk (
  362. PHYSICAL_ADDRESS Base,
  363. ULONGLONG Size,
  364. PKERNEL_INITIALIZATION_BLOCK Parameters
  365. )
  366. /*++
  367. Routine Description:
  368. This routine maps a RAM disk at the given address.
  369. Arguments:
  370. Base - Supplies the base physical address of the RAM disk.
  371. Size - Supplies the size of the RAM disk in bytes.
  372. Parameters - Supplies a pointer to the kernel initialization block.
  373. Return Value:
  374. Status code.
  375. --*/
  376. {
  377. PHYSICAL_ADDRESS AlignedBase;
  378. ULONGLONG AlignedSize;
  379. MEMORY_DESCRIPTOR Descriptor;
  380. ULONG PageOffset;
  381. ULONG PageSize;
  382. PSYSTEM_RESOURCE_RAM_DISK RamDisk;
  383. KSTATUS Status;
  384. PageSize = MmPageSize();
  385. RamDisk = BoAllocateMemory(sizeof(SYSTEM_RESOURCE_RAM_DISK));
  386. if (RamDisk == NULL) {
  387. Status = STATUS_INSUFFICIENT_RESOURCES;
  388. goto MapRamDiskEnd;
  389. }
  390. RtlZeroMemory(RamDisk, sizeof(SYSTEM_RESOURCE_RAM_DISK));
  391. RamDisk->Header.Type = SystemResourceRamDisk;
  392. RamDisk->Header.PhysicalAddress = Base;
  393. RamDisk->Header.Size = Size;
  394. RamDisk->Header.VirtualAddress = (PVOID)-1;
  395. AlignedBase = ALIGN_RANGE_DOWN(Base, PageSize);
  396. PageOffset = Base - AlignedBase;
  397. AlignedSize = ALIGN_RANGE_UP(Size + PageOffset, PageSize);
  398. Status = BoMapPhysicalAddress(&(RamDisk->Header.VirtualAddress),
  399. AlignedBase,
  400. AlignedSize,
  401. MAP_FLAG_GLOBAL,
  402. MemoryTypeLoaderPermanent);
  403. if (!KSUCCESS(Status)) {
  404. goto MapRamDiskEnd;
  405. }
  406. RamDisk->Header.VirtualAddress += PageOffset;
  407. //
  408. // Mark the pages of the RAM disk as loader permanent.
  409. //
  410. MmMdInitDescriptor(&Descriptor,
  411. AlignedBase,
  412. AlignedBase + AlignedSize,
  413. MemoryTypeLoaderPermanent);
  414. Status = MmMdAddDescriptorToList(&BoMemoryMap, &Descriptor);
  415. if (!KSUCCESS(Status)) {
  416. goto MapRamDiskEnd;
  417. }
  418. INSERT_BEFORE(&(RamDisk->Header.ListEntry),
  419. &(Parameters->SystemResourceListHead));
  420. Status = STATUS_SUCCESS;
  421. MapRamDiskEnd:
  422. if (!KSUCCESS(Status)) {
  423. if (RamDisk != NULL) {
  424. if (RamDisk->Header.VirtualAddress != NULL) {
  425. BoUnmapPhysicalAddress(RamDisk->Header.VirtualAddress,
  426. AlignedSize / PageSize);
  427. }
  428. BoFreeMemory(RamDisk);
  429. }
  430. }
  431. return Status;
  432. }