Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
amd.c
Go to the documentation of this file.
1 #include <linux/init.h>
2 #include <linux/mm.h>
3 #include <asm/mtrr.h>
4 #include <asm/msr.h>
5 
6 #include "mtrr.h"
7 
8 static void
9 amd_get_mtrr(unsigned int reg, unsigned long *base,
10  unsigned long *size, mtrr_type *type)
11 {
12  unsigned long low, high;
13 
14  rdmsr(MSR_K6_UWCCR, low, high);
15  /* Upper dword is region 1, lower is region 0 */
16  if (reg == 1)
17  low = high;
18  /* The base masks off on the right alignment */
19  *base = (low & 0xFFFE0000) >> PAGE_SHIFT;
20  *type = 0;
21  if (low & 1)
22  *type = MTRR_TYPE_UNCACHABLE;
23  if (low & 2)
24  *type = MTRR_TYPE_WRCOMB;
25  if (!(low & 3)) {
26  *size = 0;
27  return;
28  }
29  /*
30  * This needs a little explaining. The size is stored as an
31  * inverted mask of bits of 128K granularity 15 bits long offset
32  * 2 bits.
33  *
34  * So to get a size we do invert the mask and add 1 to the lowest
35  * mask bit (4 as its 2 bits in). This gives us a size we then shift
36  * to turn into 128K blocks.
37  *
38  * eg 111 1111 1111 1100 is 512K
39  *
40  * invert 000 0000 0000 0011
41  * +1 000 0000 0000 0100
42  * *128K ...
43  */
44  low = (~low) & 0x1FFFC;
45  *size = (low + 4) << (15 - PAGE_SHIFT);
46 }
47 
58 static void
59 amd_set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type type)
60 {
61  u32 regs[2];
62 
63  /*
64  * Low is MTRR0, High MTRR 1
65  */
66  rdmsr(MSR_K6_UWCCR, regs[0], regs[1]);
67  /*
68  * Blank to disable
69  */
70  if (size == 0) {
71  regs[reg] = 0;
72  } else {
73  /*
74  * Set the register to the base, the type (off by one) and an
75  * inverted bitmask of the size The size is the only odd
76  * bit. We are fed say 512K We invert this and we get 111 1111
77  * 1111 1011 but if you subtract one and invert you get the
78  * desired 111 1111 1111 1100 mask
79  *
80  * But ~(x - 1) == ~x + 1 == -x. Two's complement rocks!
81  */
82  regs[reg] = (-size >> (15 - PAGE_SHIFT) & 0x0001FFFC)
83  | (base << PAGE_SHIFT) | (type + 1);
84  }
85 
86  /*
87  * The writeback rule is quite specific. See the manual. Its
88  * disable local interrupts, write back the cache, set the mtrr
89  */
90  wbinvd();
91  wrmsr(MSR_K6_UWCCR, regs[0], regs[1]);
92 }
93 
94 static int
95 amd_validate_add_page(unsigned long base, unsigned long size, unsigned int type)
96 {
97  /*
98  * Apply the K6 block alignment and size rules
99  * In order
100  * o Uncached or gathering only
101  * o 128K or bigger block
102  * o Power of 2 block
103  * o base suitably aligned to the power
104  */
105  if (type > MTRR_TYPE_WRCOMB || size < (1 << (17 - PAGE_SHIFT))
106  || (size & ~(size - 1)) - size || (base & (size - 1)))
107  return -EINVAL;
108  return 0;
109 }
110 
111 static const struct mtrr_ops amd_mtrr_ops = {
112  .vendor = X86_VENDOR_AMD,
113  .set = amd_set_mtrr,
114  .get = amd_get_mtrr,
115  .get_free_region = generic_get_free_region,
116  .validate_add_page = amd_validate_add_page,
117  .have_wrcomb = positive_have_wrcomb,
118 };
119 
121 {
122  set_mtrr_ops(&amd_mtrr_ops);
123  return 0;
124 }