1 /*
2  *	<ofmem_sparc32.c>
3  *
4  *	OF Memory manager
5  *
6  *   Copyright (C) 1999-2004 Samuel Rydh (samuel@ibrium.se)
7  *   Copyright (C) 2004 Stefan Reinauer
8  *
9  *   This program is free software; you can redistribute it and/or
10  *   modify it under the terms of the GNU General Public License
11  *   as published by the Free Software Foundation
12  *
13  */
14 
15 #include "config.h"
16 #include "libopenbios/bindings.h"
17 #include "libc/string.h"
18 #include "arch/sparc32/ofmem_sparc32.h"
19 #include "asm/asi.h"
20 #include "arch/sparc32/pgtsrmmu.h"
21 
22 #define OF_MALLOC_BASE		((char*)OFMEM + ALIGN_SIZE(sizeof(ofmem_t), 8))
23 
24 #define MEMSIZE (256 * 1024)
25 static union {
26 	char memory[MEMSIZE];
27 	ofmem_t ofmem;
28 } s_ofmem_data;
29 
30 #define OFMEM      	(&s_ofmem_data.ofmem)
31 #define TOP_OF_RAM 	(s_ofmem_data.memory + MEMSIZE)
32 
33 #define OFMEM_PHYS_RESERVED	0x1000000
34 
35 translation_t **g_ofmem_translations = &s_ofmem_data.ofmem.trans;
36 
37 extern uint32_t qemu_mem_size;
38 
ALIGN_SIZE(size_t x,size_t a)39 static inline size_t ALIGN_SIZE(size_t x, size_t a)
40 {
41     return (x + a - 1) & ~(a-1);
42 }
43 
get_heap_top(void)44 static ucell get_heap_top( void )
45 {
46 	return (ucell)TOP_OF_RAM;
47 }
48 
ofmem_arch_get_private(void)49 ofmem_t* ofmem_arch_get_private(void)
50 {
51 	return OFMEM;
52 }
53 
ofmem_arch_get_malloc_base(void)54 void* ofmem_arch_get_malloc_base(void)
55 {
56 	return OF_MALLOC_BASE;
57 }
58 
ofmem_arch_get_heap_top(void)59 ucell ofmem_arch_get_heap_top(void)
60 {
61 	return get_heap_top();
62 }
63 
ofmem_arch_get_virt_top(void)64 ucell ofmem_arch_get_virt_top(void)
65 {
66 	return (ucell)OFMEM_VIRT_TOP;
67 }
68 
ofmem_arch_get_iomem_base(void)69 ucell ofmem_arch_get_iomem_base(void)
70 {
71 	return pointer2cell(&_end);
72 }
73 
ofmem_arch_get_iomem_top(void)74 ucell ofmem_arch_get_iomem_top(void)
75 {
76 	return pointer2cell(&_iomem);
77 }
78 
ofmem_arch_get_retained(void)79 retain_t *ofmem_arch_get_retained(void)
80 {
81 	/* Not used */
82 	return 0;
83 }
84 
ofmem_arch_get_physaddr_cellsize(void)85 int ofmem_arch_get_physaddr_cellsize(void)
86 {
87 	return 2;
88 }
89 
ofmem_arch_encode_physaddr(ucell * p,phys_addr_t value)90 int ofmem_arch_encode_physaddr(ucell *p, phys_addr_t value)
91 {
92 	int n = 0;
93 
94 	p[n++] = value >> 32;
95 	p[n++] = value;
96 
97 	return n;
98 }
99 
ofmem_arch_get_translation_entry_size(void)100 int ofmem_arch_get_translation_entry_size(void)
101 {
102 	/* Return size of a single MMU package translation property entry in cells */
103 	return 3;
104 }
105 
ofmem_arch_create_translation_entry(ucell * transentry,translation_t * t)106 void ofmem_arch_create_translation_entry(ucell *transentry, translation_t *t)
107 {
108 	/* Generate translation property entry for SPARC. While there is no
109 	formal documentation for this, both Linux kernel and OpenSolaris sources
110 	expect a translation property entry to have the following layout:
111 
112 		virtual address
113 		length
114 		mode
115 	*/
116 
117 	transentry[0] = t->virt;
118 	transentry[1] = t->size;
119 	transentry[2] = t->mode;
120 }
121 
122 /* Return the size of a memory available entry given the phandle in cells */
ofmem_arch_get_available_entry_size(phandle_t ph)123 int ofmem_arch_get_available_entry_size(phandle_t ph)
124 {
125 	return 1 + ofmem_arch_get_physaddr_cellsize();
126 }
127 
128 /* Generate memory available property entry for Sparc32 */
ofmem_arch_create_available_entry(phandle_t ph,ucell * availentry,phys_addr_t start,ucell size)129 void ofmem_arch_create_available_entry(phandle_t ph, ucell *availentry, phys_addr_t start, ucell size)
130 {
131   int i = 0;
132 
133 	i += ofmem_arch_encode_physaddr(availentry, start);
134 	availentry[i] = size;
135 }
136 
137 /* Unmap a set of pages */
ofmem_arch_unmap_pages(ucell virt,ucell size)138 void ofmem_arch_unmap_pages(ucell virt, ucell size)
139 {
140 	unsigned long pa;
141 	ucell i;
142 
143 	for (i = 0; i < size; i += PAGE_SIZE) {
144 		pa = find_pte(virt, 0);
145 		*(uint32_t *)pa = 0;
146 		virt += PAGE_SIZE;
147 	}
148 
149 	srmmu_flush_whole_tlb();
150 }
151 
152 /* Map a set of pages */
ofmem_arch_map_pages(phys_addr_t phys,ucell virt,ucell size,ucell mode)153 void ofmem_arch_map_pages(phys_addr_t phys, ucell virt, ucell size, ucell mode)
154 {
155 	unsigned long npages, off;
156 	uint32_t pte;
157 	unsigned long pa;
158 
159 	off = phys & (PAGE_SIZE - 1);
160 	npages = (off + (size - 1) + (PAGE_SIZE - 1)) / PAGE_SIZE;
161 	phys &= ~(uint64_t)(PAGE_SIZE - 1);
162 
163 	while (npages-- != 0) {
164 		pa = find_pte(virt, 1);
165 
166 		pte = SRMMU_ET_PTE | ((phys & PAGE_MASK) >> 4);
167 		pte |= mode;
168 
169 		*(uint32_t *)pa = pte;
170 
171 		virt += PAGE_SIZE;
172 		phys += PAGE_SIZE;
173 	}
174 }
175 
176 /* Architecture-specific OFMEM helpers */
177 unsigned long
find_pte(unsigned long va,int alloc)178 find_pte(unsigned long va, int alloc)
179 {
180     uint32_t pte;
181     void *p;
182     unsigned long pa;
183     int ret;
184 
185     pte = l1[(va >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1)];
186     if ((pte & SRMMU_ET_MASK) == SRMMU_ET_INVALID) {
187         if (alloc) {
188             ret = ofmem_posix_memalign(&p, SRMMU_PTRS_PER_PMD * sizeof(int),
189                                  SRMMU_PTRS_PER_PMD * sizeof(int));
190             if (ret != 0)
191                 return ret;
192             pte = SRMMU_ET_PTD | ((va2pa((unsigned long)p)) >> 4);
193             l1[(va >> SRMMU_PGDIR_SHIFT) & (SRMMU_PTRS_PER_PGD - 1)] = pte;
194             /* barrier() */
195         } else {
196             return -1;
197         }
198     }
199 
200     pa = (pte & 0xFFFFFFF0) << 4;
201     pa += ((va >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)) << 2;
202     pte = *(uint32_t *)pa2va(pa);
203     if ((pte & SRMMU_ET_MASK) == SRMMU_ET_INVALID) {
204         if (alloc) {
205             ret = ofmem_posix_memalign(&p, SRMMU_PTRS_PER_PTE * sizeof(void *),
206                                  SRMMU_PTRS_PER_PTE * sizeof(void *));
207             if (ret != 0)
208                 return ret;
209             pte = SRMMU_ET_PTD | ((va2pa((unsigned int)p)) >> 4);
210             *(uint32_t *)pa2va(pa) = pte;
211         } else {
212             return -2;
213         }
214     }
215 
216     pa = (pte & 0xFFFFFFF0) << 4;
217     pa += ((va >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)) << 2;
218 
219     return pa2va(pa);
220 }
221 
222 /************************************************************************/
223 /* misc                                                                 */
224 /************************************************************************/
225 
ofmem_arch_default_translation_mode(phys_addr_t phys)226 ucell ofmem_arch_default_translation_mode( phys_addr_t phys )
227 {
228 	return SRMMU_REF | SRMMU_CACHE | SRMMU_PRIV;
229 }
230 
ofmem_arch_io_translation_mode(phys_addr_t phys)231 ucell ofmem_arch_io_translation_mode( phys_addr_t phys )
232 {
233 	return SRMMU_REF | SRMMU_PRIV;
234 }
235 
236 /************************************************************************/
237 /* init / cleanup                                                       */
238 /************************************************************************/
239 
ofmem_init(void)240 void ofmem_init( void )
241 {
242 	memset(&s_ofmem_data, 0, sizeof(s_ofmem_data));
243 	s_ofmem_data.ofmem.ramsize = qemu_mem_size;
244 
245 	/* Mark the first page as non-free */
246 	ofmem_claim_virt(0, PAGE_SIZE, 0);
247 
248 	/* Claim reserved physical addresses at top of RAM */
249 	ofmem_claim_phys(s_ofmem_data.ofmem.ramsize - OFMEM_PHYS_RESERVED, OFMEM_PHYS_RESERVED, 0);
250 
251 	/* Claim OpenBIOS reserved space */
252 	ofmem_claim_virt(0xffd00000, 0x200000, 0);
253 }
254