Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1281 lines
31 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. sessload.c
  5. Abstract:
  6. This module contains the routines which implement the loading of
  7. session space drivers.
  8. Author:
  9. Landy Wang (landyw) 05-Dec-1997
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. //
  14. // This tracks allocated group virtual addresses. The term SESSIONWIDE is used
  15. // to denote data that is the same across all sessions (as opposed to
  16. // per-session data which can vary from session to session).
  17. //
  18. // Since each driver loaded into a session space is linked and fixed up
  19. // against the system image, it must remain at the same virtual address
  20. // across the system regardless of the session.
  21. //
  22. // A list is maintained by the group allocator of which virtual
  23. // addresses are in use and by which DLL.
  24. //
  25. // The reference count tracks the number of sessions that have loaded
  26. // this image.
  27. //
  28. // Access to this structure is guarded by the MmSystemLoadLock.
  29. //
  30. //
  31. // typedef struct _SESSIONWIDE_DRIVER_ADDRESS {
  32. // LIST_ENTRY Link;
  33. // ULONG ReferenceCount;
  34. // PVOID Address;
  35. // ULONG_PTR Size;
  36. // ULONG_PTR WritablePages;
  37. // UNICODE_STRING FullDllName;
  38. // } SESSIONWIDE_DRIVER_ADDRESS, *PSESSIONWIDE_DRIVER_ADDRESS;
  39. //
  40. LIST_ENTRY MmSessionWideAddressList;
  41. //
  42. // External function references
  43. //
  44. ULONG
  45. MiSetProtectionOnTransitionPte (
  46. IN PMMPTE PointerPte,
  47. IN ULONG ProtectionMask
  48. );
  49. NTSTATUS
  50. MiSessionInsertImage (
  51. IN PVOID BaseAddress
  52. );
  53. NTSTATUS
  54. MiSessionRemoveImage (
  55. IN PVOID BaseAddress
  56. );
  57. NTSTATUS
  58. MiSessionWideInsertImageAddress (
  59. IN PVOID BaseAddress,
  60. IN ULONG_PTR Size,
  61. IN ULONG WritablePages,
  62. IN PUNICODE_STRING ImageName,
  63. IN LOGICAL AtPreferredAddress
  64. );
  65. NTSTATUS
  66. MiSessionWideDereferenceImage (
  67. IN PVOID BaseAddress
  68. );
  69. #ifdef ALLOC_PRAGMA
  70. #pragma alloc_text(INIT, MiSessionWideInitializeAddresses)
  71. #pragma alloc_text(PAGE, MiSessionWideInsertImageAddress)
  72. #pragma alloc_text(PAGE, MiSessionWideDereferenceImage)
  73. #pragma alloc_text(PAGE, MiSessionWideGetImageSize)
  74. #pragma alloc_text(PAGE, MiSessionWideReserveImageAddress)
  75. #pragma alloc_text(PAGE, MiRemoveImageSessionWide)
  76. #pragma alloc_text(PAGE, MiShareSessionImage)
  77. #pragma alloc_text(PAGE, MiSessionInsertImage)
  78. #pragma alloc_text(PAGE, MiSessionRemoveImage)
  79. #pragma alloc_text(PAGE, MiSessionLookupImage)
  80. #pragma alloc_text(PAGE, MiSessionUnloadAllImages)
  81. #endif
  82. LOGICAL
  83. MiMarkControlAreaInSystemSpace (
  84. IN PCONTROL_AREA ControlArea
  85. )
  86. /*++
  87. Routine Description:
  88. Nonpaged wrapper to mark the argument control area properly.
  89. Arguments:
  90. ControlArea - Supplies a control area to mark as mapped in system space.
  91. Return Value:
  92. Returns TRUE if this was the first system space mapping of this
  93. control area. FALSE otherwise.
  94. Environment:
  95. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  96. --*/
  97. {
  98. KIRQL OldIrql;
  99. LOGICAL FirstMapped;
  100. FirstMapped = FALSE;
  101. LOCK_PFN (OldIrql);
  102. if (ControlArea->u.Flags.ImageMappedInSystemSpace == 0) {
  103. FirstMapped = TRUE;
  104. ControlArea->u.Flags.ImageMappedInSystemSpace = 1;
  105. }
  106. UNLOCK_PFN (OldIrql);
  107. return FirstMapped;
  108. }
  109. NTSTATUS
  110. MiShareSessionImage (
  111. IN PSECTION Section,
  112. IN OUT PSIZE_T ViewSize
  113. )
  114. /*++
  115. Routine Description:
  116. This routine maps the given image into the current session space.
  117. This allows the image to be executed backed by the image file in the
  118. filesystem and allow code and read-only data to be shared.
  119. Arguments:
  120. Section - Supplies a pointer to a section.
  121. ViewSize - Supplies the size in bytes of the view desired.
  122. Return Value:
  123. Returns STATUS_SUCCESS on success, various NTSTATUS codes on failure.
  124. Environment:
  125. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  126. --*/
  127. {
  128. KIRQL WsIrql;
  129. PSUBSECTION Subsection;
  130. PCONTROL_AREA ControlArea;
  131. ULONG NumberOfPtes;
  132. PMMPTE StartPte;
  133. PMMPTE EndPte;
  134. PVOID AllocationStart;
  135. SIZE_T AllocationSize;
  136. NTSTATUS Status;
  137. LOGICAL FirstMapped;
  138. PVOID MappedBase;
  139. SIZE_T CommittedPages;
  140. PIMAGE_ENTRY_IN_SESSION DriverImage;
  141. PAGED_CODE();
  142. SYSLOAD_LOCK_OWNED_BY_ME ();
  143. if (*ViewSize == 0) {
  144. return STATUS_SUCCESS;
  145. }
  146. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  147. MappedBase = Section->Segment->BasedAddress;
  148. ASSERT (((ULONG_PTR)MappedBase % PAGE_SIZE) == 0);
  149. ASSERT ((*ViewSize % PAGE_SIZE) == 0);
  150. //
  151. // Check to see if a purge operation is in progress and if so, wait
  152. // for the purge to complete. In addition, up the count of mapped
  153. // views for this control area.
  154. //
  155. ControlArea = Section->Segment->ControlArea;
  156. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  157. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  158. (ControlArea->u.Flags.Rom == 0)) {
  159. Subsection = (PSUBSECTION)(ControlArea + 1);
  160. }
  161. else {
  162. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  163. }
  164. if (MiCheckPurgeAndUpMapCount (ControlArea) == FALSE) {
  165. return STATUS_INSUFFICIENT_RESOURCES;
  166. }
  167. if (*ViewSize == 0) {
  168. *ViewSize = Section->SizeOfSection.LowPart;
  169. }
  170. else if (*ViewSize > Section->SizeOfSection.LowPart) {
  171. //
  172. // Section offset or view size past size of section.
  173. //
  174. MiDereferenceControlArea (ControlArea);
  175. return STATUS_INVALID_VIEW_SIZE;
  176. }
  177. AllocationStart = MappedBase;
  178. AllocationSize = *ViewSize;
  179. //
  180. // Calculate the PTE ranges and amount.
  181. //
  182. StartPte = MiGetPteAddress (AllocationStart);
  183. EndPte = MiGetPteAddress ((PCHAR)AllocationStart + AllocationSize);
  184. NumberOfPtes = BYTES_TO_PAGES (AllocationSize);
  185. Status = MiSessionWideGetImageSize (MappedBase, NULL, &CommittedPages);
  186. if (!NT_SUCCESS(Status)) {
  187. CommittedPages = NumberOfPtes;
  188. }
  189. if (MiChargeCommitment (CommittedPages, NULL) == FALSE) {
  190. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_COMMIT);
  191. //
  192. // Don't bother releasing the page tables or their commit here, another
  193. // load will happen shortly or the whole session will go away. On
  194. // session exit everything will be released automatically.
  195. //
  196. MiDereferenceControlArea (ControlArea);
  197. return STATUS_NO_MEMORY;
  198. }
  199. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  200. CommittedPages);
  201. LOCK_SESSION_SPACE_WS (WsIrql, PsGetCurrentThread ());
  202. //
  203. // Make sure we have page tables for the PTE
  204. // entries we must fill in the session space structure.
  205. //
  206. Status = MiSessionCommitPageTables (AllocationStart,
  207. (PVOID)((PCHAR)AllocationStart + AllocationSize));
  208. if (!NT_SUCCESS(Status)) {
  209. UNLOCK_SESSION_SPACE_WS (WsIrql);
  210. Status = STATUS_NO_MEMORY;
  211. bail:
  212. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  213. 0 - CommittedPages);
  214. MiDereferenceControlArea (ControlArea);
  215. MiReturnCommitment (CommittedPages);
  216. return Status;
  217. }
  218. #if DBG
  219. while (StartPte < EndPte) {
  220. ASSERT (StartPte->u.Long == 0);
  221. StartPte += 1;
  222. }
  223. StartPte = MiGetPteAddress (AllocationStart);
  224. #endif
  225. //
  226. // Flag that the image is mapped into system space.
  227. //
  228. if (Section->u.Flags.Image) {
  229. FirstMapped = MiMarkControlAreaInSystemSpace (ControlArea);
  230. //
  231. // Initialize all of the prototype PTEs as read only - later, copy
  232. // on write protections will be set on the actual PTEs mapping the
  233. // data (but not code) pages.
  234. //
  235. if (FirstMapped == TRUE) {
  236. MiSetImageProtect (Section->Segment, MM_EXECUTE_READ);
  237. }
  238. }
  239. //
  240. // Initialize the PTEs to point at the prototype PTEs.
  241. //
  242. Status = MiAddMappedPtes (StartPte, NumberOfPtes, ControlArea);
  243. UNLOCK_SESSION_SPACE_WS (WsIrql);
  244. if (!NT_SUCCESS (Status)) {
  245. //
  246. // Regardless of whether the PTEs were mapped, leave the control area
  247. // marked as mapped in system space so user applications cannot map the
  248. // file as an image as clearly the intent is to run it as a driver.
  249. //
  250. goto bail;
  251. }
  252. MM_TRACK_COMMIT (MM_DBG_COMMIT_SESSION_SHARED_IMAGE, CommittedPages);
  253. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_SYSMAPPED_PAGES_COMMITTED, (ULONG)CommittedPages);
  254. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_SYSMAPPED_PAGES_ALLOC, NumberOfPtes);
  255. //
  256. // No session space image faults may be taken until these fields of the
  257. // image entry are initialized.
  258. //
  259. DriverImage = MiSessionLookupImage (AllocationStart);
  260. ASSERT (DriverImage);
  261. DriverImage->LastAddress = (PVOID)((PCHAR)AllocationStart + AllocationSize - 1);
  262. DriverImage->PrototypePtes = Subsection->SubsectionBase;
  263. return STATUS_SUCCESS;
  264. }
  265. NTSTATUS
  266. MiSessionInsertImage (
  267. IN PVOID BaseAddress
  268. )
  269. /*++
  270. Routine Description:
  271. This routine allocates an image entry for the specified address in the
  272. current session space.
  273. Arguments:
  274. BaseAddress - Supplies the base address for the executable image.
  275. Return Value:
  276. STATUS_SUCCESS or various NTSTATUS error codes on failure.
  277. Environment:
  278. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  279. Note both the system load resource and the session working set
  280. mutex must be held to modify the list of images in this session.
  281. Either may be held to safely walk the list.
  282. --*/
  283. {
  284. KIRQL OldIrql;
  285. PLIST_ENTRY NextEntry;
  286. PIMAGE_ENTRY_IN_SESSION Image;
  287. PIMAGE_ENTRY_IN_SESSION NewImage;
  288. PAGED_CODE();
  289. SYSLOAD_LOCK_OWNED_BY_ME ();
  290. //
  291. // Create and initialize a new image entry prior to acquiring the session
  292. // space ws mutex. This is to reduce the amount of time the mutex is held.
  293. // If an existing entry is found this allocation is just discarded.
  294. //
  295. NewImage = ExAllocatePoolWithTag (NonPagedPool,
  296. sizeof(IMAGE_ENTRY_IN_SESSION),
  297. 'iHmM');
  298. if (NewImage == NULL) {
  299. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_NONPAGED_POOL);
  300. return STATUS_NO_MEMORY;
  301. }
  302. RtlZeroMemory (NewImage, sizeof(IMAGE_ENTRY_IN_SESSION));
  303. NewImage->Address = BaseAddress;
  304. NewImage->ImageCountInThisSession = 1;
  305. //
  306. // Check to see if the address is already loaded.
  307. //
  308. LOCK_SESSION_SPACE_WS (OldIrql, PsGetCurrentThread ());
  309. NextEntry = MmSessionSpace->ImageList.Flink;
  310. while (NextEntry != &MmSessionSpace->ImageList) {
  311. Image = CONTAINING_RECORD (NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  312. if (Image->Address == BaseAddress) {
  313. Image->ImageCountInThisSession += 1;
  314. UNLOCK_SESSION_SPACE_WS (OldIrql);
  315. ExFreePool (NewImage);
  316. return STATUS_ALREADY_COMMITTED;
  317. }
  318. NextEntry = NextEntry->Flink;
  319. }
  320. //
  321. // Insert the image entry into the session space structure.
  322. //
  323. InsertTailList (&MmSessionSpace->ImageList, &NewImage->Link);
  324. UNLOCK_SESSION_SPACE_WS (OldIrql);
  325. return STATUS_SUCCESS;
  326. }
  327. NTSTATUS
  328. MiSessionRemoveImage (
  329. PVOID BaseAddr
  330. )
  331. /*++
  332. Routine Description:
  333. This routine removes the given image entry from the current session space.
  334. Arguments:
  335. BaseAddress - Supplies the base address for the executable image.
  336. Return Value:
  337. Returns STATUS_SUCCESS on success, STATUS_NOT_FOUND if the image is not
  338. in the current session space.
  339. Environment:
  340. Kernel mode, APC_LEVEL and below.
  341. Note both the system load resource and the session working set
  342. mutex must be held to modify the list of images in this session.
  343. Either may be held to safely walk the list.
  344. --*/
  345. {
  346. PLIST_ENTRY NextEntry;
  347. PIMAGE_ENTRY_IN_SESSION Image;
  348. KIRQL OldIrql;
  349. PAGED_CODE();
  350. SYSLOAD_LOCK_OWNED_BY_ME ();
  351. LOCK_SESSION_SPACE_WS (OldIrql, PsGetCurrentThread ());
  352. NextEntry = MmSessionSpace->ImageList.Flink;
  353. while (NextEntry != &MmSessionSpace->ImageList) {
  354. Image = CONTAINING_RECORD(NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  355. if (Image->Address == BaseAddr) {
  356. RemoveEntryList (NextEntry);
  357. UNLOCK_SESSION_SPACE_WS (OldIrql);
  358. ExFreePool (Image);
  359. return STATUS_SUCCESS;
  360. }
  361. NextEntry = NextEntry->Flink;
  362. }
  363. UNLOCK_SESSION_SPACE_WS (OldIrql);
  364. return STATUS_NOT_FOUND;
  365. }
  366. PIMAGE_ENTRY_IN_SESSION
  367. MiSessionLookupImage (
  368. IN PVOID BaseAddress
  369. )
  370. /*++
  371. Routine Description:
  372. This routine looks up the image entry within the current session by the
  373. specified base address.
  374. Arguments:
  375. BaseAddress - Supplies the base address for the executable image.
  376. Return Value:
  377. The image entry within this session on success or NULL on failure.
  378. Environment:
  379. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  380. Note both the system load resource and the session working set
  381. mutex must be held to modify the list of images in this session.
  382. Either may be held to safely walk the list.
  383. --*/
  384. {
  385. PLIST_ENTRY NextEntry;
  386. PIMAGE_ENTRY_IN_SESSION Image;
  387. SYSLOAD_LOCK_OWNED_BY_ME ();
  388. NextEntry = MmSessionSpace->ImageList.Flink;
  389. while (NextEntry != &MmSessionSpace->ImageList) {
  390. Image = CONTAINING_RECORD(NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  391. if (Image->Address == BaseAddress) {
  392. return Image;
  393. }
  394. NextEntry = NextEntry->Flink;
  395. }
  396. return NULL;
  397. }
  398. VOID
  399. MiSessionUnloadAllImages (
  400. VOID
  401. )
  402. /*++
  403. Routine Description:
  404. This routine dereferences each image that has been loaded in the
  405. current session space.
  406. As each image is dereferenced, checks are made:
  407. If this session's reference count to the image reaches zero, the VA
  408. range in this session is deleted. If the reference count to the image
  409. in the SESSIONWIDE list drops to zero, then the SESSIONWIDE's VA
  410. reservation is removed and the address space is made available to any
  411. new image.
  412. If this is the last systemwide reference to the driver then the driver
  413. is deleted from memory.
  414. Arguments:
  415. None.
  416. Return Value:
  417. None.
  418. Environment:
  419. Kernel mode. This is called in one of two contexts:
  420. 1. the last thread in the last process of the current session space.
  421. 2. or by any thread in the SMSS process.
  422. Note both the system load resource and the session working set
  423. mutex must be held to modify the list of images in this session.
  424. Either may be held to safely walk the list.
  425. --*/
  426. {
  427. NTSTATUS Status;
  428. PLIST_ENTRY NextEntry;
  429. PIMAGE_ENTRY_IN_SESSION Module;
  430. PKLDR_DATA_TABLE_ENTRY ImageHandle;
  431. ASSERT (MmSessionSpace->ReferenceCount == 0);
  432. //
  433. // The session's working set lock does not need to be acquired here since
  434. // no thread can be faulting on these addresses.
  435. //
  436. NextEntry = MmSessionSpace->ImageList.Flink;
  437. while (NextEntry != &MmSessionSpace->ImageList) {
  438. Module = CONTAINING_RECORD(NextEntry, IMAGE_ENTRY_IN_SESSION, Link);
  439. //
  440. // Lookup the image entry in the system PsLoadedModuleList,
  441. // unload the image and delete it.
  442. //
  443. ImageHandle = MiLookupDataTableEntry (Module->Address, FALSE);
  444. ASSERT (ImageHandle);
  445. Status = MmUnloadSystemImage (ImageHandle);
  446. //
  447. // Restart the search at the beginning since the entry has been deleted.
  448. //
  449. ASSERT (MmSessionSpace->ReferenceCount == 0);
  450. NextEntry = MmSessionSpace->ImageList.Flink;
  451. }
  452. }
  453. VOID
  454. MiSessionWideInitializeAddresses (
  455. VOID
  456. )
  457. /*++
  458. Routine Description:
  459. This routine is called at system initialization to set up the group
  460. address list.
  461. Arguments:
  462. None.
  463. Return Value:
  464. None.
  465. Environment:
  466. Kernel mode.
  467. --*/
  468. {
  469. InitializeListHead (&MmSessionWideAddressList);
  470. }
  471. NTSTATUS
  472. MiSessionWideInsertImageAddress (
  473. IN PVOID BaseAddress,
  474. IN ULONG_PTR NumberOfBytes,
  475. IN ULONG WritablePages,
  476. IN PUNICODE_STRING ImageName,
  477. IN LOGICAL AtPreferredAddress
  478. )
  479. /*++
  480. Routine Description:
  481. Allocate and add a SessionWide Entry reference to the global address
  482. allocation list for the current process' session space.
  483. Arguments:
  484. BaseAddress - Supplies the base address to allocate an entry for.
  485. NumberOfBytes - Supplies the number of bytes the entry spans.
  486. WritablePages - Supplies the number of pages to charge commit for.
  487. ImageName - Supplies the name of the image in the PsLoadedModuleList
  488. that is represented by the virtual region.
  489. AtPreferredAddress - Supplies TRUE if the image is based at its preferred
  490. address.
  491. Return Value:
  492. Returns STATUS_SUCCESS on success, STATUS_NO_MEMORY on failure.
  493. Environment:
  494. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  495. --*/
  496. {
  497. PVOID LastAddress;
  498. PLIST_ENTRY NextEntry;
  499. PSESSIONWIDE_DRIVER_ADDRESS Vaddr;
  500. PSESSIONWIDE_DRIVER_ADDRESS New;
  501. PWCHAR NewName;
  502. SYSLOAD_LOCK_OWNED_BY_ME ();
  503. New = ExAllocatePoolWithTag (NonPagedPool,
  504. sizeof(SESSIONWIDE_DRIVER_ADDRESS),
  505. 'vHmM');
  506. if (New == NULL) {
  507. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_NONPAGED_POOL);
  508. return STATUS_NO_MEMORY;
  509. }
  510. RtlZeroMemory (New, sizeof(SESSIONWIDE_DRIVER_ADDRESS));
  511. New->ReferenceCount = 1;
  512. New->Address = BaseAddress;
  513. New->Size = NumberOfBytes;
  514. if (AtPreferredAddress == TRUE) {
  515. New->WritablePages = WritablePages;
  516. }
  517. else {
  518. New->WritablePages = (MI_ROUND_TO_SIZE (NumberOfBytes, PAGE_SIZE)) >> PAGE_SHIFT;
  519. }
  520. ASSERT (ImageName != NULL);
  521. NewName = (PWCHAR) ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  522. ImageName->Length + sizeof(UNICODE_NULL),
  523. 'nHmM');
  524. if (NewName == NULL) {
  525. ExFreePool (New);
  526. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_PAGED_POOL);
  527. return STATUS_NO_MEMORY;
  528. }
  529. RtlCopyMemory (NewName, ImageName->Buffer, ImageName->Length);
  530. NewName [ImageName->Length / sizeof(WCHAR)] = UNICODE_NULL;
  531. New->FullDllName.Buffer = NewName;
  532. New->FullDllName.Length = ImageName->Length;
  533. New->FullDllName.MaximumLength = ImageName->Length;
  534. //
  535. // Insert the entry in the memory-ordered list.
  536. //
  537. LastAddress = NULL;
  538. NextEntry = MmSessionWideAddressList.Flink;
  539. while (NextEntry != &MmSessionWideAddressList) {
  540. Vaddr = CONTAINING_RECORD (NextEntry,
  541. SESSIONWIDE_DRIVER_ADDRESS,
  542. Link);
  543. if (LastAddress < Vaddr->Address && Vaddr->Address > New->Address) {
  544. break;
  545. }
  546. LastAddress = Vaddr->Address;
  547. NextEntry = NextEntry->Flink;
  548. }
  549. InsertTailList (NextEntry, &New->Link);
  550. return STATUS_SUCCESS;
  551. }
  552. NTSTATUS
  553. MiSessionWideDereferenceImage (
  554. IN PVOID BaseAddress
  555. )
  556. /*++
  557. Routine Description:
  558. Dereference the SessionWide entry for the specified address, potentially
  559. resulting in a deletion of the image.
  560. Arguments:
  561. BaseAddress - Supplies the address for the driver to dereference.
  562. Return Value:
  563. Returns STATUS_SUCCESS on success, STATUS_NOT_FOUND on failure.
  564. Environment:
  565. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  566. --*/
  567. {
  568. PLIST_ENTRY NextEntry;
  569. PSESSIONWIDE_DRIVER_ADDRESS SessionWideImageEntry;
  570. SYSLOAD_LOCK_OWNED_BY_ME ();
  571. ASSERT (BaseAddress);
  572. NextEntry = MmSessionWideAddressList.Flink;
  573. while (NextEntry != &MmSessionWideAddressList) {
  574. SessionWideImageEntry = CONTAINING_RECORD (NextEntry,
  575. SESSIONWIDE_DRIVER_ADDRESS,
  576. Link);
  577. if (BaseAddress == SessionWideImageEntry->Address) {
  578. SessionWideImageEntry->ReferenceCount -= 1;
  579. //
  580. // If reference count is 0, delete the node.
  581. //
  582. if (SessionWideImageEntry->ReferenceCount == 0) {
  583. RemoveEntryList (NextEntry);
  584. ASSERT (SessionWideImageEntry->FullDllName.Buffer != NULL);
  585. ExFreePool (SessionWideImageEntry->FullDllName.Buffer);
  586. ExFreePool (SessionWideImageEntry);
  587. }
  588. return STATUS_SUCCESS;
  589. }
  590. NextEntry = NextEntry->Flink;
  591. }
  592. return STATUS_NOT_FOUND;
  593. }
  594. NTSTATUS
  595. MiSessionWideGetImageSize (
  596. IN PVOID BaseAddress,
  597. OUT PSIZE_T NumberOfBytes OPTIONAL,
  598. OUT PSIZE_T CommitPages OPTIONAL
  599. )
  600. /*++
  601. Routine Description:
  602. Lookup the size allocated and committed for the image at the base address.
  603. This ensures that we free every page that may have been allocated due
  604. to rounding up.
  605. Arguments:
  606. BaseAddress - Supplies the preferred address that the driver has
  607. been linked (rebased) at. If this address is available,
  608. the driver will require no relocation.
  609. NumberOfBytes - Supplies a pointer to store the image size into.
  610. CommitPages - Supplies a pointer to store the number of committed pages
  611. that were charged for this image.
  612. Return Value:
  613. Returns STATUS_SUCCESS on success, STATUS_NOT_FOUND on failure.
  614. Environment:
  615. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  616. --*/
  617. {
  618. PLIST_ENTRY NextEntry;
  619. PSESSIONWIDE_DRIVER_ADDRESS SessionWideEntry;
  620. SYSLOAD_LOCK_OWNED_BY_ME ();
  621. NextEntry = MmSessionWideAddressList.Flink;
  622. while (NextEntry != &MmSessionWideAddressList) {
  623. SessionWideEntry = CONTAINING_RECORD (NextEntry,
  624. SESSIONWIDE_DRIVER_ADDRESS,
  625. Link);
  626. if (BaseAddress == SessionWideEntry->Address) {
  627. if (ARGUMENT_PRESENT (NumberOfBytes)) {
  628. *NumberOfBytes = SessionWideEntry->Size;
  629. }
  630. if (ARGUMENT_PRESENT (CommitPages)) {
  631. *CommitPages = SessionWideEntry->WritablePages;
  632. }
  633. return STATUS_SUCCESS;
  634. }
  635. NextEntry = NextEntry->Flink;
  636. }
  637. return STATUS_NOT_FOUND;
  638. }
  639. NTSTATUS
  640. MiSessionWideReserveImageAddress (
  641. IN PUNICODE_STRING ImageName,
  642. IN PSECTION Section,
  643. IN ULONG_PTR Alignment,
  644. OUT PVOID *AssignedAddress,
  645. OUT PLOGICAL AlreadyLoaded
  646. )
  647. /*++
  648. Routine Description:
  649. This routine allocates a range of virtual address space within
  650. session space. This address range is unique system-wide and in this
  651. manner, code and pristine data of session drivers can be shared across
  652. multiple sessions.
  653. This routine does not actually commit pages, but reserves the virtual
  654. address region for the named image. An entry is created here and attached
  655. to the current session space to track the loaded image. Thus if all
  656. the references to a given range go away, the range can then be reused.
  657. Arguments:
  658. ImageName - Supplies the name of the driver that will be loaded into
  659. the allocated space.
  660. Section - Supplies the section (and thus, the preferred address that the
  661. driver has been linked (rebased) at. If this address is
  662. available, the driver will require no relocation. The section
  663. is also used to derive the number of bytes to reserve.
  664. Alignment - Supplies the virtual address alignment for the address.
  665. AssignedAddress - Supplies a pointer to a variable that receives the
  666. allocated address if the routine succeeds.
  667. AlreadyLoaded - Supplies a pointer to a variable that receives TRUE if the
  668. specified image name has already been loaded.
  669. Return Value:
  670. Returns STATUS_SUCCESS on success, various NTSTATUS codes on failure.
  671. Environment:
  672. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  673. --*/
  674. {
  675. PLIST_ENTRY NextEntry;
  676. PSESSIONWIDE_DRIVER_ADDRESS Vaddr;
  677. NTSTATUS Status;
  678. PWCHAR pName;
  679. PVOID NewAddress;
  680. ULONG_PTR AvailableAddress;
  681. ULONG_PTR SessionSpaceEnd;
  682. PVOID PreferredAddress;
  683. ULONG_PTR NumberOfBytes;
  684. ULONG WritablePages;
  685. LOGICAL AtPreferredAddress;
  686. PAGED_CODE();
  687. SYSLOAD_LOCK_OWNED_BY_ME ();
  688. ASSERT (PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION);
  689. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  690. pName = NULL;
  691. *AlreadyLoaded = FALSE;
  692. PreferredAddress = Section->Segment->BasedAddress;
  693. NumberOfBytes = Section->Segment->TotalNumberOfPtes << PAGE_SHIFT;
  694. AvailableAddress = MiSessionImageStart;
  695. NumberOfBytes = MI_ROUND_TO_SIZE (NumberOfBytes, Alignment);
  696. SessionSpaceEnd = MiSessionImageEnd;
  697. Status = MiGetWritablePagesInSection(Section, &WritablePages);
  698. if (!NT_SUCCESS(Status)) {
  699. WritablePages = Section->Segment->TotalNumberOfPtes;
  700. }
  701. //
  702. // If the requested address is not properly aligned or not in the session
  703. // space region, pick an address for it. This image will not be shared.
  704. //
  705. if ((ULONG_PTR)PreferredAddress & (Alignment - 1)) {
  706. #if DBG
  707. DbgPrint("MiSessionWideReserveImageAddress: Bad alignment 0x%x for PreferredAddress 0x%x\n",
  708. Alignment,
  709. PreferredAddress);
  710. #endif
  711. PreferredAddress = NULL;
  712. }
  713. else if ((ULONG_PTR)PreferredAddress < AvailableAddress ||
  714. ((ULONG_PTR)PreferredAddress + NumberOfBytes >= SessionSpaceEnd)) {
  715. #if DBG
  716. if (MmDebug & MM_DBG_SESSIONS) {
  717. DbgPrint ("MiSessionWideReserveImageAddress: PreferredAddress 0x%x not in session space\n", PreferredAddress);
  718. }
  719. #endif
  720. PreferredAddress = NULL;
  721. }
  722. //
  723. // Check the system wide session space image list to see if the
  724. // image name has already been given a slot.
  725. //
  726. NextEntry = MmSessionWideAddressList.Flink;
  727. while (NextEntry != &MmSessionWideAddressList) {
  728. Vaddr = CONTAINING_RECORD (NextEntry,
  729. SESSIONWIDE_DRIVER_ADDRESS,
  730. Link);
  731. if (Vaddr->FullDllName.Buffer != NULL) {
  732. if (RtlEqualUnicodeString(ImageName, &Vaddr->FullDllName, TRUE)) {
  733. //
  734. // The size requested should be the same.
  735. //
  736. if (Vaddr->Size < NumberOfBytes) {
  737. #if DBG
  738. DbgPrint ("MiSessionWideReserveImageAddress: Size %d Larger than Entry %d, DLL %wZ\n",
  739. NumberOfBytes,
  740. Vaddr->Size,
  741. ImageName);
  742. #endif
  743. return STATUS_CONFLICTING_ADDRESSES;
  744. }
  745. //
  746. // This image has already been loaded systemwide. If it's
  747. // already been loaded in this session space as well, just
  748. // bump the reference count using the already allocated
  749. // address. Otherwise, insert it into this session space.
  750. //
  751. Status = MiSessionInsertImage (Vaddr->Address);
  752. if (Status == STATUS_ALREADY_COMMITTED) {
  753. *AlreadyLoaded = TRUE;
  754. *AssignedAddress = Vaddr->Address;
  755. return STATUS_SUCCESS;
  756. }
  757. if (!NT_SUCCESS (Status)) {
  758. return Status;
  759. }
  760. //
  761. // Bump the reference count as this is a new entry.
  762. //
  763. Vaddr->ReferenceCount += 1;
  764. *AssignedAddress = Vaddr->Address;
  765. return STATUS_SUCCESS;
  766. }
  767. }
  768. //
  769. // Note this list must be sorted by ascending address.
  770. // See if the PreferredAddress and size collide with any entries.
  771. //
  772. if (PreferredAddress) {
  773. if ((PreferredAddress >= Vaddr->Address) &&
  774. (PreferredAddress < (PVOID)((ULONG_PTR)Vaddr->Address + Vaddr->Size))) {
  775. PreferredAddress = NULL;
  776. }
  777. else if ((PreferredAddress < Vaddr->Address) &&
  778. ((PVOID)((ULONG_PTR)PreferredAddress + NumberOfBytes) > Vaddr->Address)) {
  779. PreferredAddress = NULL;
  780. }
  781. }
  782. //
  783. // Check for an available general allocation slot.
  784. //
  785. if (((PVOID)AvailableAddress >= Vaddr->Address) &&
  786. (AvailableAddress <= (ULONG_PTR)Vaddr->Address + Vaddr->Size)) {
  787. AvailableAddress = (ULONG_PTR)Vaddr->Address + Vaddr->Size;
  788. if (AvailableAddress & (Alignment - 1)) {
  789. AvailableAddress = MI_ROUND_TO_SIZE (AvailableAddress, Alignment);
  790. }
  791. }
  792. else if (AvailableAddress + NumberOfBytes > (ULONG_PTR)Vaddr->Address) {
  793. AvailableAddress = (ULONG_PTR)Vaddr->Address + Vaddr->Size;
  794. if (AvailableAddress & (Alignment - 1)) {
  795. AvailableAddress = MI_ROUND_TO_SIZE (AvailableAddress, Alignment);
  796. }
  797. }
  798. NextEntry = NextEntry->Flink;
  799. }
  800. if ((PreferredAddress == NULL) &&
  801. (AvailableAddress + NumberOfBytes > MiSessionImageEnd)) {
  802. MM_BUMP_SESSION_FAILURES (MM_SESSION_FAILURE_NO_IMAGE_VA_SPACE);
  803. return STATUS_NO_MEMORY;
  804. }
  805. //
  806. // Try to put the module into its requested address so it can be shared.
  807. //
  808. if (PreferredAddress) {
  809. #if DBG
  810. if (MmDebug & MM_DBG_SESSIONS) {
  811. DbgPrint ("MiSessionWideReserveImageAddress: Code Sharing on %wZ, Address 0x%x\n",ImageName,PreferredAddress);
  812. }
  813. #endif
  814. NewAddress = PreferredAddress;
  815. }
  816. else {
  817. ASSERT (AvailableAddress != 0);
  818. ASSERT ((AvailableAddress & (Alignment - 1)) == 0);
  819. #if DBG
  820. DbgPrint ("MiSessionWideReserveImageAddress: NO Code Sharing on %wZ, Address 0x%x\n",ImageName,AvailableAddress);
  821. #endif
  822. NewAddress = (PVOID)AvailableAddress;
  823. }
  824. //
  825. // Create a new node entry for the address range.
  826. //
  827. if (NewAddress == PreferredAddress) {
  828. AtPreferredAddress = TRUE;
  829. }
  830. else {
  831. AtPreferredAddress = FALSE;
  832. }
  833. Status = MiSessionWideInsertImageAddress (NewAddress,
  834. NumberOfBytes,
  835. WritablePages,
  836. ImageName,
  837. AtPreferredAddress);
  838. if (!NT_SUCCESS(Status)) {
  839. return Status;
  840. }
  841. //
  842. // Create an entry for this image in the current session space.
  843. //
  844. Status = MiSessionInsertImage (NewAddress);
  845. if (!NT_SUCCESS(Status)) {
  846. MiSessionWideDereferenceImage (NewAddress);
  847. return Status;
  848. }
  849. *AssignedAddress = NewAddress;
  850. return STATUS_SUCCESS;
  851. }
  852. NTSTATUS
  853. MiRemoveImageSessionWide (
  854. IN PVOID BaseAddress
  855. )
  856. /*++
  857. Routine Description:
  858. Delete the image space region from the current session space.
  859. This dereferences the globally allocated SessionWide region.
  860. The SessionWide region will be deleted if the reference count goes to zero.
  861. Arguments:
  862. BaseAddress - Supplies the address the driver is loaded at.
  863. Return Value:
  864. Returns STATUS_SUCCESS on success, STATUS_NOT_FOUND on failure.
  865. Environment:
  866. Kernel mode, APC_LEVEL and below, MmSystemLoadLock held.
  867. --*/
  868. {
  869. NTSTATUS Status;
  870. PAGED_CODE();
  871. SYSLOAD_LOCK_OWNED_BY_ME ();
  872. ASSERT (MmIsAddressValid(MmSessionSpace) == TRUE);
  873. Status = MiSessionWideDereferenceImage (BaseAddress);
  874. ASSERT (NT_SUCCESS(Status));
  875. //
  876. // Remove the image reference from the current session space.
  877. //
  878. MiSessionRemoveImage (BaseAddress);
  879. return Status;
  880. }