Fedora Core 2 Updates 2.6.10-1.771_FC2
[linux-2.6.git] / arch / xen / i386 / kernel / cpu / mtrr / main.c
1 #include <linux/init.h>
2 #include <linux/proc_fs.h>
3 #include <linux/ctype.h>
4 #include <linux/module.h>
5 #include <linux/seq_file.h>
6 #include <asm/uaccess.h>
7
8 #include <asm/mtrr.h>
9 #include "mtrr.h"
10
11 void generic_get_mtrr(unsigned int reg, unsigned long *base,
12                       unsigned int *size, mtrr_type * type)
13 {
14         dom0_op_t op;
15
16         op.cmd = DOM0_READ_MEMTYPE;
17         op.u.read_memtype.reg = reg;
18         (void)HYPERVISOR_dom0_op(&op);
19
20         *size = op.u.read_memtype.nr_pfns;
21         *base = op.u.read_memtype.pfn;
22         *type = op.u.read_memtype.type;
23 }
24
25 struct mtrr_ops generic_mtrr_ops = {
26         .use_intel_if      = 1,
27         .get               = generic_get_mtrr,
28 };
29
30 struct mtrr_ops *mtrr_if = &generic_mtrr_ops;
31 unsigned int num_var_ranges;
32 unsigned int *usage_table;
33
34 void __init set_num_var_ranges(void)
35 {
36         dom0_op_t op;
37
38         for (num_var_ranges = 0; ; num_var_ranges++) {
39                 op.cmd = DOM0_READ_MEMTYPE;
40                 op.u.read_memtype.reg = num_var_ranges;
41                 if (HYPERVISOR_dom0_op(&op) != 0)
42                         break;
43         }
44 }
45
46 static void __init init_table(void)
47 {
48         int i, max;
49
50         max = num_var_ranges;
51         if ((usage_table = kmalloc(max * sizeof *usage_table, GFP_KERNEL))
52             == NULL) {
53                 printk(KERN_ERR "mtrr: could not allocate\n");
54                 return;
55         }
56         for (i = 0; i < max; i++)
57                 usage_table[i] = 0;
58 }
59
60 int mtrr_add_page(unsigned long base, unsigned long size, 
61                   unsigned int type, char increment)
62 {
63         int error;
64         dom0_op_t op;
65
66         op.cmd = DOM0_ADD_MEMTYPE;
67         op.u.add_memtype.pfn     = base;
68         op.u.add_memtype.nr_pfns = size;
69         op.u.add_memtype.type    = type;
70         if ((error = HYPERVISOR_dom0_op(&op)))
71                 return error;
72
73         if (increment)
74                 ++usage_table[op.u.add_memtype.reg];
75
76         return op.u.add_memtype.reg;
77 }
78
79 int
80 mtrr_add(unsigned long base, unsigned long size, unsigned int type,
81          char increment)
82 {
83         if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
84                 printk(KERN_WARNING "mtrr: size and base must be multiples of 4 kiB\n");
85                 printk(KERN_DEBUG "mtrr: size: 0x%lx  base: 0x%lx\n", size, base);
86                 return -EINVAL;
87         }
88         return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type,
89                              increment);
90 }
91
92 int mtrr_del_page(int reg, unsigned long base, unsigned long size)
93 {
94         int i, max;
95         mtrr_type ltype;
96         unsigned long lbase;
97         unsigned int lsize;
98         int error = -EINVAL;
99         dom0_op_t op;
100
101         max = num_var_ranges;
102         if (reg < 0) {
103                 /*  Search for existing MTRR  */
104                 for (i = 0; i < max; ++i) {
105                         mtrr_if->get(i, &lbase, &lsize, &ltype);
106                         if (lbase == base && lsize == size) {
107                                 reg = i;
108                                 break;
109                         }
110                 }
111                 if (reg < 0) {
112                         printk(KERN_DEBUG "mtrr: no MTRR for %lx000,%lx000 found\n", base,
113                                size);
114                         goto out;
115                 }
116         }
117         if (usage_table[reg] < 1) {
118                 printk(KERN_WARNING "mtrr: reg: %d has count=0\n", reg);
119                 goto out;
120         }
121         if (--usage_table[reg] < 1) {
122                 op.cmd = DOM0_DEL_MEMTYPE;
123                 op.u.del_memtype.handle = 0;
124                 op.u.add_memtype.reg    = reg;
125                 (void)HYPERVISOR_dom0_op(&op);
126         }
127         error = reg;
128  out:
129         return error;
130 }
131
132 int
133 mtrr_del(int reg, unsigned long base, unsigned long size)
134 {
135         if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
136                 printk(KERN_INFO "mtrr: size and base must be multiples of 4 kiB\n");
137                 printk(KERN_DEBUG "mtrr: size: 0x%lx  base: 0x%lx\n", size, base);
138                 return -EINVAL;
139         }
140         return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);
141 }
142
143 EXPORT_SYMBOL(mtrr_add);
144 EXPORT_SYMBOL(mtrr_del);
145
146 static int __init mtrr_init(void)
147 {
148         struct cpuinfo_x86 *c = &boot_cpu_data;
149
150         if (!(xen_start_info.flags & SIF_PRIVILEGED))
151                 return -ENODEV;
152
153         if ((!cpu_has(c, X86_FEATURE_MTRR)) &&
154             (!cpu_has(c, X86_FEATURE_K6_MTRR)) &&
155             (!cpu_has(c, X86_FEATURE_CYRIX_ARR)) &&
156             (!cpu_has(c, X86_FEATURE_CENTAUR_MCR)))
157                 return -ENODEV;
158
159         set_num_var_ranges();
160         init_table();
161
162         return 0;
163 }
164
165 subsys_initcall(mtrr_init);