Subversion Repositories Kolibri OS

Rev

Rev 4424 | Rev 4619 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2004-2014. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 4608 $
  9.  
  10. ; Initializes MTRRs.
  11. proc init_mtrr
  12.  
  13.         cmp     [BOOT_VARS+BOOT_MTRR], byte 2
  14.         je      .exit
  15.  
  16.         bt      [cpu_caps], CAPS_MTRR
  17.         jnc     .exit
  18.  
  19.         call    mtrr_reconfigure
  20.         stdcall set_mtrr, [LFBAddress], 0x1000000, MEM_WC
  21.  
  22. .exit:
  23.         ret
  24. endp
  25.  
  26. ; Helper procedure for mtrr_reconfigure and set_mtrr,
  27. ; called before changes in MTRRs.
  28. proc mtrr_begin_change
  29.         mov     eax, cr0
  30.         or      eax, 0x60000000 ;disable caching
  31.         mov     cr0, eax
  32.         wbinvd                  ;invalidate cache
  33.         ret
  34. endp
  35.  
  36. ; Helper procedure for mtrr_reconfigure and set_mtrr,
  37. ; called after changes in MTRRs.
  38. proc mtrr_end_change
  39.         wbinvd                  ;again invalidate
  40.         mov     eax, cr0
  41.         and     eax, not 0x60000000
  42.         mov     cr0, eax        ; enable caching
  43.         ret
  44. endp
  45.  
  46. ; Some limits to number of structures located in the stack.
  47. MAX_USEFUL_MTRRS = 16
  48. MAX_RANGES = 16
  49.  
  50. ; mtrr_reconfigure keeps a list of MEM_WB ranges.
  51. ; This structure describes one item in the list.
  52. struct mtrr_range
  53. next            dd      ?       ; next item
  54. start           dq      ?       ; first byte
  55. length          dq      ?       ; length in bytes
  56. ends
  57.  
  58. uglobal
  59. align 4
  60. num_variable_mtrrs      dd      0       ; number of variable-range MTRRs
  61. endg
  62.  
  63. ; Helper procedure for MTRR initialization.
  64. ; Takes MTRR configured by BIOS and tries to recongifure them
  65. ; in order to allow non-UC data at top of 4G memory.
  66. ; Example: if low part of physical memory is 3.5G = 0xE0000000 bytes wide,
  67. ; BIOS can configure two MTRRs so that the first MTRR describes [0, 4G) as WB
  68. ; and the second MTRR describes [3.5G, 4G) as UC;
  69. ; WB+UC=UC, so the resulting memory map would be as needed,
  70. ; but in this configuration our attempts to map LFB at (say) 0xE8000000 as WC
  71. ; would be ignored, WB+UC+WC is still UC.
  72. ; So we must keep top of 4G memory not covered by MTRRs,
  73. ; using three WB MTRRs [0,2G) + [2G,3G) + [3G,3.5G),
  74. ; this gives the same memory map, but allows to add further entries.
  75. ; See mtrrtest.asm for detailed input/output from real hardware+BIOS.
  76. proc mtrr_reconfigure
  77.         push    ebp     ; we're called from init_LFB, and it feels hurt when ebp is destroyed
  78. ; 1. Prepare local variables.
  79. ; 1a. Create list of MAX_RANGES free (aka not yet allocated) ranges.
  80.         xor     eax, eax
  81.         lea     ecx, [eax+MAX_RANGES]
  82. .init_ranges:
  83.         sub     esp, sizeof.mtrr_range - 4
  84.         push    eax
  85.         mov     eax, esp
  86.         dec     ecx
  87.         jnz     .init_ranges
  88.         mov     eax, esp
  89. ; 1b. Fill individual local variables.
  90.         xor     edx, edx
  91.         sub     esp, MAX_USEFUL_MTRRS * 16      ; .mtrrs
  92.         push    edx             ; .mtrrs_end
  93.         push    edx             ; .num_used_mtrrs
  94.         push    eax             ; .first_free_range
  95.         push    edx             ; .first_range: no ranges yet
  96.         mov     cl, [cpu_phys_addr_width]
  97.         or      eax, -1
  98.         shl     eax, cl ; note: this uses cl&31 = cl-32, not the entire cl
  99.         push    eax     ; .phys_reserved_mask
  100. virtual at esp
  101. .phys_reserved_mask     dd      ?
  102. .first_range            dd      ?
  103. .first_free_range       dd      ?
  104. .num_used_mtrrs         dd      ?
  105. .mtrrs_end              dd      ?
  106. .mtrrs          rq      MAX_USEFUL_MTRRS * 2
  107. .local_vars_size = $ - esp
  108. end virtual
  109.  
  110. ; 2. Get the number of variable-range MTRRs from MTRRCAP register.
  111. ; Abort if zero.
  112.         mov     ecx, 0xFE
  113.         rdmsr
  114.         test    al, al
  115.         jz      .abort
  116.         mov     byte [num_variable_mtrrs], al
  117. ; 3. Validate MTRR_DEF_TYPE register.
  118.         mov     ecx, 0x2FF
  119.         rdmsr
  120. ; If BIOS has not initialized variable-range MTRRs, fallback to step 7.
  121.         test    ah, 8
  122.         jz      .fill_ranges_from_memory_map
  123. ; If the default memory type (not covered by MTRRs) is not UC,
  124. ; then probably BIOS did something strange, so it is better to exit immediately
  125. ; hoping for the best.
  126.         cmp     al, MEM_UC
  127.         jnz     .abort
  128. ; 4. Validate all variable-range MTRRs
  129. ; and copy configured MTRRs to the local array [.mtrrs].
  130. ; 4a. Prepare for the loop over existing variable-range MTRRs.
  131.         mov     ecx, 0x200
  132.         lea     edi, [.mtrrs]
  133. .get_used_mtrrs_loop:
  134. ; 4b. For every MTRR, read PHYSBASEn and PHYSMASKn.
  135. ; In PHYSBASEn, clear upper bits and copy to ebp:ebx.
  136.         rdmsr
  137.         or      edx, [.phys_reserved_mask]
  138.         xor     edx, [.phys_reserved_mask]
  139.         mov     ebp, edx
  140.         mov     ebx, eax
  141.         inc     ecx
  142. ; If PHYSMASKn is not active, ignore this MTRR.
  143.         rdmsr
  144.         inc     ecx
  145.         test    ah, 8
  146.         jz      .get_used_mtrrs_next
  147. ; 4c. For every active MTRR, check that number of local entries is not too large.
  148.         inc     [.num_used_mtrrs]
  149.         cmp     [.num_used_mtrrs], MAX_USEFUL_MTRRS
  150.         ja      .abort
  151. ; 4d. For every active MTRR, store PHYSBASEn with upper bits cleared.
  152. ; This contains the MTRR base and the memory type in low byte.
  153.         mov     [edi], ebx
  154.         mov     [edi+4], ebp
  155. ; 4e. For every active MTRR, check that the range is continuous:
  156. ; PHYSMASKn with upper bits set must be negated power of two, and
  157. ; low bits of PHYSBASEn must be zeroes:
  158. ; PHYSMASKn = 1...10...0,
  159. ; PHYSBASEn = x...x0...0,
  160. ; this defines a continuous range from x...x0...0 to x...x1...1,
  161. ; length = 10...0 = negated PHYSMASKn.
  162. ; Store length in the local array.
  163.         and     eax, not 0xFFF
  164.         or      edx, [.phys_reserved_mask]
  165.         mov     dword [edi+8], 0
  166.         mov     dword [edi+12], 0
  167.         sub     [edi+8], eax
  168.         sbb     [edi+12], edx
  169. ; (x and -x) is the maximum power of two that divides x.
  170. ; Condition for powers of two: (x and -x) equals x.
  171.         and     eax, [edi+8]
  172.         and     edx, [edi+12]
  173.         cmp     eax, [edi+8]
  174.         jnz     .abort
  175.         cmp     edx, [edi+12]
  176.         jnz     .abort
  177.         sub     eax, 1
  178.         sbb     edx, 0
  179.         and     eax, not 0xFFF
  180.         and     eax, ebx
  181.         jnz     .abort
  182.         and     edx, ebp
  183.         jnz     .abort
  184. ; 4f. For every active MTRR, validate memory type: it must be either WB or UC.
  185.         add     edi, 16
  186.         cmp     bl, MEM_UC
  187.         jz      .get_used_mtrrs_next
  188.         cmp     bl, MEM_WB
  189.         jnz     .abort
  190. .get_used_mtrrs_next:
  191. ; 4g. Repeat the loop at 4b-4f for all [num_variable_mtrrs] entries.
  192.         mov     eax, [num_variable_mtrrs]
  193.         lea     eax, [0x200+eax*2]
  194.         cmp     ecx, eax
  195.         jb      .get_used_mtrrs_loop
  196. ; 4h. If no active MTRRs were detected, fallback to step 7.
  197.         cmp     [.num_used_mtrrs], 0
  198.         jz      .fill_ranges_from_memory_map
  199.         mov     [.mtrrs_end], edi
  200. ; 5. Generate sorted list of ranges marked as WB.
  201. ; 5a. Prepare for the loop over configured MTRRs filled at step 4.
  202.         lea     ecx, [.mtrrs]
  203. .fill_wb_ranges:
  204. ; 5b. Ignore non-WB MTRRs.
  205.         mov     ebx, [ecx]
  206.         cmp     bl, MEM_WB
  207.         jnz     .next_wb_range
  208.         mov     ebp, [ecx+4]
  209.         and     ebx, not 0xFFF  ; clear memory type and reserved bits
  210. ; ebp:ebx = start of the range described by the current MTRR.
  211. ; 5c. Find the first existing range containing a point greater than ebp:ebx.
  212.         lea     esi, [.first_range]
  213. .find_range_wb:
  214. ; If there is no next range or start of the next range is greater than ebp:ebx,
  215. ; exit the loop to 5d.
  216.         mov     edi, [esi]
  217.         test    edi, edi
  218.         jz      .found_place_wb
  219.         mov     eax, ebx
  220.         mov     edx, ebp
  221.         sub     eax, dword [edi+mtrr_range.start]
  222.         sbb     edx, dword [edi+mtrr_range.start+4]
  223.         jb      .found_place_wb
  224. ; Otherwise, if end of the next range is greater than or equal to ebp:ebx,
  225. ; exit the loop to 5e.
  226.         mov     esi, edi
  227.         sub     eax, dword [edi+mtrr_range.length]
  228.         sbb     edx, dword [edi+mtrr_range.length+4]
  229.         jb      .expand_wb
  230.         or      eax, edx
  231.         jnz     .find_range_wb
  232.         jmp     .expand_wb
  233. .found_place_wb:
  234. ; 5d. ebp:ebx is not within any existing range.
  235. ; Insert a new range between esi and edi.
  236. ; (Later, during 5e, it can be merged with the following ranges.)
  237.         mov     eax, [.first_free_range]
  238.         test    eax, eax
  239.         jz      .abort
  240.         mov     [esi], eax
  241.         mov     edx, [eax+mtrr_range.next]
  242.         mov     [.first_free_range], edx
  243.         mov     dword [eax+mtrr_range.start], ebx
  244.         mov     dword [eax+mtrr_range.start+4], ebp
  245. ; Don't fill [eax+mtrr_range.next] and [eax+mtrr_range.length] yet,
  246. ; they will be calculated including merges at step 5e.
  247.         mov     esi, edi
  248.         mov     edi, eax
  249. .expand_wb:
  250. ; 5e. The range at edi contains ebp:ebx, and esi points to the first range
  251. ; to be checked for merge: esi=edi if ebp:ebx was found in an existing range,
  252. ; esi is next after edi if a new range with ebp:ebx was created.
  253. ; Merge it with following ranges while start of the next range is not greater
  254. ; than the end of the new range.
  255.         add     ebx, [ecx+8]
  256.         adc     ebp, [ecx+12]
  257. ; ebp:ebx = end of the range described by the current MTRR.
  258. .expand_wb_loop:
  259. ; If there is no next range or start of the next range is greater than ebp:ebx,
  260. ; exit the loop to 5g.
  261.         test    esi, esi
  262.         jz      .expand_wb_done
  263.         mov     eax, ebx
  264.         mov     edx, ebp
  265.         sub     eax, dword [esi+mtrr_range.start]
  266.         sbb     edx, dword [esi+mtrr_range.start+4]
  267.         jb      .expand_wb_done
  268. ; Otherwise, if end of the next range is greater than or equal to ebp:ebx,
  269. ; exit the loop to 5f.
  270.         sub     eax, dword [esi+mtrr_range.length]
  271.         sbb     edx, dword [esi+mtrr_range.length+4]
  272.         jb      .expand_wb_last
  273. ; Otherwise, the current range is completely within the new range.
  274. ; Free it and continue the loop.
  275.         mov     edx, [esi+mtrr_range.next]
  276.         cmp     esi, edi
  277.         jz      @f
  278.         mov     eax, [.first_free_range]
  279.         mov     [esi+mtrr_range.next], eax
  280.         mov     [.first_free_range], esi
  281. @@:
  282.         mov     esi, edx
  283.         jmp     .expand_wb_loop
  284. .expand_wb_last:
  285. ; 5f. Start of the new range is inside range described by esi,
  286. ; end of the new range is inside range described by edi.
  287. ; If esi is equal to edi, the new range is completely within
  288. ; an existing range, so proceed to the next range.
  289.         cmp     esi, edi
  290.         jz      .next_wb_range
  291. ; Otherwise, set end of interval at esi to end of interval at edi
  292. ; and free range described by edi.
  293.         mov     ebx, dword [esi+mtrr_range.start]
  294.         mov     ebp, dword [esi+mtrr_range.start+4]
  295.         add     ebx, dword [esi+mtrr_range.length]
  296.         adc     ebp, dword [esi+mtrr_range.length+4]
  297.         mov     edx, [esi+mtrr_range.next]
  298.         mov     eax, [.first_free_range]
  299.         mov     [esi+mtrr_range.next], eax
  300.         mov     [.first_free_range], esi
  301.         mov     esi, edx
  302. .expand_wb_done:
  303. ; 5g. We have found the next range (maybe 0) after merging and
  304. ; the new end of range (maybe ebp:ebx from the new range
  305. ; or end of another existing interval calculated at step 5f).
  306. ; Write them to range at edi.
  307.         mov     [edi+mtrr_range.next], esi
  308.         sub     ebx, dword [edi+mtrr_range.start]
  309.         sbb     ebp, dword [edi+mtrr_range.start+4]
  310.         mov     dword [edi+mtrr_range.length], ebx
  311.         mov     dword [edi+mtrr_range.length+4], ebp
  312. .next_wb_range:
  313. ; 5h. Continue the loop 5b-5g over all configured MTRRs.
  314.         add     ecx, 16
  315.         cmp     ecx, [.mtrrs_end]
  316.         jb      .fill_wb_ranges
  317. ; 6. Exclude all ranges marked as UC.
  318. ; 6a. Prepare for the loop over configured MTRRs filled at step 4.
  319.         lea     ecx, [.mtrrs]
  320. .fill_uc_ranges:
  321. ; 6b. Ignore non-UC MTRRs.
  322.         mov     ebx, [ecx]
  323.         cmp     bl, MEM_UC
  324.         jnz     .next_uc_range
  325.         mov     ebp, [ecx+4]
  326.         and     ebx, not 0xFFF  ; clear memory type and reserved bits
  327. ; ebp:ebx = start of the range described by the current MTRR.
  328.         lea     esi, [.first_range]
  329. ; 6c. Find the first existing range containing a point greater than ebp:ebx.
  330. .find_range_uc:
  331. ; If there is no next range, ignore this MTRR,
  332. ; exit the loop and continue to next MTRR.
  333.         mov     edi, [esi]
  334.         test    edi, edi
  335.         jz      .next_uc_range
  336. ; If start of the next range is greater than or equal to ebp:ebx,
  337. ; exit the loop to 6e.
  338.         mov     eax, dword [edi+mtrr_range.start]
  339.         mov     edx, dword [edi+mtrr_range.start+4]
  340.         sub     eax, ebx
  341.         sbb     edx, ebp
  342.         jnb     .truncate_uc
  343. ; Otherwise, continue the loop if end of the next range is less than ebp:ebx,
  344. ; exit the loop to 6d otherwise.
  345.         mov     esi, edi
  346.         add     eax, dword [edi+mtrr_range.length]
  347.         adc     edx, dword [edi+mtrr_range.length+4]
  348.         jnb     .find_range_uc
  349. ; 6d. ebp:ebx is inside (or at end of) an existing range.
  350. ; Split the range. (The second range, maybe containing completely within UC-range,
  351. ; maybe of zero length, can be removed at step 6e, if needed.)
  352.         mov     edi, [.first_free_range]
  353.         test    edi, edi
  354.         jz      .abort
  355.         mov     dword [edi+mtrr_range.start], ebx
  356.         mov     dword [edi+mtrr_range.start+4], ebp
  357.         mov     dword [edi+mtrr_range.length], eax
  358.         mov     dword [edi+mtrr_range.length+4], edx
  359.         mov     eax, [edi+mtrr_range.next]
  360.         mov     [.first_free_range], eax
  361.         mov     eax, [esi+mtrr_range.next]
  362.         mov     [edi+mtrr_range.next], eax
  363. ; don't change [esi+mtrr_range.next] yet, it will be filled at step 6e
  364.         mov     eax, ebx
  365.         mov     edx, ebp
  366.         sub     eax, dword [esi+mtrr_range.start]
  367.         sbb     edx, dword [esi+mtrr_range.start+4]
  368.         mov     dword [esi+mtrr_range.length], eax
  369.         mov     dword [esi+mtrr_range.length+4], edx
  370. .truncate_uc:
  371. ; 6e. edi is the first range after ebp:ebx, check it and next ranges
  372. ; for intersection with the new range, truncate heads.
  373.         add     ebx, [ecx+8]
  374.         adc     ebp, [ecx+12]
  375. ; ebp:ebx = end of the range described by the current MTRR.
  376. .truncate_uc_loop:
  377. ; If start of the next range is greater than ebp:ebx,
  378. ; exit the loop to 6g.
  379.         mov     eax, ebx
  380.         mov     edx, ebp
  381.         sub     eax, dword [edi+mtrr_range.start]
  382.         sbb     edx, dword [edi+mtrr_range.start+4]
  383.         jb      .truncate_uc_done
  384. ; Otherwise, if end of the next range is greater than ebp:ebx,
  385. ; exit the loop to 6f.
  386.         sub     eax, dword [edi+mtrr_range.length]
  387.         sbb     edx, dword [edi+mtrr_range.length+4]
  388.         jb      .truncate_uc_last
  389. ; Otherwise, the current range is completely within the new range.
  390. ; Free it and continue the loop if there is a next range.
  391. ; If that was a last range, exit the loop to 6g.
  392.         mov     edx, [edi+mtrr_range.next]
  393.         mov     eax, [.first_free_range]
  394.         mov     [.first_free_range], edi
  395.         mov     [edi+mtrr_range.next], eax
  396.         mov     edi, edx
  397.         test    edi, edi
  398.         jnz     .truncate_uc_loop
  399.         jmp     .truncate_uc_done
  400. .truncate_uc_last:
  401. ; 6f. The range at edi partially intersects with the UC-range described by MTRR.
  402. ; Truncate it from the head.
  403.         mov     dword [edi+mtrr_range.start], ebx
  404.         mov     dword [edi+mtrr_range.start+4], ebp
  405.         neg     eax
  406.         adc     edx, 0
  407.         neg     edx
  408.         mov     dword [edi+mtrr_range.length], eax
  409.         mov     dword [edi+mtrr_range.length+4], edx
  410. .truncate_uc_done:
  411. ; 6g. We have found the next range (maybe 0) after intersection.
  412. ; Write it to [esi+mtrr_range.next].
  413.         mov     [esi+mtrr_range.next], edi
  414. .next_uc_range:
  415. ; 6h. Continue the loop 6b-6g over all configured MTRRs.
  416.         add     ecx, 16
  417.         cmp     ecx, [.mtrrs_end]
  418.         jb      .fill_uc_ranges
  419. ; Sanity check: if there are no ranges after steps 5-6,
  420. ; fallback to step 7. Otherwise, go to 8.
  421.         cmp     [.first_range], 0
  422.         jnz     .ranges_ok
  423. .fill_ranges_from_memory_map:
  424. ; 7. BIOS has not configured variable-range MTRRs.
  425. ; Create one range from 0 to [MEM_AMOUNT].
  426.         mov     eax, [.first_free_range]
  427.         mov     edx, [eax+mtrr_range.next]
  428.         mov     [.first_free_range], edx
  429.         mov     [.first_range], eax
  430.         xor     edx, edx
  431.         mov     [eax+mtrr_range.next], edx
  432.         mov     dword [eax+mtrr_range.start], edx
  433.         mov     dword [eax+mtrr_range.start+4], edx
  434.         mov     ecx, [MEM_AMOUNT]
  435.         mov     dword [eax+mtrr_range.length], ecx
  436.         mov     dword [eax+mtrr_range.length+4], edx
  437. .ranges_ok:
  438. ; 8. We have calculated list of WB-ranges.
  439. ; Now we should calculate a list of MTRRs so that
  440. ; * every MTRR describes a range with length = power of 2 and start that is aligned,
  441. ; * every MTRR can be WB or UC
  442. ; * (sum of all WB ranges) minus (sum of all UC ranges) equals the calculated list
  443. ; * top of 4G memory must not be covered by any ranges
  444. ; Example: range [0,0xBC000000) can be converted to
  445. ; [0,0x80000000)+[0x80000000,0xC0000000)-[0xBC000000,0xC0000000)
  446. ; WB            +WB                     -UC
  447. ; but not to [0,0x100000000)-[0xC0000000,0x100000000)-[0xBC000000,0xC0000000).
  448. ; 8a. Check that list of ranges is [0,something) plus, optionally, [4G,something).
  449. ; This holds in practice (see mtrrtest.asm for real-life examples)
  450. ; and significantly simplifies the code: ranges are independent, start of range
  451. ; is almost always aligned (the only exception >4G upper memory can be easily covered),
  452. ; there is no need to consider adding holes before start of range, only
  453. ; append them to end of range.
  454.         xor     eax, eax
  455.         mov     edi, [.first_range]
  456.         cmp     dword [edi+mtrr_range.start], eax
  457.         jnz     .abort
  458.         cmp     dword [edi+mtrr_range.start+4], eax
  459.         jnz     .abort
  460.         cmp     dword [edi+mtrr_range.length+4], eax
  461.         jnz     .abort
  462.         mov     edx, [edi+mtrr_range.next]
  463.         test    edx, edx
  464.         jz      @f
  465.         cmp     dword [edx+mtrr_range.start], eax
  466.         jnz     .abort
  467.         cmp     dword [edx+mtrr_range.start+4], 1
  468.         jnz     .abort
  469.         cmp     [edx+mtrr_range.next], eax
  470.         jnz     .abort
  471. @@:
  472. ; 8b. Initialize: no MTRRs filled.
  473.         mov     [.num_used_mtrrs], eax
  474.         lea     esi, [.mtrrs]
  475. .range2mtrr_loop:
  476. ; 8c. If we are dealing with upper-memory range (after 4G)
  477. ; with length > start, create one WB MTRR with [start,2*start),
  478. ; reset start to 2*start and return to this step.
  479. ; Example: [4G,24G) -> [4G,8G) {returning} + [8G,16G) {returning}
  480. ; + [16G,24G) {advancing to ?}.
  481.         mov     eax, dword [edi+mtrr_range.length+4]
  482.         test    eax, eax
  483.         jz      .less4G
  484.         mov     edx, dword [edi+mtrr_range.start+4]
  485.         cmp     eax, edx
  486.         jb      .start_aligned
  487.         inc     [.num_used_mtrrs]
  488.         cmp     [.num_used_mtrrs], MAX_USEFUL_MTRRS
  489.         ja      .abort
  490.         mov     dword [esi], MEM_WB
  491.         mov     dword [esi+4], edx
  492.         mov     dword [esi+8], 0
  493.         mov     dword [esi+12], edx
  494.         add     esi, 16
  495.         add     dword [edi+mtrr_range.start+4], edx
  496.         sub     dword [edi+mtrr_range.length+4], edx
  497.         jnz     .range2mtrr_loop
  498.         cmp     dword [edi+mtrr_range.length], 0
  499.         jz      .range2mtrr_next
  500. .less4G:
  501. ; 8d. If we are dealing with low-memory range (before 4G)
  502. ; and appending a maximal-size hole would create a range covering top of 4G,
  503. ; create a maximal-size WB range and return to this step.
  504. ; Example: for [0,0xBC000000) the following steps would consider
  505. ; variants [0,0x80000000)+(another range to be splitted) and
  506. ; [0,0x100000000)-(another range to be splitted); we forbid the last variant,
  507. ; so the first variant must be used.
  508.         bsr     ecx, dword [edi+mtrr_range.length]
  509.         xor     edx, edx
  510.         inc     edx
  511.         shl     edx, cl
  512.         lea     eax, [edx*2]
  513.         add     eax, dword [edi+mtrr_range.start]
  514.         jnz     .start_aligned
  515.         inc     [.num_used_mtrrs]
  516.         cmp     [.num_used_mtrrs], MAX_USEFUL_MTRRS
  517.         ja      .abort
  518.         mov     eax, dword [edi+mtrr_range.start]
  519.         mov     dword [esi], eax
  520.         or      dword [esi], MEM_WB
  521.         mov     dword [esi+4], 0
  522.         mov     dword [esi+8], edx
  523.         mov     dword [esi+12], 0
  524.         add     esi, 16
  525.         add     dword [edi+mtrr_range.start], edx
  526.         sub     dword [edi+mtrr_range.length], edx
  527.         jnz     .less4G
  528.         jmp     .range2mtrr_next
  529. .start_aligned:
  530. ; Start is aligned for any allowed length, maximum-size hole is allowed.
  531. ; Select the best MTRR configuration for one range.
  532. ; length=...101101
  533. ; Without hole at the end, we need one WB MTRR for every 1-bit in length:
  534. ; length=...100000 + ...001000 + ...000100 + ...000001
  535. ; We can also append one hole at the end so that one 0-bit (selected by us)
  536. ; becomes 1 and all lower bits become 0 for WB-range:
  537. ; length=...110000 - (...00010 + ...00001)
  538. ; In this way, we need one WB MTRR for every 1-bit higher than the selected bit,
  539. ; one WB MTRR for the selected bit, one UC MTRR for every 0-bit between
  540. ; the selected bit and lowest 1-bit (they become 1-bits after negation)
  541. ; and one UC MTRR for lowest 1-bit.
  542. ; So we need to select 0-bit with the maximal difference
  543. ; (number of 0-bits) - (number of 1-bits) between selected and lowest 1-bit,
  544. ; this equals the gain from using a hole. If the difference is negative for
  545. ; all 0-bits, don't append hole.
  546. ; Note that lowest 1-bit is not included when counting, but selected 0-bit is.
  547. ; 8e. Find the optimal bit position for hole.
  548. ; eax = current difference, ebx = best difference,
  549. ; ecx = hole bit position, edx = current bit position.
  550.         xor     eax, eax
  551.         xor     ebx, ebx
  552.         xor     ecx, ecx
  553.         bsf     edx, dword [edi+mtrr_range.length]
  554.         jnz     @f
  555.         bsf     edx, dword [edi+mtrr_range.length+4]
  556.         add     edx, 32
  557. @@:
  558.         push    edx     ; save position of lowest 1-bit for step 8f
  559. .calc_stat:
  560.         inc     edx
  561.         cmp     edx, 64
  562.         jae     .stat_done
  563.         inc     eax     ; increment difference in hope for 1-bit
  564. ; Note: bt conveniently works with both .length and .length+4,
  565. ; depending on whether edx>=32.
  566.         bt      dword [edi+mtrr_range.length], edx
  567.         jc      .calc_stat
  568.         dec     eax     ; hope was wrong, decrement difference to correct 'inc'
  569.         dec     eax     ; and again, now getting the real difference
  570.         cmp     eax, ebx
  571.         jle     .calc_stat
  572.         mov     ebx, eax
  573.         mov     ecx, edx
  574.         jmp     .calc_stat
  575. .stat_done:
  576. ; 8f. If we decided to create a hole, flip all bits between lowest and selected.
  577.         pop     edx     ; restore position of lowest 1-bit saved at step 8e
  578.         test    ecx, ecx
  579.         jz      .fill_hi_init
  580. @@:
  581.         inc     edx
  582.         cmp     edx, ecx
  583.         ja      .fill_hi_init
  584.         btc     dword [edi+mtrr_range.length], edx
  585.         jmp     @b
  586. .fill_hi_init:
  587. ; 8g. Create MTRR ranges corresponding to upper 32 bits.
  588.         sub     ecx, 32
  589. .fill_hi_loop:
  590.         bsr     edx, dword [edi+mtrr_range.length+4]
  591.         jz      .fill_hi_done
  592.         inc     [.num_used_mtrrs]
  593.         cmp     [.num_used_mtrrs], MAX_USEFUL_MTRRS
  594.         ja      .abort
  595.         mov     eax, dword [edi+mtrr_range.start]
  596.         mov     [esi], eax
  597.         mov     eax, dword [edi+mtrr_range.start+4]
  598.         mov     [esi+4], eax
  599.         xor     eax, eax
  600.         mov     [esi+8], eax
  601.         bts     eax, edx
  602.         mov     [esi+12], eax
  603.         cmp     edx, ecx
  604.         jl      .fill_hi_uc
  605.         or      dword [esi], MEM_WB
  606.         add     dword [edi+mtrr_range.start+4], eax
  607.         jmp     @f
  608. .fill_hi_uc:
  609.         sub     dword [esi+4], eax
  610.         sub     dword [edi+mtrr_range.start+4], eax
  611. @@:
  612.         add     esi, 16
  613.         sub     dword [edi+mtrr_range.length], eax
  614.         jmp     .fill_hi_loop
  615. .fill_hi_done:
  616. ; 8h. Create MTRR ranges corresponding to lower 32 bits.
  617.         add     ecx, 32
  618. .fill_lo_loop:
  619.         bsr     edx, dword [edi+mtrr_range.length]
  620.         jz      .range2mtrr_next
  621.         inc     [.num_used_mtrrs]
  622.         cmp     [.num_used_mtrrs], MAX_USEFUL_MTRRS
  623.         ja      .abort
  624.         mov     eax, dword [edi+mtrr_range.start]
  625.         mov     [esi], eax
  626.         mov     eax, dword [edi+mtrr_range.start+4]
  627.         mov     [esi+4], eax
  628.         xor     eax, eax
  629.         mov     [esi+12], eax
  630.         bts     eax, edx
  631.         mov     [esi+8], eax
  632.         cmp     edx, ecx
  633.         jl      .fill_lo_uc
  634.         or      dword [esi], MEM_WB
  635.         add     dword [edi+mtrr_range.start], eax
  636.         jmp     @f
  637. .fill_lo_uc:
  638.         sub     dword [esi], eax
  639.         sub     dword [edi+mtrr_range.start], eax
  640. @@:
  641.         add     esi, 16
  642.         sub     dword [edi+mtrr_range.length], eax
  643.         jmp     .fill_lo_loop
  644. .range2mtrr_next:
  645. ; 8i. Repeat the loop at 8c-8h for all ranges.
  646.         mov     edi, [edi+mtrr_range.next]
  647.         test    edi, edi
  648.         jnz     .range2mtrr_loop
  649. ; 9. We have calculated needed MTRRs, now setup them in the CPU.
  650. ; 9a. Abort if number of MTRRs is too large.
  651.         mov     eax, [num_variable_mtrrs]
  652.         cmp     [.num_used_mtrrs], eax
  653.         ja      .abort
  654.  
  655. ; 9b. Prepare for changes.
  656.         call    mtrr_begin_change
  657.  
  658. ; 9c. Prepare for loop over MTRRs.
  659.         lea     esi, [.mtrrs]
  660.         mov     ecx, 0x200
  661. @@:
  662. ; 9d. For every MTRR, copy PHYSBASEn as is: step 8 has configured
  663. ; start value and type bits as needed.
  664.         mov     eax, [esi]
  665.         mov     edx, [esi+4]
  666.         wrmsr
  667.         inc     ecx
  668. ; 9e. For every MTRR, calculate PHYSMASKn = -(length) or 0x800
  669. ; with upper bits cleared, 0x800 = MTRR is valid.
  670.         xor     eax, eax
  671.         xor     edx, edx
  672.         sub     eax, [esi+8]
  673.         sbb     edx, [esi+12]
  674.         or      eax, 0x800
  675.         or      edx, [.phys_reserved_mask]
  676.         xor     edx, [.phys_reserved_mask]
  677.         wrmsr
  678.         inc     ecx
  679. ; 9f. Continue steps 9d and 9e for all MTRRs calculated at step 8.
  680.         add     esi, 16
  681.         dec     [.num_used_mtrrs]
  682.         jnz     @b
  683. ; 9g. Zero other MTRRs.
  684.         xor     eax, eax
  685.         xor     edx, edx
  686.         mov     ebx, [num_variable_mtrrs]
  687.         lea     ebx, [0x200+ebx*2]
  688. @@:
  689.         cmp     ecx, ebx
  690.         jae     @f
  691.         wrmsr
  692.         inc     ecx
  693.         wrmsr
  694.         inc     ecx
  695.         jmp     @b
  696. @@:
  697.  
  698. ; 9i. Configure MTRR_DEF_TYPE.
  699.         mov     ecx, 0x2FF
  700.         rdmsr
  701.         or      ah, 8   ; enable variable-ranges MTRR
  702.         and     al, 0xF0; default memtype = UC
  703.         wrmsr
  704.  
  705. ; 9j. Changes are done.
  706.         call    mtrr_end_change
  707.  
  708. .abort:
  709.         add     esp, .local_vars_size + MAX_RANGES * sizeof.mtrr_range
  710.         pop     ebp
  711.         ret
  712. endp
  713.  
  714. ; Allocate&set one MTRR for given range.
  715. ; size must be power of 2 that divides base.
  716. proc set_mtrr stdcall, base:dword,size:dword,mem_type:dword
  717. ; find unused register
  718.         mov     ecx, 0x201
  719. .scan:
  720.         rdmsr
  721.         dec     ecx
  722.         test    ah, 8
  723.         jz      .found
  724.         rdmsr
  725.         test    edx, edx
  726.         jnz     @f
  727.         and     eax, not 0xFFF  ; clear reserved bits
  728.         cmp     eax, [base]
  729.         jz      .ret
  730. @@:
  731.         add     ecx, 3
  732.         mov     eax, [num_variable_mtrrs]
  733.         lea     eax, [0x200+eax*2]
  734.         cmp     ecx, eax
  735.         jb      .scan
  736. ; no free registers, ignore the call
  737. .ret:
  738.         ret
  739. .found:
  740. ; found, write values
  741.         call    mtrr_begin_change
  742.         xor     edx, edx
  743.         mov     eax, [base]
  744.         or      eax, [mem_type]
  745.         wrmsr
  746.  
  747.         mov     al, [cpu_phys_addr_width]
  748.         xor     edx, edx
  749.         bts     edx, eax
  750.         xor     eax, eax
  751.         sub     eax, [size]
  752.         sbb     edx, 0
  753.         or      eax, 0x800
  754.         inc     ecx
  755.         wrmsr
  756.         call    mtrr_end_change
  757.         ret
  758. endp
  759.  
  760. ; Helper procedure for mtrr_validate.
  761. ; Calculates memory type for given address according to variable-range MTRRs.
  762. ; Assumes that MTRRs are enabled.
  763. ; in: ebx = 32-bit physical address
  764. ; out: eax = memory type for ebx
  765. proc mtrr_get_real_type
  766. ; 1. Initialize: we have not yet found any MTRRs covering ebx.
  767.         push    0
  768.         mov     ecx, 0x201
  769. .mtrr_loop:
  770. ; 2. For every MTRR, check whether it is valid; if not, continue to the next MTRR.
  771.         rdmsr
  772.         dec     ecx
  773.         test    ah, 8
  774.         jz      .next
  775. ; 3. For every valid MTRR, check whether (ebx and PHYSMASKn) == PHYSBASEn,
  776. ; excluding low 12 bits.
  777.         and     eax, ebx
  778.         push    eax
  779.         rdmsr
  780.         test    edx, edx
  781.         pop     edx
  782.         jnz     .next
  783.         xor     edx, eax
  784.         and     edx, not 0xFFF
  785.         jnz     .next
  786. ; 4. If so, set the bit corresponding to memory type defined by this MTRR.
  787.         and     eax, 7
  788.         bts     [esp], eax
  789. .next:
  790. ; 5. Continue loop at 2-4 for all variable-range MTRRs.
  791.         add     ecx, 3
  792.         mov     eax, [num_variable_mtrrs]
  793.         lea     eax, [0x200+eax*2]
  794.         cmp     ecx, eax
  795.         jb      .mtrr_loop
  796. ; 6. If no MTRRs cover address in ebx, use default MTRR type from MTRR_DEF_CAP.
  797.         pop     edx
  798.         test    edx, edx
  799.         jz      .default
  800. ; 7. Find&clear 1-bit in edx.
  801.         bsf     eax, edx
  802.         btr     edx, eax
  803. ; 8. If there was only one 1-bit, then all MTRRs are consistent, return that bit.
  804.         test    edx, edx
  805.         jz      .nothing
  806. ; Otherwise, return MEM_UC (e.g. WB+UC is UC).
  807.         xor     eax, eax
  808. .nothing:
  809.         ret
  810. .default:
  811.         mov     ecx, 0x2FF
  812.         rdmsr
  813.         movzx   eax, al
  814.         ret
  815. endp
  816.  
  817. ; If MTRRs are configured improperly, this is not obvious to the user;
  818. ; everything works, but the performance can be horrible.
  819. ; Try to detect this and let the user know that the low performance
  820. ; is caused by some problem and is not a global property of the system.
  821. ; Let's hope he would report it to developers...
  822. proc mtrr_validate
  823. ; 1. If MTRRs are not supported, they cannot be configured improperly.
  824.         bt      [cpu_caps], CAPS_MTRR
  825.         jnc     .exit
  826. ; 2. If variable-range MTRRs are not configured, this is a problem.
  827.         mov     ecx, 0x2FF
  828.         rdmsr
  829.         test    ah, 8
  830.         jz      .fail
  831. ; 3. Get the memory type for address somewhere inside working memory.
  832. ; It must be write-back.
  833.         mov     ebx, 0x27FFFF
  834.         call    mtrr_get_real_type
  835.         cmp     al, MEM_WB
  836.         jnz     .fail
  837. ; 4. If we're using a mode with LFB,
  838. ; get the memory type for last pixel of the framebuffer.
  839. ; It must be write-combined.
  840.         test    word [SCR_MODE], 0x4000
  841.         jz      .exit
  842.         mov     eax, [_display.pitch]
  843.         mul     [_display.height]
  844.         dec     eax
  845. ; LFB is mapped to virtual address LFB_BASE,
  846. ; it uses global pages if supported by CPU.
  847.         mov     ebx, [sys_pgdir+(LFB_BASE shr 20)]
  848.         test    ebx, PG_LARGE
  849.         jnz     @f
  850.         mov     ebx, [page_tabs+(LFB_BASE shr 10)]
  851. @@:
  852.         and     ebx, not 0xFFF
  853.         add     ebx, eax
  854.         call    mtrr_get_real_type
  855.         cmp     al, MEM_WC
  856.         jz      .exit
  857. ; 5. The check at step 4 fails on Bochs:
  858. ; Bochs BIOS configures MTRRs in a strange way not respecting [cpu_phys_addr_width],
  859. ; so mtrr_reconfigure avoids to touch anything.
  860. ; However, Bochs core ignores MTRRs (keeping them only for rdmsr/wrmsr),
  861. ; so we don't care about proper setting for Bochs.
  862. ; Use northbridge PCI id to detect Bochs: it emulates either i440fx or i430fx
  863. ; depending on configuration file.
  864.         mov     eax, [pcidev_list.fd]
  865.         cmp     eax, pcidev_list        ; sanity check: fail if no PCI devices
  866.         jz      .fail
  867.         cmp     [eax+PCIDEV.vendor_device_id], 0x12378086
  868.         jz      .exit
  869.         cmp     [eax+PCIDEV.vendor_device_id], 0x01228086
  870.         jnz     .fail
  871. .exit:
  872.         ret
  873. .fail:
  874.         mov     ebx, mtrr_user_message
  875.         mov     ebp, notifyapp
  876.         call    fs_execute_from_sysdir_param
  877.         ret
  878. endp
  879.