1/*
2 * Copyright 2013, Pawe�� Dziepak, pdziepak@quarnos.org.
3 * Copyright 2002-2008, Axel D��rfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
5 *
6 * Copyright 2002, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
9
10/* This file contains the cpu functions (init, etc). */
11
12
13#include <cpu.h>
14#include <arch/cpu.h>
15#include <arch/system_info.h>
16
17#include <string.h>
18
19#include <cpufreq.h>
20#include <cpuidle.h>
21
22#include <boot/kernel_args.h>
23#include <kscheduler.h>
24#include <thread_types.h>
25#include <util/AutoLock.h>
26#include <util/ThreadAutoLock.h>
27
28
29/* global per-cpu structure */
30cpu_ent gCPU[SMP_MAX_CPUS];
31
32uint32 gCPUCacheLevelCount;
33static cpu_topology_node sCPUTopology;
34
35static cpufreq_module_info* sCPUPerformanceModule;
36static cpuidle_module_info* sCPUIdleModule;
37
38static spinlock sSetCpuLock;
39
40
41status_t
42cpu_init(kernel_args *args)
43{
44	return arch_cpu_init(args);
45}
46
47
48status_t
49cpu_init_percpu(kernel_args *args, int curr_cpu)
50{
51	return arch_cpu_init_percpu(args, curr_cpu);
52}
53
54
55status_t
56cpu_init_post_vm(kernel_args *args)
57{
58	return arch_cpu_init_post_vm(args);
59}
60
61
62static void
63load_cpufreq_module()
64{
65	void* cookie = open_module_list(CPUFREQ_MODULES_PREFIX);
66
67	while (true) {
68		char name[B_FILE_NAME_LENGTH];
69		size_t nameLength = sizeof(name);
70		cpufreq_module_info* current = NULL;
71
72		if (read_next_module_name(cookie, name, &nameLength) != B_OK)
73			break;
74
75		if (get_module(name, (module_info**)&current) == B_OK) {
76			dprintf("found cpufreq module: %s\n", name);
77
78			if (sCPUPerformanceModule != NULL) {
79				if (sCPUPerformanceModule->rank < current->rank) {
80					put_module(sCPUPerformanceModule->info.name);
81					sCPUPerformanceModule = current;
82				} else
83					put_module(name);
84			} else
85				sCPUPerformanceModule = current;
86		}
87	}
88
89	close_module_list(cookie);
90
91	if (sCPUPerformanceModule == NULL)
92		dprintf("no valid cpufreq module found\n");
93	else
94		scheduler_update_policy();
95}
96
97
98static void
99load_cpuidle_module()
100{
101	void* cookie = open_module_list(CPUIDLE_MODULES_PREFIX);
102
103	while (true) {
104		char name[B_FILE_NAME_LENGTH];
105		size_t nameLength = sizeof(name);
106		cpuidle_module_info* current = NULL;
107
108		if (read_next_module_name(cookie, name, &nameLength) != B_OK)
109			break;
110
111		if (get_module(name, (module_info**)&current) == B_OK) {
112			dprintf("found cpuidle module: %s\n", name);
113
114			if (sCPUIdleModule != NULL) {
115				if (sCPUIdleModule->rank < current->rank) {
116					put_module(sCPUIdleModule->info.name);
117					sCPUIdleModule = current;
118				} else
119					put_module(name);
120			} else
121				sCPUIdleModule = current;
122		}
123	}
124
125	close_module_list(cookie);
126
127	if (sCPUIdleModule == NULL)
128		dprintf("no valid cpuidle module found\n");
129}
130
131
132status_t
133cpu_init_post_modules(kernel_args *args)
134{
135	status_t result = arch_cpu_init_post_modules(args);
136	if (result != B_OK)
137		return result;
138
139	load_cpufreq_module();
140	load_cpuidle_module();
141	return B_OK;
142}
143
144
145status_t
146cpu_preboot_init_percpu(kernel_args *args, int curr_cpu)
147{
148	// set the cpu number in the local cpu structure so that
149	// we can use it for get_current_cpu
150	memset(&gCPU[curr_cpu], 0, sizeof(gCPU[curr_cpu]));
151	gCPU[curr_cpu].cpu_num = curr_cpu;
152
153	list_init(&gCPU[curr_cpu].irqs);
154	B_INITIALIZE_SPINLOCK(&gCPU[curr_cpu].irqs_lock);
155
156	return arch_cpu_preboot_init_percpu(args, curr_cpu);
157}
158
159
160bigtime_t
161cpu_get_active_time(int32 cpu)
162{
163	if (cpu < 0 || cpu > smp_get_num_cpus())
164		return 0;
165
166	bigtime_t activeTime;
167	uint32 count;
168
169	do {
170		count = acquire_read_seqlock(&gCPU[cpu].active_time_lock);
171		activeTime = gCPU[cpu].active_time;
172	} while (!release_read_seqlock(&gCPU[cpu].active_time_lock, count));
173
174	return activeTime;
175}
176
177
178uint64
179cpu_frequency(int32 cpu)
180{
181	if (cpu < 0 || cpu >= smp_get_num_cpus())
182		return 0;
183	uint64 frequency = 0;
184	arch_get_frequency(&frequency, cpu);
185	return frequency;
186}
187
188
189void
190clear_caches(void *address, size_t length, uint32 flags)
191{
192	// TODO: data cache
193	if ((B_INVALIDATE_ICACHE & flags) != 0) {
194		arch_cpu_sync_icache(address, length);
195	}
196}
197
198
199static status_t
200cpu_create_topology_node(cpu_topology_node* node, int32* maxID, int32 id)
201{
202	cpu_topology_level level = static_cast<cpu_topology_level>(node->level - 1);
203	ASSERT(level >= 0);
204
205	cpu_topology_node* newNode = new(std::nothrow) cpu_topology_node;
206	if (newNode == NULL)
207		return B_NO_MEMORY;
208	node->children[id] = newNode;
209
210	newNode->level = level;
211	if (level != CPU_TOPOLOGY_SMT) {
212		newNode->children_count = maxID[level - 1];
213		newNode->children
214			= new(std::nothrow) cpu_topology_node*[maxID[level - 1]];
215		if (newNode->children == NULL)
216			return B_NO_MEMORY;
217
218		memset(newNode->children, 0,
219			maxID[level - 1] * sizeof(cpu_topology_node*));
220	} else {
221		newNode->children_count = 0;
222		newNode->children = NULL;
223	}
224
225	return B_OK;
226}
227
228
229static void
230cpu_rebuild_topology_tree(cpu_topology_node* node, int32* lastID)
231{
232	if (node->children == NULL)
233		return;
234
235	int32 count = 0;
236	for (int32 i = 0; i < node->children_count; i++) {
237		if (node->children[i] == NULL)
238			continue;
239
240		if (count != i)
241			node->children[count] = node->children[i];
242
243		if (node->children[count]->level != CPU_TOPOLOGY_SMT)
244			node->children[count]->id = lastID[node->children[count]->level]++;
245
246		cpu_rebuild_topology_tree(node->children[count], lastID);
247		count++;
248	}
249	node->children_count = count;
250}
251
252
253status_t
254cpu_build_topology_tree(void)
255{
256	sCPUTopology.level = CPU_TOPOLOGY_LEVELS;
257
258	int32 maxID[CPU_TOPOLOGY_LEVELS];
259	memset(&maxID, 0, sizeof(maxID));
260
261	const int32 kCPUCount = smp_get_num_cpus();
262	for (int32 i = 0; i < kCPUCount; i++) {
263		for (int32 j = 0; j < CPU_TOPOLOGY_LEVELS; j++)
264			maxID[j] = max_c(maxID[j], gCPU[i].topology_id[j]);
265	}
266
267	for (int32 j = 0; j < CPU_TOPOLOGY_LEVELS; j++)
268		maxID[j]++;
269
270	sCPUTopology.children_count = maxID[CPU_TOPOLOGY_LEVELS - 1];
271	sCPUTopology.children
272		= new(std::nothrow) cpu_topology_node*[maxID[CPU_TOPOLOGY_LEVELS - 1]];
273	if (sCPUTopology.children == NULL)
274		return B_NO_MEMORY;
275	memset(sCPUTopology.children, 0,
276		maxID[CPU_TOPOLOGY_LEVELS - 1] * sizeof(cpu_topology_node*));
277
278	for (int32 i = 0; i < kCPUCount; i++) {
279		cpu_topology_node* node = &sCPUTopology;
280		for (int32 j = CPU_TOPOLOGY_LEVELS - 1; j >= 0; j--) {
281			int32 id = gCPU[i].topology_id[j];
282			if (node->children[id] == NULL) {
283				status_t result = cpu_create_topology_node(node, maxID, id);
284				if (result != B_OK)
285					return result;
286			}
287
288			node = node->children[id];
289		}
290
291		ASSERT(node->level == CPU_TOPOLOGY_SMT);
292		node->id = i;
293	}
294
295	int32 lastID[CPU_TOPOLOGY_LEVELS];
296	memset(&lastID, 0, sizeof(lastID));
297	cpu_rebuild_topology_tree(&sCPUTopology, lastID);
298
299	return B_OK;
300}
301
302
303const cpu_topology_node*
304get_cpu_topology(void)
305{
306	return &sCPUTopology;
307}
308
309
310void
311cpu_set_scheduler_mode(enum scheduler_mode mode)
312{
313	if (sCPUPerformanceModule != NULL)
314		sCPUPerformanceModule->cpufreq_set_scheduler_mode(mode);
315	if (sCPUIdleModule != NULL)
316		sCPUIdleModule->cpuidle_set_scheduler_mode(mode);
317}
318
319
320status_t
321increase_cpu_performance(int delta)
322{
323	if (sCPUPerformanceModule != NULL)
324		return sCPUPerformanceModule->cpufreq_increase_performance(delta);
325	return B_NOT_SUPPORTED;
326}
327
328
329status_t
330decrease_cpu_performance(int delta)
331{
332	if (sCPUPerformanceModule != NULL)
333		return sCPUPerformanceModule->cpufreq_decrease_performance(delta);
334	return B_NOT_SUPPORTED;
335}
336
337
338void
339cpu_idle(void)
340{
341#if KDEBUG
342	if (!are_interrupts_enabled())
343		panic("cpu_idle() called with interrupts disabled.");
344#endif
345
346	if (sCPUIdleModule != NULL)
347		sCPUIdleModule->cpuidle_idle();
348	else
349		arch_cpu_idle();
350}
351
352
353void
354cpu_wait(int32* variable, int32 test)
355{
356	if (sCPUIdleModule != NULL)
357		sCPUIdleModule->cpuidle_wait(variable, test);
358	else
359		arch_cpu_pause();
360}
361
362
363//	#pragma mark -
364
365
366void
367_user_clear_caches(void *address, size_t length, uint32 flags)
368{
369	clear_caches(address, length, flags);
370}
371
372
373bool
374_user_cpu_enabled(int32 cpu)
375{
376	if (cpu < 0 || cpu >= smp_get_num_cpus())
377		return false;
378
379	return !gCPU[cpu].disabled;
380}
381
382
383status_t
384_user_set_cpu_enabled(int32 cpu, bool enabled)
385{
386	int32 i, count;
387
388	if (geteuid() != 0)
389		return B_PERMISSION_DENIED;
390	if (cpu < 0 || cpu >= smp_get_num_cpus())
391		return B_BAD_VALUE;
392
393	// We need to lock here to make sure that no one can disable
394	// the last CPU
395
396	InterruptsSpinLocker locker(sSetCpuLock);
397
398	if (!enabled) {
399		// check if this is the last CPU to be disabled
400		for (i = 0, count = 0; i < smp_get_num_cpus(); i++) {
401			if (!gCPU[i].disabled)
402				count++;
403		}
404
405		if (count == 1)
406			return B_NOT_ALLOWED;
407	}
408
409	bool oldState = gCPU[cpu].disabled;
410
411	if (oldState != !enabled)
412		scheduler_set_cpu_enabled(cpu, enabled);
413
414	if (!enabled) {
415		if (smp_get_current_cpu() == cpu) {
416			locker.Unlock();
417			thread_yield();
418			locker.Lock();
419		}
420
421		// someone reenabled the CPU while we were rescheduling
422		if (!gCPU[cpu].disabled)
423			return B_OK;
424
425		ASSERT(smp_get_current_cpu() != cpu);
426		while (!thread_is_idle_thread(gCPU[cpu].running_thread)) {
427			locker.Unlock();
428			thread_yield();
429			locker.Lock();
430
431			if (!gCPU[cpu].disabled)
432				return B_OK;
433			ASSERT(smp_get_current_cpu() != cpu);
434		}
435	}
436
437	return B_OK;
438}
439
440