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