Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / drivers / iommu.c
1 /**
2  ** Proll (PROM replacement)
3  ** iommu.c: Functions for DVMA management.
4  ** Copyright 1999 Pete Zaitcev
5  ** This code is licensed under GNU General Public License.
6  **/
7 #include "config.h"
8 #include "libopenbios/bindings.h"
9 #include "libopenbios/ofmem.h"
10 #include "drivers/drivers.h"
11 #include "iommu.h"
12 #include "arch/sparc32/ofmem_sparc32.h"
13
14 #ifdef CONFIG_DEBUG_IOMMU
15 #define DPRINTF(fmt, args...)                   \
16     do { printk(fmt , ##args); } while (0)
17 #else
18 #define DPRINTF(fmt, args...)
19 #endif
20
21 /*
22  * IOMMU parameters
23  */
24 struct iommu {
25     struct iommu_regs *regs;
26     unsigned int *page_table;
27     unsigned long plow;     /* Base bus address */
28 };
29
30 static struct iommu ciommu;
31
32 static void
33 iommu_invalidate(struct iommu_regs *iregs)
34 {
35     iregs->tlbflush = 0;
36 }
37
38 /*
39  * XXX This is a problematic interface. We alloc _memory_ which is uncached.
40  * So if we ever reuse allocations somebody is going to get uncached pages.
41  * Returned address is always aligned by page.
42  * BTW, we were not going to give away anonymous storage, were we not?
43  */
44 void *
45 dvma_alloc(int size, unsigned int *pphys)
46 {
47     void *va;
48     unsigned int pa, ba;
49     unsigned int npages;
50     unsigned int mva, mpa;
51     unsigned int i;
52     unsigned int *iopte;
53     struct iommu *t = &ciommu;
54     int ret;
55
56     npages = (size + (PAGE_SIZE-1)) / PAGE_SIZE;
57     ret = ofmem_posix_memalign(&va, npages * PAGE_SIZE, PAGE_SIZE);
58     if (ret != 0)
59         return NULL;
60
61     ba = (unsigned int)mem_alloc(&cdvmem, npages * PAGE_SIZE, PAGE_SIZE);
62     if (ba == 0)
63         return NULL;
64
65     pa = (unsigned int)va2pa((unsigned long)va);
66
67     /*
68      * Change page attributes in MMU to uncached.
69      */
70     mva = (unsigned int) va;
71     mpa = (unsigned int) pa;
72     ofmem_arch_map_pages(mpa, mva, npages * PAGE_SIZE, ofmem_arch_io_translation_mode(mpa));
73
74     /*
75      * Map into IOMMU page table.
76      */
77     mpa = (unsigned int) pa;
78     iopte = &t->page_table[(ba - t->plow) / PAGE_SIZE];
79     for (i = 0; i < npages; i++) {
80         *iopte++ = MKIOPTE(mpa);
81         mpa += PAGE_SIZE;
82     }
83
84     *pphys = ba;
85
86     return va;
87 }
88
89 /*
90  * Initialize IOMMU
91  * This looks like initialization of CPU MMU but
92  * the routine is higher in food chain.
93  */
94 static struct iommu_regs *
95 iommu_init(struct iommu *t, uint64_t base)
96 {
97     unsigned int *ptab;
98     int ptsize;
99 #ifdef CONFIG_DEBUG_IOMMU
100     unsigned int impl, vers;
101 #endif
102     unsigned int tmp;
103     struct iommu_regs *regs;
104     int ret;
105     unsigned long vasize;
106
107     regs = (struct iommu_regs *)ofmem_map_io(base, IOMMU_REGS);
108     if (regs == NULL) {
109         DPRINTF("Cannot map IOMMU\n");
110         for (;;) { }
111     }
112     t->regs = regs;
113 #ifdef CONFIG_DEBUG_IOMMU
114     impl = (regs->control & IOMMU_CTRL_IMPL) >> 28;
115     vers = (regs->control & IOMMU_CTRL_VERS) >> 24;
116 #endif
117
118     tmp = regs->control;
119     tmp &= ~(IOMMU_CTRL_RNGE);
120
121     tmp |= (IOMMU_RNGE_32MB | IOMMU_CTRL_ENAB);
122     t->plow = 0xfe000000;               /* End - 32 MB */
123     /* Size of VA region that we manage */
124     vasize = 0x2000000; /* 32 MB */
125
126     regs->control = tmp;
127     iommu_invalidate(regs);
128
129     /* Allocate IOMMU page table */
130     /* Tremendous alignment causes great waste... */
131     ptsize = (vasize / PAGE_SIZE) * sizeof(int);
132     ret = ofmem_posix_memalign((void *)&ptab, ptsize, ptsize);
133     if (ret != 0) {
134         DPRINTF("Cannot allocate IOMMU table [0x%x]\n", ptsize);
135         for (;;) { }
136     }
137     t->page_table = ptab;
138
139     /* flush_cache_all(); */
140     /** flush_tlb_all(); **/
141     tmp = (unsigned int)va2pa((unsigned long)ptab);
142     regs->base = tmp >> 4;
143     iommu_invalidate(regs);
144
145     DPRINTF("IOMMU: impl %d vers %d page table at 0x%p (pa 0x%x) of size %d bytes\n",
146             impl, vers, t->page_table, tmp, ptsize);
147
148     mem_init(&cdvmem, (char*)t->plow, (char *)0xfffff000);
149     return regs;
150 }
151
152 /* ( addr.lo addr.hi size -- virt ) */
153
154 static void
155 ob_iommu_map_in(void)
156 {
157     phys_addr_t phys;
158     ucell size, virt;
159
160     size = POP();
161     phys = POP();
162     phys = (phys << 32) + POP();
163
164     virt = ofmem_map_io(phys, size);
165
166     PUSH(virt);
167 }
168
169 /* ( virt size ) */
170
171 static void
172 ob_iommu_map_out(void)
173 {
174     ucell size = POP();
175     ucell virt = POP();
176
177     ofmem_release_io(virt, size);
178 }
179
180 void
181 ob_init_iommu(uint64_t base)
182 {
183     struct iommu_regs *regs;
184
185     regs = iommu_init(&ciommu, base);
186
187     push_str("/iommu");
188     fword("find-device");
189     PUSH((unsigned long)regs);
190     fword("encode-int");
191     push_str("address");
192     fword("property");
193
194     PUSH(base >> 32);
195     fword("encode-int");
196     PUSH(base & 0xffffffff);
197     fword("encode-int");
198     fword("encode+");
199     PUSH(IOMMU_REGS);
200     fword("encode-int");
201     fword("encode+");
202     push_str("reg");
203     fword("property");
204
205     bind_func("map-in", ob_iommu_map_in);
206     bind_func("map-out", ob_iommu_map_out);
207 }