Leaked source code of windows server 2003
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.

603 lines
19 KiB

  1. /*========================================================================== *
  2. *
  3. * Copyright (C) 1994-1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: ddagp.c
  6. * Content: Functions for dealing with AGP memory in DirectDraw
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 18-jan-97 colinmc initial implementation
  12. * 13-mar-97 colinmc Bug 6533: Pass uncached flag to VMM correctly
  13. * 07-may-97 colinmc Add support for AGP on OSR 2.1
  14. * 12-Feb-98 DrewB Split into common, Win9x and NT sections.
  15. *
  16. ***************************************************************************/
  17. #include "precomp.hxx"
  18. #ifndef PAGE_SIZE
  19. #define PAGE_SIZE 4096
  20. #endif
  21. #define PAGE_COUNT(Bytes) (((Bytes) + (PAGE_SIZE - 1)) / PAGE_SIZE)
  22. #define PAGE_ROUND(Bytes) (((Bytes) + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1))
  23. //
  24. // AGP memory policy parameters.
  25. //
  26. // Maximum amount of AGP memory to use. Currently 32MB.
  27. // Recomputed when the DirectDraw interface is created.
  28. DWORD dwAGPPolicyMaxBytes = 32 * 1024 * 1024;
  29. // Amount of memory to commit when a commit is needed.
  30. // Reset when the DirectDraw interface is created.
  31. DWORD dwAGPPolicyCommitDelta = DEFAULT_AGP_COMMIT_DELTA;
  32. DWORD AGPReserve( HANDLE hdev, DWORD dwSize, BOOL fIsUC, BOOL fIsWC,
  33. FLATPTR *pfpLinStart, LARGE_INTEGER *pliDevStart,
  34. PVOID *ppvReservation )
  35. {
  36. DWORD dwNumPages;
  37. DDASSERT( INVALID_HANDLE_VALUE != hdev );
  38. DDASSERT( 0UL != dwSize );
  39. DDASSERT( NULL != pfpLinStart );
  40. DDASSERT( NULL != pliDevStart );
  41. DDASSERT( NULL != ppvReservation );
  42. /*
  43. * The first thing to do is make sure our AGP policy is respected.
  44. * The easy way to do that is to limit how much we reserve...
  45. */
  46. dwSize = min(dwSize, dwAGPPolicyMaxBytes);
  47. /*
  48. * DDraw will attempt to reserve space for the heap, but if that fails,
  49. * we'll ratchet down the reservation by 4 megs at a time until it works.
  50. * This is a defensive move that should prevent a few problems for AGP
  51. * aware drivers on memphis: they cannot know how large an aperture to
  52. * claim (cuz of weird OS restraints like the fact that
  53. * half the ap is reserved for UC and the other half WC etc, plus
  54. * random BIOS limitations.
  55. * We arbitrarily decide that 4 megs is the legal minimum.
  56. */
  57. while (dwSize >= 0x400000 )
  58. {
  59. dwNumPages = PAGE_COUNT(dwSize);
  60. if ( OsAGPReserve( hdev, dwNumPages, fIsUC, fIsWC,
  61. pfpLinStart, pliDevStart, ppvReservation ) )
  62. {
  63. return dwSize;
  64. }
  65. /*
  66. * If the driver asked for WC but the processor doesn't support WC,
  67. * then OsAGPReserve will have failed. The best thing to do is try
  68. * again with UC...
  69. * If the aperture size is the problem, then this will still fail
  70. * and we'll back off and try again WC.
  71. */
  72. if (fIsWC)
  73. {
  74. if ( OsAGPReserve( hdev, dwNumPages, TRUE, FALSE,
  75. pfpLinStart, pliDevStart, ppvReservation ) )
  76. {
  77. return dwSize;
  78. }
  79. }
  80. /*
  81. * Subtract 4 megs and try again
  82. */
  83. dwSize -= 0x400000;
  84. }
  85. return 0;
  86. } /* AGPReserve */
  87. #define IS_CHUNK_COMMITTED(x,y) \
  88. ((x)[(y)/BITS_IN_BYTE] & (1 << ((y) % BITS_IN_BYTE)))
  89. #define MARK_CHUNK_COMMITTED(x,y) \
  90. (x)[(y)/BITS_IN_BYTE] |= (1 << ((y) % BITS_IN_BYTE))
  91. /*
  92. * Initially we implemented this where each page had a bit indicating whether it
  93. * was committed or not, but we decided to change it so that each bit indicates
  94. * whether 16 pages are mapped or not for the following reasons:
  95. *
  96. * 1. In AGPCommitVirtual, we walk the entire mask everytime we create a
  97. * surface. For performance reasons, we want to keep this mask small.
  98. *
  99. * 2. The miniport always virtually commits everything in 16 page chunks and
  100. * physically commits everything in 64 page chunks regardless of what we
  101. * pass to them, so there is no real reason for page granularity.
  102. */
  103. // x = byte offset
  104. #define CHUNK_INDEX(x) \
  105. ((x) / (DDLOCAL_AGP_MAPPING_PAGES * PAGE_SIZE))
  106. #define PAGE_FROM_CHUNK(x) \
  107. ((x) * DDLOCAL_AGP_MAPPING_PAGES)
  108. #define NUM_CHUNKS(x) \
  109. ((PAGE_COUNT(x) + (DDLOCAL_AGP_MAPPING_PAGES - 1)) / DDLOCAL_AGP_MAPPING_PAGES)
  110. // x = start chunk
  111. // y = chunk past the end (not included in the total)
  112. // z = heap size in bytes
  113. #define NUM_PAGES_FROM_CHUNK(x,y,z) \
  114. (((y) == NUM_CHUNKS(z)) ? \
  115. (PAGE_COUNT(z) - PAGE_FROM_CHUNK(x)) : \
  116. (((y) - (x)) * DDLOCAL_AGP_MAPPING_PAGES))
  117. BOOL AGPCommit( HANDLE hdev, PVOID pvReservation,
  118. DWORD dwOffset, DWORD dwSize,
  119. BYTE* pAgpCommitMask,
  120. DWORD* pdwCommittedSize,
  121. DWORD dwHeapSize)
  122. {
  123. DWORD dwChunk;
  124. DWORD dwLastChunk;
  125. BOOL bRet = TRUE;
  126. DDASSERT( INVALID_HANDLE_VALUE != hdev );
  127. DDASSERT( NULL != pvReservation );
  128. DDASSERT( 0UL != dwSize );
  129. *pdwCommittedSize = 0;
  130. if( pAgpCommitMask == NULL )
  131. {
  132. return FALSE;
  133. }
  134. dwChunk = CHUNK_INDEX(dwOffset);
  135. dwLastChunk = CHUNK_INDEX(dwOffset + dwSize - 1);
  136. ASSERTGDI((dwOffset + dwSize <= dwHeapSize),
  137. "Attempting to allocate beyond the heap size");
  138. /*
  139. * Now walk through all of the 16 page chunks and determine if they are
  140. * already committed, and if not, commit them.
  141. */
  142. while( ( dwChunk <= dwLastChunk ) && bRet )
  143. {
  144. if( IS_CHUNK_COMMITTED( pAgpCommitMask,dwChunk ) )
  145. {
  146. // Chunk is already committed.
  147. dwChunk++;
  148. }
  149. else
  150. {
  151. DWORD dwEndChunk;
  152. // The page is not already committed, so figure out how many
  153. // non-committed pages we have and we will commit them all at once.
  154. dwEndChunk = dwChunk + 1;
  155. while( ( dwEndChunk <= dwLastChunk ) &&
  156. !IS_CHUNK_COMMITTED( pAgpCommitMask, dwEndChunk ) )
  157. {
  158. dwEndChunk++;
  159. }
  160. bRet = OsAGPCommit( hdev,
  161. pvReservation,
  162. PAGE_FROM_CHUNK(dwChunk),
  163. NUM_PAGES_FROM_CHUNK(dwChunk, dwEndChunk, dwHeapSize));
  164. if( bRet )
  165. {
  166. // If we succeeded, we need to mark the pages as being committed
  167. *pdwCommittedSize += ((dwEndChunk - dwChunk) *
  168. DDLOCAL_AGP_MAPPING_PAGES * PAGE_SIZE);
  169. while( dwChunk < dwEndChunk )
  170. {
  171. MARK_CHUNK_COMMITTED( pAgpCommitMask, dwChunk );
  172. dwChunk++;
  173. }
  174. }
  175. }
  176. }
  177. return bRet;
  178. } /* AGPCommit */
  179. VOID AGPUpdateCommitMask( BYTE* pAgpCommitMask, DWORD dwOffset, DWORD dwSize, DWORD dwHeapSize )
  180. {
  181. DWORD dwChunk;
  182. DWORD dwLastChunk;
  183. dwChunk = CHUNK_INDEX(dwOffset);
  184. dwLastChunk = CHUNK_INDEX(dwOffset + dwSize - 1);
  185. ASSERTGDI((dwOffset + dwSize <= dwHeapSize),
  186. "Attempting to allocate beyond the heap size");
  187. /*
  188. * Now set all of the bits indicating that they are committed.
  189. */
  190. while( dwChunk <= dwLastChunk )
  191. {
  192. MARK_CHUNK_COMMITTED( pAgpCommitMask, dwChunk );
  193. dwChunk++;
  194. }
  195. }
  196. BOOL AGPDecommitAll( HANDLE hdev, PVOID pvReservation,
  197. BYTE* pAgpCommitMask, DWORD dwAgpCommitMaskSize,
  198. DWORD* pdwDecommittedSize,
  199. DWORD dwHeapSize)
  200. {
  201. DWORD dwNumChunks;
  202. DWORD dwChunk;
  203. DDASSERT( INVALID_HANDLE_VALUE != hdev );
  204. DDASSERT( 0UL != pvReservation );
  205. *pdwDecommittedSize = 0;
  206. /*
  207. * Walk the mask and decomit all of the previously committed chunks of
  208. * pages. Do not decommit them one by one as this is fairly slow.
  209. */
  210. dwNumChunks = NUM_CHUNKS(dwHeapSize);
  211. dwChunk = 0;
  212. while( dwChunk < dwNumChunks )
  213. {
  214. if( !IS_CHUNK_COMMITTED( pAgpCommitMask, dwChunk ) )
  215. {
  216. // Page is not committed.
  217. dwChunk++;
  218. }
  219. else
  220. {
  221. DWORD dwEndChunk;
  222. // We are at the start of a block of committed chunks, so figure
  223. // out how many chunks are in this block and decommit them all.
  224. dwEndChunk = dwChunk + 1;
  225. while( ( dwEndChunk < dwNumChunks ) &&
  226. IS_CHUNK_COMMITTED( pAgpCommitMask, dwEndChunk ) )
  227. {
  228. dwEndChunk++;
  229. }
  230. OsAGPDecommit( hdev, pvReservation,
  231. PAGE_FROM_CHUNK(dwChunk),
  232. NUM_PAGES_FROM_CHUNK(dwChunk, dwEndChunk, dwHeapSize));
  233. *pdwDecommittedSize += ((dwEndChunk - dwChunk) *
  234. DDLOCAL_AGP_MAPPING_PAGES * PAGE_SIZE);
  235. dwChunk = dwEndChunk;
  236. }
  237. }
  238. RtlZeroMemory( pAgpCommitMask, dwAgpCommitMaskSize );
  239. return TRUE;
  240. } /* AGPDecommitAll */
  241. BOOL AGPFree( HANDLE hdev, PVOID pvReservation )
  242. {
  243. DDASSERT( INVALID_HANDLE_VALUE != hdev );
  244. DDASSERT( 0UL != pvReservation );
  245. return OsAGPFree( hdev, pvReservation );
  246. } /* AGPFree */
  247. DWORD AGPGetChunkCount( DWORD dwSize )
  248. {
  249. return NUM_CHUNKS(dwSize);
  250. }
  251. BOOL AGPCommitAllVirtual( EDD_DIRECTDRAW_LOCAL* peDirectDrawLocal, VIDEOMEMORY* lpVidMem, int iHeapIndex)
  252. {
  253. DWORD i, j;
  254. DWORD dwNumChunks;
  255. BOOL bSuccess = TRUE;
  256. BYTE* pPhysicalCommitMask;
  257. BYTE* pVirtualCommitMask;
  258. EDD_DIRECTDRAW_GLOBAL* peDirectDrawGlobal;
  259. EDD_VMEMMAPPING* peMap;
  260. peDirectDrawGlobal = peDirectDrawLocal->peDirectDrawGlobal;
  261. dwNumChunks = NUM_CHUNKS(lpVidMem->lpHeap->dwTotalSize);
  262. pPhysicalCommitMask = lpVidMem->lpHeap->pAgpCommitMask;
  263. if (peDirectDrawLocal->ppeMapAgp != NULL)
  264. {
  265. peMap = peDirectDrawLocal->ppeMapAgp[iHeapIndex];
  266. if (peMap != NULL)
  267. {
  268. pVirtualCommitMask = peMap->pAgpVirtualCommitMask;
  269. for (i = 0; (i < dwNumChunks) && bSuccess; i++)
  270. {
  271. if (IS_CHUNK_COMMITTED(pPhysicalCommitMask, i) &&
  272. !IS_CHUNK_COMMITTED(pVirtualCommitMask, i))
  273. {
  274. // We have found a page that needs to be committed.
  275. // Now find the last page in the block to commit.
  276. for (j = i + 1; j < dwNumChunks; j++)
  277. {
  278. if (!IS_CHUNK_COMMITTED(pPhysicalCommitMask,j) ||
  279. IS_CHUNK_COMMITTED(pVirtualCommitMask,j))
  280. {
  281. break;
  282. }
  283. }
  284. if (peDirectDrawGlobal->AgpInterface.AgpServices.
  285. AgpCommitVirtual(peDirectDrawGlobal->AgpInterface.Context,
  286. peMap->pvReservation,
  287. NUM_PAGES_FROM_CHUNK(i, j, lpVidMem->lpHeap->dwTotalSize),
  288. PAGE_FROM_CHUNK(i)) == NULL)
  289. {
  290. bSuccess = FALSE;
  291. }
  292. else
  293. {
  294. // If we succeeded in committing the block, then mark the pages
  295. // as committed.
  296. while (i < j)
  297. {
  298. MARK_CHUNK_COMMITTED(pVirtualCommitMask,i);
  299. i++;
  300. }
  301. }
  302. }
  303. }
  304. }
  305. }
  306. else
  307. {
  308. bSuccess = FALSE;
  309. }
  310. return bSuccess;
  311. }
  312. BOOL AGPCommitVirtual( EDD_DIRECTDRAW_LOCAL* peDirectDrawLocal,
  313. VIDEOMEMORY* lpVidMem,
  314. int iHeapIndex,
  315. DWORD dwOffset,
  316. DWORD dwSize )
  317. {
  318. DWORD i, j;
  319. DWORD dwNumChunks;
  320. BYTE* pVirtualCommitMask;
  321. EDD_DIRECTDRAW_GLOBAL* peDirectDrawGlobal;
  322. EDD_VMEMMAPPING* peMap;
  323. BOOL bSuccess = TRUE;
  324. peDirectDrawGlobal = peDirectDrawLocal->peDirectDrawGlobal;
  325. if (peDirectDrawLocal->ppeMapAgp != NULL)
  326. {
  327. peMap = peDirectDrawLocal->ppeMapAgp[iHeapIndex];
  328. if (peMap != NULL)
  329. {
  330. DWORD dwChunk = CHUNK_INDEX(dwOffset);
  331. DWORD dwLastChunk = CHUNK_INDEX(dwOffset + dwSize - 1);
  332. pVirtualCommitMask = peMap->pAgpVirtualCommitMask;
  333. if (peDirectDrawGlobal->AgpInterface.AgpServices.
  334. AgpCommitVirtual(peDirectDrawGlobal->AgpInterface.Context,
  335. peMap->pvReservation,
  336. NUM_PAGES_FROM_CHUNK(dwChunk, dwLastChunk + 1, lpVidMem->lpHeap->dwTotalSize),
  337. PAGE_FROM_CHUNK(dwChunk)) != NULL)
  338. {
  339. while( dwChunk <= dwLastChunk )
  340. {
  341. MARK_CHUNK_COMMITTED( pVirtualCommitMask, dwChunk );
  342. dwChunk++;
  343. }
  344. }
  345. else
  346. {
  347. bSuccess = FALSE;
  348. }
  349. }
  350. }
  351. return bSuccess;
  352. }
  353. BOOL AGPDecommitVirtual( EDD_VMEMMAPPING* peMap,
  354. EDD_DIRECTDRAW_GLOBAL* peDirectDrawGlobal,
  355. EDD_DIRECTDRAW_LOCAL* peDirectDrawLocal,
  356. DWORD dwHeapSize)
  357. {
  358. DWORD i, j;
  359. DWORD dwNumChunks;
  360. BYTE* pVirtualCommitMask;
  361. DWORD dwCommitMaskSize;
  362. pVirtualCommitMask = peMap->pAgpVirtualCommitMask;
  363. dwCommitMaskSize = peMap->dwAgpVirtualCommitMaskSize;
  364. dwNumChunks = NUM_CHUNKS(dwHeapSize);
  365. for (i = 0; i < dwNumChunks; i++)
  366. {
  367. if (IS_CHUNK_COMMITTED(pVirtualCommitMask,i))
  368. {
  369. // We have found a chunk that needs to be decommitted.
  370. // Now find the last chunk in the block to commit.
  371. for (j = i + 1; j < dwNumChunks; j++)
  372. {
  373. if (!IS_CHUNK_COMMITTED(pVirtualCommitMask,j))
  374. {
  375. break;
  376. }
  377. }
  378. peDirectDrawGlobal->AgpInterface.AgpServices.
  379. AgpFreeVirtual(peDirectDrawGlobal->AgpInterface.Context,
  380. peMap->pvReservation,
  381. NUM_PAGES_FROM_CHUNK(i, j, dwHeapSize),
  382. PAGE_FROM_CHUNK(i));
  383. i = j;
  384. }
  385. }
  386. RtlZeroMemory( pVirtualCommitMask, dwCommitMaskSize );
  387. return TRUE;
  388. }
  389. NTSTATUS AGPMapToDummy( EDD_VMEMMAPPING* peMap,
  390. EDD_DIRECTDRAW_GLOBAL* peDirectDrawGlobal,
  391. PVOID pDummyPage )
  392. {
  393. BYTE* pVirtualCommitMask;
  394. ULONG ulOffs;
  395. VOID* pvVirtAddr;
  396. NTSTATUS Status = STATUS_SUCCESS;
  397. DWORD dwNumChunks;
  398. DWORD i;
  399. DWORD j;
  400. pVirtualCommitMask = peMap->pAgpVirtualCommitMask;
  401. dwNumChunks = NUM_CHUNKS(peDirectDrawGlobal->pvmList[peMap->iHeapIndex].lpHeap->dwTotalSize);
  402. for (i = 0; i < dwNumChunks; i++)
  403. {
  404. // This takes a little bit of explaining. Even though AGPCommitVirtual
  405. // passes in as many pages as it wants to get mapped at any given time,
  406. // videoport.sys always breaks these down and maps them in 16 page
  407. // chunks. Therefore, we need to re-amp them in 16 page chunks in order
  408. // for MmMapUserAddress to work correctly.
  409. if (IS_CHUNK_COMMITTED(pVirtualCommitMask,i))
  410. {
  411. pvVirtAddr = (VOID*)((PAGE_FROM_CHUNK(i) * PAGE_SIZE) + (ULONG_PTR) peMap->pvVirtAddr);
  412. Status = MmMapUserAddressesToPage(
  413. pvVirtAddr, 0, gpDummyPage);
  414. }
  415. if (!NT_SUCCESS(Status))
  416. {
  417. break;
  418. }
  419. }
  420. return Status;
  421. }
  422. #ifndef __NTDDKCOMP__
  423. #define OSR2_POINT_0_BUILD_NUMBER 1111
  424. #define OSR2_BUILD_NUMBER_A 1212
  425. #define OSR2_BUILD_NUMBER_B 1214
  426. /*
  427. * Does this operating system understand AGP?
  428. *
  429. * NOTE: There may be a better way of determining this but for now I will
  430. * assumed that Memphis and NT 5.0 class operating systems are AGP aware.
  431. *
  432. * NOTE: The VXD handle is (obviously) only important on Win95. On NT
  433. * NULL should be passed.
  434. */
  435. BOOL OSIsAGPAware( HANDLE hvxd )
  436. {
  437. OSVERSIONINFO osvi;
  438. BOOL success;
  439. BOOL fIsVMMAGPAware;
  440. ZeroMemory(&osvi, sizeof(osvi));
  441. osvi.dwOSVersionInfoSize = sizeof(osvi);
  442. success = GetVersionEx(&osvi);
  443. DDASSERT( success );
  444. if( VER_PLATFORM_WIN32_WINDOWS == osvi.dwPlatformId )
  445. {
  446. DPF( 8, "Major version = %d", osvi.dwMajorVersion );
  447. DPF( 8, "Minor version = %d", osvi.dwMinorVersion );
  448. DPF( 8, "Build number = %d", LOWORD(osvi.dwBuildNumber) );
  449. if( ( osvi.dwMajorVersion > 4UL ) ||
  450. ( ( osvi.dwMajorVersion == 4UL ) &&
  451. ( osvi.dwMinorVersion >= 10UL ) &&
  452. ( LOWORD( osvi.dwBuildNumber ) >= 1373 ) ) )
  453. {
  454. /*
  455. * Memphis or greater version of Win95. AGP support assumed.
  456. */
  457. DPF( 5, "AGP aware Windows95 detected. Enabling AGP" );
  458. return TRUE;
  459. }
  460. else if( ( osvi.dwMajorVersion == 4UL ) &&
  461. ( osvi.dwMinorVersion == 0UL ) &&
  462. ( ( LOWORD( osvi.dwBuildNumber ) == OSR2_BUILD_NUMBER_A ) ||
  463. ( LOWORD( osvi.dwBuildNumber ) == OSR2_BUILD_NUMBER_B ) ||
  464. ( LOWORD( osvi.dwBuildNumber ) == OSR2_POINT_0_BUILD_NUMBER ) ) )
  465. {
  466. DPF( 5, "Win95 OSR 2.1 detected. Checking VMM for AGP services" );
  467. fIsVMMAGPAware = FALSE;
  468. #ifdef WIN95
  469. DDASSERT( INVALID_HANDLE_VALUE != hvxd );
  470. fIsVMMAGPAware = vxdIsVMMAGPAware( hvxd );
  471. #else /* WIN95 */
  472. /*
  473. * Should never occur as this would mean we are running an NT
  474. * binary on a 95 system.
  475. */
  476. DDASSERT(FALSE);
  477. #endif /* WIN95 */
  478. if( fIsVMMAGPAware )
  479. {
  480. /*
  481. * AGP services are present in the VMM. Enable AGP.
  482. */
  483. DPF( 5, "OSR 2.1 VMM has AGP services. Enabled AGP" );
  484. return TRUE;
  485. }
  486. else
  487. {
  488. /*
  489. * No AGP services. Disable AGP.
  490. */
  491. DPF( 5, "OSR 2.1 VMM has no AGP services. AGP not available" );
  492. return FALSE;
  493. }
  494. }
  495. else
  496. {
  497. DPF( 5, "Win95 Gold, OSR 1.0 or OSR 2.0 detected. No AGP support available" );
  498. return FALSE;
  499. }
  500. }
  501. else if( VER_PLATFORM_WIN32_NT == osvi.dwPlatformId )
  502. {
  503. /*
  504. * AGP support assumed in NT 5.0 and above.
  505. */
  506. if( osvi.dwMajorVersion >= 5UL )
  507. {
  508. DPF( 4, "AGP aware WindowsNT detected. Enabling AGP" );
  509. return TRUE;
  510. }
  511. }
  512. /*
  513. * If we got to here we failed the AGP aware test.
  514. */
  515. DPF( 5, "Operating system is not AGP aware. Disabling AGP" );
  516. return FALSE;
  517. } /* OSIsAGPAware */
  518. #endif // __NTDDKCOMP__