1/*
2 * Copyright 2004-2008, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <arch/system_info.h>
8
9#include <string.h>
10
11#include <KernelExport.h>
12#include <OS.h>
13
14#include <boot/kernel_args.h>
15#include <cpu.h>
16#include <kernel.h>
17#include <smp.h>
18
19
20enum cpu_vendor sCPUVendor;
21uint32 sCPUModel;
22int64 sCPUClockSpeed;
23
24
25static bool
26get_cpuid_for(cpuid_info *info, uint32 currentCPU, uint32 eaxRegister,
27	uint32 forCPU)
28{
29	if (currentCPU != forCPU)
30		return false;
31
32	get_current_cpuid(info, eaxRegister, 0);
33	return true;
34}
35
36
37status_t
38get_cpuid(cpuid_info *info, uint32 eaxRegister, uint32 forCPU)
39{
40	uint32 numCPUs = (uint32)smp_get_num_cpus();
41	cpu_status state;
42
43	if (forCPU >= numCPUs)
44		return B_BAD_VALUE;
45
46	// prevent us from being rescheduled
47	state = disable_interrupts();
48
49	// ToDo: as long as we only run on pentium-class systems, we can assume
50	//	that the CPU supports cpuid.
51
52	if (!get_cpuid_for(info, smp_get_current_cpu(), eaxRegister, forCPU)) {
53		smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (addr_t)info,
54			eaxRegister, forCPU, (void *)get_cpuid_for, SMP_MSG_FLAG_SYNC);
55	}
56
57	restore_interrupts(state);
58	return B_OK;
59}
60
61
62status_t
63arch_system_info_init(struct kernel_args *args)
64{
65	// So far we don't have to care about heterogeneous x86 platforms.
66	cpu_ent* cpu = get_cpu_struct();
67
68	switch (cpu->arch.vendor) {
69		case VENDOR_AMD:
70			sCPUVendor = B_CPU_VENDOR_AMD;
71			break;
72		case VENDOR_CENTAUR:
73			sCPUVendor = B_CPU_VENDOR_VIA;
74			break;
75		case VENDOR_CYRIX:
76			sCPUVendor = B_CPU_VENDOR_CYRIX;
77			break;
78		case VENDOR_INTEL:
79			sCPUVendor = B_CPU_VENDOR_INTEL;
80			break;
81		case VENDOR_NSC:
82			sCPUVendor = B_CPU_VENDOR_NATIONAL_SEMICONDUCTOR;
83			break;
84		case VENDOR_RISE:
85			sCPUVendor = B_CPU_VENDOR_RISE;
86			break;
87		case VENDOR_TRANSMETA:
88			sCPUVendor = B_CPU_VENDOR_TRANSMETA;
89			break;
90		case VENDOR_HYGON:
91			sCPUVendor = B_CPU_VENDOR_HYGON;
92			break;
93		default:
94			sCPUVendor = B_CPU_VENDOR_UNKNOWN;
95			break;
96	}
97
98	sCPUModel = (cpu->arch.extended_family << 20)
99		| (cpu->arch.extended_model << 16) | (cpu->arch.type << 12)
100		| (cpu->arch.family << 8) | (cpu->arch.model << 4) | cpu->arch.stepping;
101
102	sCPUClockSpeed = args->arch_args.cpu_clock_speed;
103	if (cpu->arch.vendor == VENDOR_INTEL) {
104		cpuid_info cpuid;
105		get_current_cpuid(&cpuid, 0, 0);
106		uint32 maxBasicLeaf = cpuid.eax_0.max_eax;
107		if (maxBasicLeaf >= 0x16) {
108			get_current_cpuid(&cpuid, 0x16, 0);
109			if (cpuid.regs.eax != 0) {
110				sCPUClockSpeed = cpuid.regs.eax * 1000000LL;
111				dprintf("found clock speed with CPUID.16h\n");
112			}
113		}
114	}
115	return B_OK;
116}
117
118
119void
120arch_fill_topology_node(cpu_topology_node_info* node, int32 cpu)
121{
122	switch (node->type) {
123		case B_TOPOLOGY_ROOT:
124#if __i386__
125			node->data.root.platform = B_CPU_x86;
126#elif __x86_64__
127			node->data.root.platform = B_CPU_x86_64;
128#else
129			node->data.root.platform = B_CPU_UNKNOWN;
130#endif
131			break;
132
133		case B_TOPOLOGY_PACKAGE:
134			node->data.package.vendor = sCPUVendor;
135			node->data.package.cache_line_size = CACHE_LINE_SIZE;
136			break;
137
138		case B_TOPOLOGY_CORE:
139			node->data.core.model = sCPUModel;
140			node->data.core.default_frequency = sCPUClockSpeed;
141			break;
142
143		default:
144			break;
145	}
146}
147
148
149static void
150get_frequency_for(void *_frequency, int cpu)
151{
152	uint64 *frequency = (uint64*)_frequency;
153
154	bigtime_t timestamp = gCPU[cpu].arch.perf_timestamp;
155	bigtime_t timestamp2 = system_time();
156	if (timestamp2 - timestamp < 100) {
157		*frequency = gCPU[cpu].arch.frequency;
158		return;
159	}
160
161	uint64 mperf = gCPU[cpu].arch.mperf_prev;
162	uint64 aperf = gCPU[cpu].arch.aperf_prev;
163	uint64 mperf2 = x86_read_msr(IA32_MSR_MPERF);
164	uint64 aperf2 = x86_read_msr(IA32_MSR_APERF);
165
166	if (mperf2 == mperf)
167		*frequency = 0;
168	else {
169		*frequency = (aperf2 - aperf) * sCPUClockSpeed / (mperf2 - mperf);
170		gCPU[cpu].arch.mperf_prev = mperf2;
171		gCPU[cpu].arch.aperf_prev = aperf2;
172		gCPU[cpu].arch.perf_timestamp = timestamp2;
173		gCPU[cpu].arch.frequency = *frequency;
174	}
175}
176
177
178status_t
179arch_get_frequency(uint64 *frequency, int32 cpu)
180{
181	if (x86_check_feature(IA32_FEATURE_APERFMPERF, FEATURE_6_ECX))
182		call_single_cpu_sync(cpu, get_frequency_for, frequency);
183	else
184		*frequency = sCPUClockSpeed;
185
186	return B_OK;
187}
188
189
190//	#pragma mark -
191
192
193status_t
194_user_get_cpuid(cpuid_info *userInfo, uint32 eaxRegister, uint32 cpuNum)
195{
196	cpuid_info info;
197	status_t status;
198
199	if (!IS_USER_ADDRESS(userInfo))
200		return B_BAD_ADDRESS;
201
202	status = get_cpuid(&info, eaxRegister, cpuNum);
203
204	if (status == B_OK
205		&& user_memcpy(userInfo, &info, sizeof(cpuid_info)) < B_OK)
206		return B_BAD_ADDRESS;
207
208	return status;
209}
210
211