1/*
2 * Copyright (c) 2004-2020, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Stefano Ceccherini
7 *		Axel D��rfler, axeld@pinc-software.de
8 *		Pawe�� Dziepak, pdziepak@quarnos.org
9 *		Ingo Weinhold, ingo_weinhold@gmx.de
10 */
11
12
13#include <ksystem_info.h>
14#include <system_info.h>
15#include <system_revision.h>
16#include <arch/system_info.h>
17
18#include <string.h>
19
20#include <algorithm>
21
22#include <OS.h>
23#include <KernelExport.h>
24
25#include <AutoDeleter.h>
26
27#include <block_cache.h>
28#include <cpu.h>
29#include <debug.h>
30#include <kernel.h>
31#include <lock.h>
32#include <Notifications.h>
33#include <messaging.h>
34#include <port.h>
35#include <real_time_clock.h>
36#include <sem.h>
37#include <smp.h>
38#include <team.h>
39#include <thread.h>
40#include <util/AutoLock.h>
41#include <vm/vm.h>
42#include <vm/vm_page.h>
43
44
45const static int64 kKernelVersion = 0x1;
46const static char *kKernelName = "kernel_" HAIKU_ARCH;
47
48
49static int
50dump_info(int argc, char **argv)
51{
52	kprintf("kernel build: %s %s (gcc%d %s), debug level %d\n", __DATE__,
53		__TIME__, __GNUC__, __VERSION__, KDEBUG_LEVEL);
54	kprintf("revision: %s\n\n", get_haiku_revision());
55
56	kprintf("cpu count: %" B_PRId32 "\n", smp_get_num_cpus());
57
58	for (int32 i = 0; i < smp_get_num_cpus(); i++)
59		kprintf("  [%" B_PRId32 "] active time: %10" B_PRId64 ", interrupt"
60			" time: %10" B_PRId64 ", irq time: %10" B_PRId64 "\n", i + 1,
61			gCPU[i].active_time, gCPU[i].interrupt_time, gCPU[i].irq_time);
62
63	// ToDo: Add page_faults
64	kprintf("pages:\t\t%" B_PRIuPHYSADDR " (%" B_PRIuPHYSADDR " max)\n",
65		vm_page_num_pages() - vm_page_num_free_pages(), vm_page_num_pages());
66
67	kprintf("sems:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", sem_used_sems(),
68		sem_max_sems());
69	kprintf("ports:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", port_used_ports(),
70			port_max_ports());
71	kprintf("threads:\t%" B_PRId32 " (%" B_PRId32 " max)\n",
72		thread_used_threads(), thread_max_threads());
73	kprintf("teams:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", team_used_teams(),
74		team_max_teams());
75
76	return 0;
77}
78
79
80//	#pragma mark - user notifications
81
82
83class SystemNotificationService : private NotificationListener {
84public:
85	SystemNotificationService()
86	{
87		mutex_init(&fLock, "system notification service");
88	}
89
90	status_t Init()
91	{
92		status_t error = fTeamListeners.Init();
93		if (error != B_OK)
94			return error;
95
96		error = NotificationManager::Manager().AddListener("teams",
97			TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this);
98		if (error != B_OK)
99			return error;
100
101		error = NotificationManager::Manager().AddListener("threads",
102			THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this);
103		if (error != B_OK)
104			return error;
105
106		return B_OK;
107	}
108
109	status_t StartListening(int32 object, uint32 flags, port_id port,
110		int32 token)
111	{
112		// check the parameters
113		if ((object < 0 && object != -1) || port < 0)
114			return B_BAD_VALUE;
115
116		if ((flags & B_WATCH_SYSTEM_ALL) == 0
117			|| (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) {
118			return B_BAD_VALUE;
119		}
120
121		MutexLocker locker(fLock);
122
123		// maybe the listener already exists
124		ListenerList* listenerList;
125		Listener* listener = _FindListener(object, port, token, listenerList);
126		if (listener != NULL) {
127			// just add the new flags
128			listener->flags |= flags;
129			return B_OK;
130		}
131
132		// create a new listener
133		listener = new(std::nothrow) Listener;
134		if (listener == NULL)
135			return B_NO_MEMORY;
136		ObjectDeleter<Listener> listenerDeleter(listener);
137
138		listener->port = port;
139		listener->token = token;
140		listener->flags = flags;
141
142		// if there's no list yet, create a new list
143		if (listenerList == NULL) {
144			listenerList = new(std::nothrow) ListenerList;
145			if (listenerList == NULL)
146				return B_NO_MEMORY;
147
148			listenerList->object = object;
149
150			fTeamListeners.Insert(listenerList);
151		}
152
153		listener->list = listenerList;
154		listenerList->listeners.Add(listener);
155		listenerDeleter.Detach();
156
157		team_associate_data(listener);
158
159		return B_OK;
160	}
161
162	status_t StopListening(int32 object, uint32 flags, port_id port,
163		int32 token)
164	{
165		MutexLocker locker(fLock);
166
167		// find the listener
168		ListenerList* listenerList;
169		Listener* listener = _FindListener(object, port, token, listenerList);
170		if (listener == NULL)
171			return B_ENTRY_NOT_FOUND;
172
173		// clear the given flags
174		listener->flags &= ~flags;
175
176		if (listener->flags != 0)
177			return B_OK;
178
179		team_dissociate_data(listener);
180		_RemoveListener(listener);
181
182		return B_OK;
183	}
184
185private:
186	struct ListenerList;
187
188	struct Listener : AssociatedData {
189		DoublyLinkedListLink<Listener>	listLink;
190		ListenerList*					list;
191		port_id							port;
192		int32							token;
193		uint32							flags;
194
195		virtual void OwnerDeleted(AssociatedDataOwner* owner);
196	};
197
198	friend struct Listener;
199
200	struct ListenerList {
201		typedef DoublyLinkedList<Listener,
202			DoublyLinkedListMemberGetLink<Listener, &Listener::listLink> > List;
203
204		ListenerList*	hashNext;
205		List			listeners;
206		int32			object;
207	};
208
209	struct ListenerHashDefinition {
210		typedef int32			KeyType;
211		typedef	ListenerList	ValueType;
212
213		size_t HashKey(int32 key) const
214		{
215			return key;
216		}
217
218		size_t Hash(const ListenerList* value) const
219		{
220			return HashKey(value->object);
221		}
222
223		bool Compare(int32 key, const ListenerList* value) const
224		{
225			return value->object == key;
226		}
227
228		ListenerList*& GetLink(ListenerList* value) const
229		{
230			return value->hashNext;
231		}
232	};
233
234	typedef BOpenHashTable<ListenerHashDefinition> ListenerHash;
235
236private:
237	virtual void EventOccurred(NotificationService& service,
238		const KMessage* event)
239	{
240		MutexLocker locker(fLock);
241
242		int32 eventCode;
243		int32 teamID = 0;
244		if (event->FindInt32("event", &eventCode) != B_OK
245			|| event->FindInt32("team", &teamID) != B_OK) {
246			return;
247		}
248
249		int32 object;
250		uint32 opcode;
251		uint32 flags;
252
253		// translate the event
254		if (event->What() == TEAM_MONITOR) {
255			switch (eventCode) {
256				case TEAM_ADDED:
257					opcode = B_TEAM_CREATED;
258					flags = B_WATCH_SYSTEM_TEAM_CREATION;
259					break;
260				case TEAM_REMOVED:
261					opcode = B_TEAM_DELETED;
262					flags = B_WATCH_SYSTEM_TEAM_DELETION;
263					break;
264				case TEAM_EXEC:
265					opcode = B_TEAM_EXEC;
266					flags = B_WATCH_SYSTEM_TEAM_CREATION
267						| B_WATCH_SYSTEM_TEAM_DELETION;
268					break;
269				default:
270					return;
271			}
272
273			object = teamID;
274		} else if (event->What() == THREAD_MONITOR) {
275			if (event->FindInt32("thread", &object) != B_OK)
276				return;
277
278			switch (eventCode) {
279				case THREAD_ADDED:
280					opcode = B_THREAD_CREATED;
281					flags = B_WATCH_SYSTEM_THREAD_CREATION;
282					break;
283				case THREAD_REMOVED:
284					opcode = B_THREAD_DELETED;
285					flags = B_WATCH_SYSTEM_THREAD_DELETION;
286					break;
287				case THREAD_NAME_CHANGED:
288					opcode = B_THREAD_NAME_CHANGED;
289					flags = B_WATCH_SYSTEM_THREAD_PROPERTIES;
290					break;
291				default:
292					return;
293			}
294		} else
295			return;
296
297		// find matching listeners
298		messaging_target targets[kMaxMessagingTargetCount];
299		int32 targetCount = 0;
300
301		_AddTargets(fTeamListeners.Lookup(teamID), flags, targets,
302			targetCount, object, opcode);
303		_AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount,
304			object, opcode);
305
306		// send the message
307		if (targetCount > 0)
308			_SendMessage(targets, targetCount, object, opcode);
309	}
310
311	void _AddTargets(ListenerList* listenerList, uint32 flags,
312		messaging_target* targets, int32& targetCount, int32 object,
313		uint32 opcode)
314	{
315		if (listenerList == NULL)
316			return;
317
318		for (ListenerList::List::Iterator it
319				= listenerList->listeners.GetIterator();
320			Listener* listener = it.Next();) {
321			if ((listener->flags & flags) == 0)
322				continue;
323
324			// array is full -- need to flush it first
325			if (targetCount == kMaxMessagingTargetCount) {
326				_SendMessage(targets, targetCount, object, opcode);
327				targetCount = 0;
328			}
329
330			// add the listener
331			targets[targetCount].port = listener->port;
332			targets[targetCount++].token = listener->token;
333		}
334	}
335
336	void _SendMessage(messaging_target* targets, int32 targetCount,
337		int32 object, uint32 opcode)
338	{
339		// prepare the message
340		char buffer[128];
341		KMessage message;
342		message.SetTo(buffer, sizeof(buffer), B_SYSTEM_OBJECT_UPDATE);
343		message.AddInt32("opcode", opcode);
344		if (opcode < B_THREAD_CREATED)
345			message.AddInt32("team", object);
346		else
347			message.AddInt32("thread", object);
348
349		// send it
350		send_message(message.Buffer(), message.ContentSize(), targets,
351			targetCount);
352	}
353
354	Listener* _FindListener(int32 object, port_id port, int32 token,
355		ListenerList*& _listenerList)
356	{
357		_listenerList = fTeamListeners.Lookup(object);
358		if (_listenerList == NULL)
359			return NULL;
360
361		for (ListenerList::List::Iterator it
362				= _listenerList->listeners.GetIterator();
363			Listener* listener = it.Next();) {
364			if (listener->port == port && listener->token == token)
365				return listener;
366		}
367
368		return NULL;
369	}
370
371	void _RemoveObsoleteListener(Listener* listener)
372	{
373		MutexLocker locker(fLock);
374		_RemoveListener(listener);
375	}
376
377	void _RemoveListener(Listener* listener)
378	{
379		// no flags anymore -- remove the listener
380		ListenerList* listenerList = listener->list;
381		listenerList->listeners.Remove(listener);
382		listener->ReleaseReference();
383
384		if (listenerList->listeners.IsEmpty()) {
385			// no listeners in the list anymore -- remove the list from the hash
386			// table
387			fTeamListeners.Remove(listenerList);
388			delete listenerList;
389		}
390	}
391
392private:
393	static const int32 kMaxMessagingTargetCount = 8;
394
395	mutex			fLock;
396	ListenerHash	fTeamListeners;
397};
398
399static SystemNotificationService sSystemNotificationService;
400
401
402void
403SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner)
404{
405	sSystemNotificationService._RemoveObsoleteListener(this);
406}
407
408
409//	#pragma mark - private functions
410
411
412static void
413count_topology_nodes(const cpu_topology_node* node, uint32& count)
414{
415	count++;
416	for (int32 i = 0; i < node->children_count; i++)
417		count_topology_nodes(node->children[i], count);
418}
419
420
421static int32
422get_logical_processor(const cpu_topology_node* node)
423{
424	while (node->level != CPU_TOPOLOGY_SMT) {
425		ASSERT(node->children_count > 0);
426		node = node->children[0];
427	}
428
429	return node->id;
430}
431
432
433static cpu_topology_node_info*
434generate_topology_array(cpu_topology_node_info* topology,
435	const cpu_topology_node* node, uint32& count)
436{
437	if (count == 0)
438		return topology;
439
440	static const topology_level_type mapTopologyLevels[] = { B_TOPOLOGY_SMT,
441		B_TOPOLOGY_CORE, B_TOPOLOGY_PACKAGE, B_TOPOLOGY_ROOT };
442
443	STATIC_ASSERT(sizeof(mapTopologyLevels) / sizeof(topology_level_type)
444		== CPU_TOPOLOGY_LEVELS + 1);
445
446	topology->id = node->id;
447	topology->level = node->level;
448	topology->type = mapTopologyLevels[node->level];
449
450	arch_fill_topology_node(topology, get_logical_processor(node));
451
452	count--;
453	topology++;
454	for (int32 i = 0; i < node->children_count && count > 0; i++)
455		topology = generate_topology_array(topology, node->children[i], count);
456	return topology;
457}
458
459
460//	#pragma mark -
461
462
463status_t
464get_system_info(system_info* info)
465{
466	memset(info, 0, sizeof(system_info));
467
468	info->boot_time = rtc_boot_time();
469	info->cpu_count = smp_get_num_cpus();
470
471	vm_page_get_stats(info);
472	vm_get_info(info);
473
474	info->used_threads = thread_used_threads();
475	info->max_threads = thread_max_threads();
476	info->used_teams = team_used_teams();
477	info->max_teams = team_max_teams();
478	info->used_ports = port_used_ports();
479	info->max_ports = port_max_ports();
480	info->used_sems = sem_used_sems();
481	info->max_sems = sem_max_sems();
482
483	info->kernel_version = kKernelVersion;
484	strlcpy(info->kernel_name, kKernelName, B_FILE_NAME_LENGTH);
485	strlcpy(info->kernel_build_date, __DATE__, B_OS_NAME_LENGTH);
486	strlcpy(info->kernel_build_time, __TIME__, B_OS_NAME_LENGTH);
487	info->abi = B_HAIKU_ABI;
488
489	return B_OK;
490}
491
492
493typedef struct {
494	bigtime_t	active_time;
495	bool		enabled;
496} beta2_cpu_info;
497
498
499extern "C" status_t
500__get_cpu_info(uint32 firstCPU, uint32 cpuCount, beta2_cpu_info* beta2_info)
501{
502	cpu_info info[cpuCount];
503	status_t err = _get_cpu_info_etc(firstCPU, cpuCount, info, sizeof(cpu_info));
504	if (err == B_OK) {
505		for (uint32 i = 0; i < cpuCount; i++) {
506			beta2_info[i].active_time = info[i].active_time;
507			beta2_info[i].enabled = info[i].enabled;
508		}
509	}
510	return err;
511}
512
513
514status_t
515_get_cpu_info_etc(uint32 firstCPU, uint32 cpuCount, cpu_info* info, size_t size)
516{
517	if (cpuCount == 0)
518		return B_OK;
519	if (size != sizeof(cpu_info))
520		return B_BAD_VALUE;
521	if (firstCPU >= (uint32)smp_get_num_cpus())
522		return B_BAD_VALUE;
523
524	const uint32 endCPU = firstCPU + std::min(cpuCount, smp_get_num_cpus() - firstCPU);
525
526	// This function is called very often from userland by applications
527	// that display CPU usage information, so we want to keep this as
528	// optimized and touch as little as possible. Hence, we avoid use
529	// of an allocated temporary buffer.
530
531	cpu_info localInfo[8];
532	for (uint32 cpuIdx = firstCPU; cpuIdx < endCPU; ) {
533		uint32 localIdx;
534		for (localIdx = 0; cpuIdx < endCPU && localIdx < B_COUNT_OF(localInfo);
535				cpuIdx++, localIdx++) {
536			localInfo[localIdx].active_time = cpu_get_active_time(cpuIdx);
537			localInfo[localIdx].enabled = !gCPU[cpuIdx].disabled;
538			localInfo[localIdx].current_frequency = cpu_frequency(cpuIdx);
539		}
540
541		if (user_memcpy(info, localInfo, sizeof(cpu_info) * localIdx) != B_OK)
542			return B_BAD_ADDRESS;
543		info += localIdx;
544	}
545
546	return B_OK;
547}
548
549
550status_t
551system_info_init(struct kernel_args *args)
552{
553	add_debugger_command("info", &dump_info, "System info");
554
555	return arch_system_info_init(args);
556}
557
558
559status_t
560system_notifications_init()
561{
562	new (&sSystemNotificationService) SystemNotificationService;
563
564	status_t error = sSystemNotificationService.Init();
565	if (error != B_OK) {
566		panic("system_info_init(): Failed to init system notification service");
567		return error;
568	}
569
570	return B_OK;
571}
572
573
574//	#pragma mark -
575
576
577status_t
578_user_get_system_info(system_info* userInfo)
579{
580	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
581		return B_BAD_ADDRESS;
582
583	system_info info;
584	status_t status = get_system_info(&info);
585	if (status == B_OK) {
586		if (user_memcpy(userInfo, &info, sizeof(system_info)) < B_OK)
587			return B_BAD_ADDRESS;
588
589		return B_OK;
590	}
591
592	return status;
593}
594
595
596status_t
597_user_get_cpu_info(uint32 firstCPU, uint32 cpuCount, cpu_info* userInfo)
598{
599	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
600		return B_BAD_ADDRESS;
601
602	return _get_cpu_info_etc(firstCPU, cpuCount, userInfo, sizeof(cpu_info));
603}
604
605
606status_t
607_user_get_cpu_topology_info(cpu_topology_node_info* topologyInfos,
608	uint32* topologyInfoCount)
609{
610	if (topologyInfoCount == NULL || !IS_USER_ADDRESS(topologyInfoCount))
611		return B_BAD_ADDRESS;
612
613	const cpu_topology_node* node = get_cpu_topology();
614
615	uint32 count = 0;
616	count_topology_nodes(node, count);
617
618	if (topologyInfos == NULL)
619		return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
620	else if (!IS_USER_ADDRESS(topologyInfos))
621		return B_BAD_ADDRESS;
622
623	uint32 userCount;
624	status_t error = user_memcpy(&userCount, topologyInfoCount, sizeof(uint32));
625	if (error != B_OK)
626		return error;
627	if (userCount == 0)
628		return B_OK;
629	count = std::min(count, userCount);
630
631	cpu_topology_node_info* topology
632		= new(std::nothrow) cpu_topology_node_info[count];
633	if (topology == NULL)
634		return B_NO_MEMORY;
635	ArrayDeleter<cpu_topology_node_info> _(topology);
636	memset(topology, 0, sizeof(cpu_topology_node_info) * count);
637
638	uint32 nodesLeft = count;
639	generate_topology_array(topology, node, nodesLeft);
640	ASSERT(nodesLeft == 0);
641
642	error = user_memcpy(topologyInfos, topology,
643		sizeof(cpu_topology_node_info) * count);
644	if (error != B_OK)
645		return error;
646	return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
647}
648
649
650status_t
651_user_start_watching_system(int32 object, uint32 flags, port_id port,
652	int32 token)
653{
654	return sSystemNotificationService.StartListening(object, flags, port,
655		token);
656}
657
658
659status_t
660_user_stop_watching_system(int32 object, uint32 flags, port_id port,
661	int32 token)
662{
663	return sSystemNotificationService.StopListening(object, flags, port, token);
664}
665