1 /* lib.c
2  * tag: simple function library
3  *
4  * Copyright (C) 2003 Stefan Reinauer
5  *
6  * See the file "COPYING" for further information about
7  * the copyright and warranty status of this work.
8  */
9 
10 #include "libc/vsprintf.h"
11 #include "libopenbios/bindings.h"
12 #include "arch/sparc32/ofmem_sparc32.h"
13 #include "asm/asi.h"
14 #include "arch/sparc32/pgtsrmmu.h"
15 #include "openprom.h"
16 #include "libopenbios/sys_info.h"
17 #include "boot.h"
18 #include "romvec.h"
19 
20 #define NCTX_SWIFT  0x100
21 #define LOWMEMSZ 32 * 1024 * 1024
22 
23 #ifdef CONFIG_DEBUG_MEM
24 #define DPRINTF(fmt, args...)                   \
25     do { printk(fmt , ##args); } while (0)
26 #else
27 #define DPRINTF(fmt, args...)
28 #endif
29 
30 /* Format a string and print it on the screen, just like the libc
31  * function printf.
32  */
printk(const char * fmt,...)33 int printk( const char *fmt, ... )
34 {
35         char *p, buf[512];
36 	va_list args;
37 	int i;
38 
39 	va_start(args, fmt);
40         i = vsnprintf(buf, sizeof(buf), fmt, args);
41 	va_end(args);
42 
43 	for( p=buf; *p; p++ )
44 		putchar(*p);
45 	return i;
46 }
47 
48 /*
49  * Allocatable memory chunk.
50  */
51 struct mem {
52     char *start, *uplim;
53     char *curp;
54 };
55 
56 struct mem cdvmem;              /* Current device virtual memory space */
57 
58 unsigned int va_shift;
59 unsigned long *l1;
60 static unsigned long *context_table;
61 
62 struct linux_mlist_v0 *ptphys;
63 struct linux_mlist_v0 *ptmap;
64 struct linux_mlist_v0 *ptavail;
65 
66 /* Private functions for mapping between physical/virtual addresses */
67 phys_addr_t
va2pa(unsigned long va)68 va2pa(unsigned long va)
69 {
70     if ((va >= (unsigned long)&_start) &&
71         (va < (unsigned long)&_end))
72         return va - va_shift;
73     else
74         return va;
75 }
76 
77 unsigned long
pa2va(phys_addr_t pa)78 pa2va(phys_addr_t pa)
79 {
80     if ((pa + va_shift >= (unsigned long)&_start) &&
81         (pa + va_shift < (unsigned long)&_end))
82         return pa + va_shift;
83     else
84         return pa;
85 }
86 
87 void *
malloc(int size)88 malloc(int size)
89 {
90     return ofmem_malloc(size);
91 }
92 
93 void *
realloc(void * ptr,size_t size)94 realloc( void *ptr, size_t size )
95 {
96     return ofmem_realloc(ptr, size);
97 }
98 
99 void
free(void * ptr)100 free(void *ptr)
101 {
102     ofmem_free(ptr);
103 }
104 
105 /*
106  * Allocate memory. This is reusable.
107  */
108 void
mem_init(struct mem * t,char * begin,char * limit)109 mem_init(struct mem *t, char *begin, char *limit)
110 {
111     t->start = begin;
112     t->uplim = limit;
113     t->curp = begin;
114 }
115 
116 void *
mem_alloc(struct mem * t,int size,int align)117 mem_alloc(struct mem *t, int size, int align)
118 {
119     char *p;
120     unsigned long pa;
121 
122     // The alignment restrictions refer to physical, not virtual
123     // addresses
124     pa = va2pa((unsigned long)t->curp) + (align - 1);
125     pa &= ~(align - 1);
126     p = (char *)pa2va(pa);
127 
128     if ((unsigned long)p >= (unsigned long)t->uplim ||
129         (unsigned long)p + size > (unsigned long)t->uplim)
130         return NULL;
131     t->curp = p + size;
132 
133     return p;
134 }
135 
136 /*
137  * D5.3 pgmap@ ( va -- pte )
138  */
139 static void
pgmap_fetch(void)140 pgmap_fetch(void)
141 {
142     uint32_t pte;
143     unsigned long va, pa;
144 
145     va = POP();
146 
147     pa = find_pte(va, 0);
148     if (pa == 1 || pa == 2)
149         goto error;
150     pte = *(uint32_t *)pa;
151     DPRINTF("pgmap@: va 0x%lx pa 0x%lx pte 0x%x\n", va, pa, pte);
152 
153     PUSH(pte);
154     return;
155  error:
156     PUSH(0);
157 }
158 
159 /*
160  * D5.3 pgmap! ( pte va -- )
161  */
162 static void
pgmap_store(void)163 pgmap_store(void)
164 {
165     uint32_t pte;
166     unsigned long va, pa;
167 
168     va = POP();
169     pte = POP();
170 
171     pa = find_pte(va, 1);
172     *(uint32_t *)pa = pte;
173     DPRINTF("pgmap!: va 0x%lx pa 0x%lx pte 0x%x\n", va, pa, pte);
174 }
175 
176 /*
177  * D5.3 map-pages ( pa space va size -- )
178  */
179 static void
ob_map_pages(void)180 ob_map_pages(void)
181 {
182     unsigned long va;
183     int size;
184     uint64_t pa;
185 
186     size = POP();
187     va = POP();
188     pa = POP();
189     pa <<= 32;
190     pa |= POP() & 0xffffffff;
191 
192     ofmem_arch_map_pages(pa, va, size, ofmem_arch_default_translation_mode(pa));
193 }
194 
obp_dumb_mmap(char * va,int which_io,unsigned int pa,unsigned int size)195 char *obp_dumb_mmap(char *va, int which_io, unsigned int pa,
196                     unsigned int size)
197 {
198     uint64_t mpa = ((uint64_t)which_io << 32) | (uint64_t)pa;
199     ucell virt;
200 
201     DPRINTF("obp_dumb_mmap: virta 0x%x, phys 0x%x, size %d\n", (unsigned int)va, pa, size);
202 
203     /* Claim virtual memory */
204     virt = ofmem_claim_virt(pointer2cell(va), size, 0);
205 
206     /* Map memory */
207     ofmem_map(mpa, virt, size, ofmem_arch_default_translation_mode(mpa));
208 
209     return cell2pointer(virt);
210 }
211 
obp_dumb_munmap(char * va,unsigned int size)212 void obp_dumb_munmap(char *va, unsigned int size)
213 {
214     DPRINTF("obp_dumb_munmap: virta 0x%x, sz %d\n", (unsigned int)va, size);
215 
216     ofmem_unmap(pointer2cell(va), size);
217     ofmem_release_virt(pointer2cell(va), size);
218 }
219 
obp_memalloc(char * va,unsigned int size,unsigned int align)220 char *obp_memalloc(char *va, unsigned int size, unsigned int align)
221 {
222     phys_addr_t phys;
223     ucell virt;
224 
225     DPRINTF("obp_memalloc: virta 0x%x, sz %d, align %d\n", (unsigned int)va, size, align);
226 
227     /* Claim physical memory */
228     phys = ofmem_claim_phys(-1, size, align);
229 
230     /* Claim virtual memory */
231     virt = ofmem_claim_virt(pointer2cell(va), size, 0);
232 
233     /* Map the memory */
234     ofmem_map(phys, virt, size, ofmem_arch_default_translation_mode(phys));
235 
236     return cell2pointer(virt);
237 }
238 
obp_dumb_memalloc(char * va,unsigned int size)239 char *obp_dumb_memalloc(char *va, unsigned int size)
240 {
241     unsigned long align = size;
242     phys_addr_t phys;
243     ucell virt;
244 
245     DPRINTF("obp_dumb_memalloc: virta 0x%x, sz %d\n", (unsigned int)va, size);
246 
247     /* Solaris seems to assume that the returned value is physically aligned to size.
248        e.g. it is used for setting up page tables. */
249 
250     /* Claim physical memory */
251     phys = ofmem_claim_phys(-1, size, align);
252 
253     /* Claim virtual memory - if va == NULL then we choose va address */
254     if (va == NULL) {
255         virt = ofmem_claim_virt((ucell)-1, size, align);
256     } else {
257         virt = ofmem_claim_virt(pointer2cell(va), size, 0);
258     }
259 
260     /* Map the memory */
261     ofmem_map(phys, virt, size, ofmem_arch_default_translation_mode(phys));
262 
263     return cell2pointer(virt);
264 }
265 
obp_dumb_memfree(char * va,unsigned size)266 void obp_dumb_memfree(char *va, unsigned size)
267 {
268     phys_addr_t phys;
269     ucell cellmode;
270 
271     DPRINTF("obp_dumb_memfree: virta 0x%x, sz %d\n", (unsigned int)va, size);
272 
273     phys = ofmem_translate(pointer2cell(va), &cellmode);
274 
275     ofmem_unmap(pointer2cell(va), size);
276     ofmem_release_virt(pointer2cell(va), size);
277     ofmem_release_phys(phys, size);
278 }
279 
280 /* Data fault handling routines */
281 
282 extern unsigned int ignore_dfault;
283 
284 /* ( -- reg ) */
srmmu_get_sfsr(void)285 static void srmmu_get_sfsr(void)
286 {
287     PUSH(srmmu_get_fstatus());
288 }
289 
290 /* ( -- addr ) */
ignore_dfault_addr(void)291 static void ignore_dfault_addr(void)
292 {
293     PUSH(pointer2cell(&ignore_dfault));
294 }
295 
296 void
ob_init_mmu(uint32_t simm_size)297 ob_init_mmu(uint32_t simm_size)
298 {
299     ucell *memreg;
300     ucell *virtreg;
301     phys_addr_t virtregsize;
302     ofmem_t *ofmem = ofmem_arch_get_private();
303     int i, c;
304 
305     /* Find the phandles for the /memory and /virtual-memory nodes */
306     push_str("/memory");
307     fword("find-package");
308     POP();
309     s_phandle_memory = POP();
310 
311     push_str("/virtual-memory");
312     fword("find-package");
313     POP();
314     s_phandle_mmu = POP();
315 
316     ofmem_register(s_phandle_memory, s_phandle_mmu);
317 
318     /* Setup /memory:reg (totphys) property */
319     c = ofmem->ramsize / simm_size;
320     memreg = malloc(3 * c * sizeof(ucell));
321     for (i = 0; i < c; i++) {
322         ofmem_arch_encode_physaddr(&memreg[i * 3], simm_size * i); /* physical base */
323         memreg[i * 3 + 2] = simm_size; /* size */
324     }
325 
326     push_str("/memory");
327     fword("find-device");
328     PUSH(pointer2cell(memreg));
329     PUSH(3 * c * sizeof(ucell));
330     push_str("reg");
331     PUSH_ph(s_phandle_memory);
332     fword("encode-property");
333 
334     /* Setup /virtual-memory:reg property */
335     virtregsize = ((phys_addr_t)((ucell)-1) + 1) / 2;
336 
337     virtreg = malloc(6 * sizeof(ucell));
338     ofmem_arch_encode_physaddr(virtreg, 0);
339     virtreg[2] = virtregsize;
340     ofmem_arch_encode_physaddr(&virtreg[3], virtregsize);
341     virtreg[5] = virtregsize;
342 
343     push_str("/virtual-memory");
344     fword("find-device");
345     PUSH(pointer2cell(virtreg));
346     PUSH(6 * sizeof(ucell));
347     push_str("reg");
348     PUSH_ph(s_phandle_mmu);
349     fword("encode-property");
350 
351     PUSH(0);
352     fword("active-package!");
353     bind_func("pgmap@", pgmap_fetch);
354     bind_func("pgmap!", pgmap_store);
355     bind_func("map-pages", ob_map_pages);
356 
357     /* Install data fault handler words for cpeek etc. */
358     PUSH_xt(bind_noname_func(srmmu_get_sfsr));
359     feval("to sfsr@");
360     PUSH_xt(bind_noname_func(ignore_dfault_addr));
361     feval("to ignore-dfault");
362 }
363 
364 /*
365  * Switch page tables.
366  */
367 void
init_mmu_swift(void)368 init_mmu_swift(void)
369 {
370     unsigned int addr, i;
371     unsigned long pa, va;
372     int size;
373 
374     ofmem_posix_memalign((void *)&context_table, NCTX_SWIFT * sizeof(int),
375                    NCTX_SWIFT * sizeof(int));
376     ofmem_posix_memalign((void *)&l1, 256 * sizeof(int), 256 * sizeof(int));
377 
378     context_table[0] = (((unsigned long)va2pa((unsigned long)l1)) >> 4) |
379         SRMMU_ET_PTD;
380 
381     for (i = 1; i < NCTX_SWIFT; i++) {
382         context_table[i] = SRMMU_ET_INVALID;
383     }
384     for (i = 0; i < 256; i++) {
385         l1[i] = SRMMU_ET_INVALID;
386     }
387 
388     // text, rodata, data, and bss mapped to end of RAM
389     va = (unsigned long)&_start;
390     size = (unsigned long)&_end - (unsigned long)&_start;
391     pa = va2pa(va);
392     ofmem_arch_map_pages(pa, va, size, ofmem_arch_default_translation_mode(pa));
393     ofmem_map_page_range(pa, va, size, ofmem_arch_default_translation_mode(pa));
394 
395     // 1:1 mapping for RAM (don't map page 0 to allow catching of NULL dereferences)
396     ofmem_arch_map_pages(PAGE_SIZE, PAGE_SIZE, LOWMEMSZ - PAGE_SIZE, ofmem_arch_default_translation_mode(0));
397     ofmem_map_page_range(PAGE_SIZE, PAGE_SIZE, LOWMEMSZ - PAGE_SIZE, ofmem_arch_default_translation_mode(0));
398 
399     /*
400      * Flush cache
401      */
402     for (addr = 0; addr < 0x2000; addr += 0x10) {
403         __asm__ __volatile__ ("sta %%g0, [%0] %1\n\t" : :
404                               "r" (addr), "i" (ASI_M_DATAC_TAG));
405         __asm__ __volatile__ ("sta %%g0, [%0] %1\n\t" : :
406                               "r" (addr<<1), "i" (ASI_M_TXTC_TAG));
407     }
408     srmmu_set_context(0);
409     srmmu_set_ctable_ptr(va2pa((unsigned long)context_table));
410     srmmu_flush_whole_tlb();
411 }
412