xref: /haiku/src/system/kernel/arch/ppc/arch_cpu.cpp (revision d157bf8522d5dc449602bec43f10ecdedc9943cd)
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_thread.h>
14 #include <arch/cpu.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_post_modules(kernel_args *args)
52 {
53 	return B_OK;
54 }
55 
56 #define CACHELINE 32
57 
58 void
59 arch_cpu_sync_icache(void *address, size_t len)
60 {
61 	int l, off;
62 	char *p;
63 
64 	off = (unsigned int)address & (CACHELINE - 1);
65 	len += off;
66 
67 	l = len;
68 	p = (char *)address - off;
69 	do {
70 		asm volatile ("dcbst 0,%0" :: "r"(p));
71 		p += CACHELINE;
72 	} while ((l -= CACHELINE) > 0);
73 	asm volatile ("sync");
74 
75 	p = (char *)address - off;
76 	do {
77 		asm volatile ("icbi 0,%0" :: "r"(p));
78 		p += CACHELINE;
79 	} while ((len -= CACHELINE) > 0);
80 	asm volatile ("sync");
81 	isync();
82 }
83 
84 
85 void
86 arch_cpu_memory_read_barrier(void)
87 {
88 // WARNING PPC: is it model-dependant ?
89 	asm volatile ("lwsync");
90 }
91 
92 
93 void
94 arch_cpu_memory_write_barrier(void)
95 {
96 // WARNING PPC: is it model-dependant ?
97 	asm volatile ("isync");
98 	asm volatile ("eieio");
99 }
100 
101 
102 void
103 arch_cpu_invalidate_TLB_range(addr_t start, addr_t end)
104 {
105 	asm volatile("sync");
106 	while (start < end) {
107 		asm volatile("tlbie %0" :: "r" (start));
108 		asm volatile("eieio");
109 		asm volatile("sync");
110 		start += B_PAGE_SIZE;
111 	}
112 	asm volatile("tlbsync");
113 	asm volatile("sync");
114 }
115 
116 
117 void
118 arch_cpu_invalidate_TLB_list(addr_t pages[], int num_pages)
119 {
120 	int i;
121 
122 	asm volatile("sync");
123 	for (i = 0; i < num_pages; i++) {
124 		asm volatile("tlbie %0" :: "r" (pages[i]));
125 		asm volatile("eieio");
126 		asm volatile("sync");
127 	}
128 	asm volatile("tlbsync");
129 	asm volatile("sync");
130 }
131 
132 
133 void
134 arch_cpu_global_TLB_invalidate(void)
135 {
136 	if (sHasTlbia) {
137 		ppc_sync();
138 		tlbia();
139 		ppc_sync();
140 	} else {
141 		addr_t address = 0;
142 		unsigned long i;
143 
144 		ppc_sync();
145 		for (i = 0; i < 0x100000; i++) {
146 			tlbie(address);
147 			eieio();
148 			ppc_sync();
149 
150 			address += B_PAGE_SIZE;
151 		}
152 		tlbsync();
153 		ppc_sync();
154 	}
155 }
156 
157 
158 void
159 arch_cpu_user_TLB_invalidate(void)
160 {
161 	arch_cpu_global_TLB_invalidate();
162 }
163 
164 
165 status_t
166 arch_cpu_user_memcpy(void *to, const void *from, size_t size,
167 	addr_t *faultHandler)
168 {
169 	char *tmp = (char *)to;
170 	char *s = (char *)from;
171 	addr_t oldFaultHandler = *faultHandler;
172 
173 	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
174 		goto error;
175 
176 	while (size--)
177 		*tmp++ = *s++;
178 
179 	*faultHandler = oldFaultHandler;
180 	return 0;
181 
182 error:
183 	*faultHandler = oldFaultHandler;
184 	return B_BAD_ADDRESS;
185 }
186 
187 
188 /**	\brief Copies at most (\a size - 1) characters from the string in \a from to
189  *	the string in \a to, NULL-terminating the result.
190  *
191  *	\param to Pointer to the destination C-string.
192  *	\param from Pointer to the source C-string.
193  *	\param size Size in bytes of the string buffer pointed to by \a to.
194  *
195  *	\return strlen(\a from).
196  */
197 
198 ssize_t
199 arch_cpu_user_strlcpy(char *to, const char *from, size_t size, addr_t *faultHandler)
200 {
201 	int from_length = 0;
202 	addr_t oldFaultHandler = *faultHandler;
203 
204 	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
205 		goto error;
206 
207 	if (size > 0) {
208 		to[--size] = '\0';
209 		// copy
210 		for ( ; size; size--, from_length++, to++, from++) {
211 			if ((*to = *from) == '\0')
212 				break;
213 		}
214 	}
215 	// count any leftover from chars
216 	while (*from++ != '\0')
217 		from_length++;
218 
219 	*faultHandler = oldFaultHandler;
220 	return from_length;
221 
222 error:
223 	*faultHandler = oldFaultHandler;
224 	return B_BAD_ADDRESS;
225 }
226 
227 
228 status_t
229 arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
230 {
231 	char *xs = (char *)s;
232 	addr_t oldFaultHandler = *faultHandler;
233 
234 	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
235 		goto error;
236 
237 	while (count--)
238 		*xs++ = c;
239 
240 	*faultHandler = oldFaultHandler;
241 	return 0;
242 
243 error:
244 	*faultHandler = oldFaultHandler;
245 	return B_BAD_ADDRESS;
246 }
247 
248 
249 status_t
250 arch_cpu_shutdown(bool reboot)
251 {
252 	PPCPlatform::Default()->ShutDown(reboot);
253 	return B_ERROR;
254 }
255 
256 
257 void
258 arch_cpu_idle(void)
259 {
260 }
261 
262 
263 // The purpose of this function is to trick the compiler. When setting the
264 // page_handler to a label that is obviously (to the compiler) never used,
265 // it may reorganize the control flow, so that the labeled part is optimized
266 // away.
267 // By invoking the function like this
268 //
269 //	if (ppc_set_fault_handler(faultHandler, (addr_t)&&error))
270 //		goto error;
271 //
272 // the compiler has to keep the labeled code, since it can't guess the return
273 // value of this (non-inlinable) function. At least in my tests it worked that
274 // way, and I hope it will continue to work like this in the future.
275 //
276 bool
277 ppc_set_fault_handler(addr_t *handlerLocation, addr_t handler)
278 {
279 	*handlerLocation = handler;
280 	return false;
281 }
282