xref: /haiku/src/system/kernel/arch/ppc/arch_cpu.cpp (revision 71452e98334eaac603bf542d159e24788a46bebb)
1 /*
2  * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <KernelExport.h>
11 
12 #include <arch_platform.h>
13 #include <arch/cpu.h>
14 #include <arch/thread.h>
15 #include <boot/kernel_args.h>
16 
17 static bool sHasTlbia;
18 
19 status_t
20 arch_cpu_preboot_init_percpu(kernel_args *args, int curr_cpu)
21 {
22 	// enable FPU
23 	set_msr(get_msr() | MSR_FP_AVAILABLE);
24 
25 	// The current thread must be NULL for all CPUs till we have threads.
26 	// Some boot code relies on this.
27 	arch_thread_set_current_thread(NULL);
28 
29 	return B_OK;
30 }
31 
32 
33 status_t
34 arch_cpu_init(kernel_args *args)
35 {
36 	// TODO: Let the boot loader put that info into the kernel args
37 	// (property "tlbia" in the CPU node).
38 	sHasTlbia = false;
39 
40 	return B_OK;
41 }
42 
43 
44 status_t
45 arch_cpu_init_post_vm(kernel_args *args)
46 {
47 	return B_OK;
48 }
49 
50 status_t
51 arch_cpu_init_percpu(kernel_args *args, int curr_cpu)
52 {
53         //detect_cpu(curr_cpu);
54 
55         // we only support one on ppc anyway at the moment...
56 	//XXX: WRITEME
57         return 0;
58 }
59 
60 status_t
61 arch_cpu_init_post_modules(kernel_args *args)
62 {
63 	return B_OK;
64 }
65 
66 #define CACHELINE 32
67 
68 void
69 arch_cpu_sync_icache(void *address, size_t len)
70 {
71 	int l, off;
72 	char *p;
73 
74 	off = (unsigned int)address & (CACHELINE - 1);
75 	len += off;
76 
77 	l = len;
78 	p = (char *)address - off;
79 	do {
80 		asm volatile ("dcbst 0,%0" :: "r"(p));
81 		p += CACHELINE;
82 	} while ((l -= CACHELINE) > 0);
83 	asm volatile ("sync");
84 
85 	p = (char *)address - off;
86 	do {
87 		asm volatile ("icbi 0,%0" :: "r"(p));
88 		p += CACHELINE;
89 	} while ((len -= CACHELINE) > 0);
90 	asm volatile ("sync");
91 	isync();
92 }
93 
94 
95 void
96 arch_cpu_memory_read_barrier(void)
97 {
98 // WARNING PPC: is it model-dependant ?
99 	asm volatile ("lwsync");
100 }
101 
102 
103 void
104 arch_cpu_memory_write_barrier(void)
105 {
106 // WARNING PPC: is it model-dependant ?
107 	asm volatile ("isync");
108 	asm volatile ("eieio");
109 }
110 
111 
112 void
113 arch_cpu_invalidate_TLB_range(addr_t start, addr_t end)
114 {
115 	asm volatile("sync");
116 	while (start < end) {
117 		asm volatile("tlbie %0" :: "r" (start));
118 		asm volatile("eieio");
119 		asm volatile("sync");
120 		start += B_PAGE_SIZE;
121 	}
122 	asm volatile("tlbsync");
123 	asm volatile("sync");
124 }
125 
126 
127 void
128 arch_cpu_invalidate_TLB_list(addr_t pages[], int num_pages)
129 {
130 	int i;
131 
132 	asm volatile("sync");
133 	for (i = 0; i < num_pages; i++) {
134 		asm volatile("tlbie %0" :: "r" (pages[i]));
135 		asm volatile("eieio");
136 		asm volatile("sync");
137 	}
138 	asm volatile("tlbsync");
139 	asm volatile("sync");
140 }
141 
142 
143 void
144 arch_cpu_global_TLB_invalidate(void)
145 {
146 	if (sHasTlbia) {
147 		ppc_sync();
148 		tlbia();
149 		ppc_sync();
150 	} else {
151 		addr_t address = 0;
152 		unsigned long i;
153 
154 		ppc_sync();
155 		for (i = 0; i < 0x100000; i++) {
156 			tlbie(address);
157 			eieio();
158 			ppc_sync();
159 
160 			address += B_PAGE_SIZE;
161 		}
162 		tlbsync();
163 		ppc_sync();
164 	}
165 }
166 
167 
168 void
169 arch_cpu_user_TLB_invalidate(void)
170 {
171 	arch_cpu_global_TLB_invalidate();
172 }
173 
174 
175 // TODO: all functions that use fault handlers need to be implemented
176 // in assembly due to problems passing in label addresses in gcc4.
177 
178 status_t
179 arch_cpu_user_memcpy(void *to, const void *from, size_t size,
180 	addr_t *faultHandler)
181 {
182 	char *tmp = (char *)to;
183 	char *s = (char *)from;
184 	addr_t oldFaultHandler = *faultHandler;
185 
186 // TODO: This doesn't work correctly with gcc 4 anymore!
187 	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
188 		goto error;
189 
190 	while (size--)
191 		*tmp++ = *s++;
192 
193 	*faultHandler = oldFaultHandler;
194 	return 0;
195 
196 error:
197 	*faultHandler = oldFaultHandler;
198 	return B_BAD_ADDRESS;
199 }
200 
201 
202 /**	\brief Copies at most (\a size - 1) characters from the string in \a from to
203  *	the string in \a to, NULL-terminating the result.
204  *
205  *	\param to Pointer to the destination C-string.
206  *	\param from Pointer to the source C-string.
207  *	\param size Size in bytes of the string buffer pointed to by \a to.
208  *
209  *	\return strlen(\a from).
210  */
211 
212 ssize_t
213 arch_cpu_user_strlcpy(char *to, const char *from, size_t size, addr_t *faultHandler)
214 {
215 	int from_length = 0;
216 	addr_t oldFaultHandler = *faultHandler;
217 
218 // TODO: This doesn't work correctly with gcc 4 anymore!
219 	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
220 		goto error;
221 
222 	if (size > 0) {
223 		to[--size] = '\0';
224 		// copy
225 		for ( ; size; size--, from_length++, to++, from++) {
226 			if ((*to = *from) == '\0')
227 				break;
228 		}
229 	}
230 	// count any leftover from chars
231 	while (*from++ != '\0')
232 		from_length++;
233 
234 	*faultHandler = oldFaultHandler;
235 	return from_length;
236 
237 error:
238 	*faultHandler = oldFaultHandler;
239 	return B_BAD_ADDRESS;
240 }
241 
242 
243 status_t
244 arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
245 {
246 	char *xs = (char *)s;
247 	addr_t oldFaultHandler = *faultHandler;
248 
249 // TODO: This doesn't work correctly with gcc 4 anymore!
250 	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
251 		goto error;
252 
253 	while (count--)
254 		*xs++ = c;
255 
256 	*faultHandler = oldFaultHandler;
257 	return 0;
258 
259 error:
260 	*faultHandler = oldFaultHandler;
261 	return B_BAD_ADDRESS;
262 }
263 
264 
265 status_t
266 arch_cpu_shutdown(bool reboot)
267 {
268 	PPCPlatform::Default()->ShutDown(reboot);
269 	return B_ERROR;
270 }
271 
272 
273 // The purpose of this function is to trick the compiler. When setting the
274 // page_handler to a label that is obviously (to the compiler) never used,
275 // it may reorganize the control flow, so that the labeled part is optimized
276 // away.
277 // By invoking the function like this
278 //
279 //	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
280 //		goto error;
281 //
282 // the compiler has to keep the labeled code, since it can't guess the return
283 // value of this (non-inlinable) function. At least in my tests it worked that
284 // way, and I hope it will continue to work like this in the future.
285 //
286 bool
287 ppc_set_fault_handler(addr_t *handlerLocation, addr_t handler)
288 {
289 // TODO: This doesn't work correctly with gcc 4 anymore!
290 	*handlerLocation = handler;
291 	return false;
292 }
293