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
17static bool sHasTlbia;
18
19status_t
20arch_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
33status_t
34arch_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
44status_t
45arch_cpu_init_post_vm(kernel_args *args)
46{
47	return B_OK;
48}
49
50status_t
51arch_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
60status_t
61arch_cpu_init_post_modules(kernel_args *args)
62{
63	return B_OK;
64}
65
66#define CACHELINE 32
67
68void
69arch_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
95void
96arch_cpu_memory_read_barrier(void)
97{
98// WARNING PPC: is it model-dependant ?
99	asm volatile ("lwsync");
100}
101
102
103void
104arch_cpu_memory_write_barrier(void)
105{
106// WARNING PPC: is it model-dependant ?
107	asm volatile ("isync");
108	asm volatile ("eieio");
109}
110
111
112void
113arch_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
127void
128arch_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
143void
144arch_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
168void
169arch_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
178status_t
179arch_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
196error:
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
212ssize_t
213arch_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
237error:
238	*faultHandler = oldFaultHandler;
239	return B_BAD_ADDRESS;
240}
241
242
243status_t
244arch_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
259error:
260	*faultHandler = oldFaultHandler;
261	return B_BAD_ADDRESS;
262}
263
264
265status_t
266arch_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//
286bool
287ppc_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