From 0edeb4b6cc28b7233212219a46ab2981cac8fb33 Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Sat, 1 Jan 2022 21:15:37 +0000
Subject: [PATCH] use KDeviceSendConnectedMessage for graphics targets; upload
 helpful commands for gdb

---
 desktop/desktop.cpp         |  12 +--
 desktop/prefix.h            |   1 -
 help/Debugging.md           | 146 ++++++++++++++++++++++++++++++++++++
 help/Notes from Discord.txt |  10 +++
 kernel/graphics.cpp         |  18 ++---
 5 files changed, 171 insertions(+), 16 deletions(-)

diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp
index 42d221c..ed8cb96 100644
--- a/desktop/desktop.cpp
+++ b/desktop/desktop.cpp
@@ -3116,6 +3116,12 @@ void DesktopSendMessage(EsMessage *message) {
 					+ EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("clock_offset_ms"), 0);
 				EsEventSet(desktop.clockReadyEvent);
 			}
+		} else if (message->device.type == ES_DEVICE_GRAPHICS_TARGET) {
+			if (desktop.setupDesktopUIComplete) {
+				DesktopSetup(); // Refresh desktop UI.
+			} else {
+				// The screen resolution will be correctly queried in DesktopSetup.
+			}
 		}
 	} else if (message->type == ES_MSG_UNREGISTER_FILE_SYSTEM || message->type == ES_MSG_DEVICE_DISCONNECTED) {
 		for (uintptr_t i = 0; i < desktop.allApplicationProcesses.Length(); i++) {
@@ -3137,12 +3143,6 @@ void DesktopSendMessage(EsMessage *message) {
 
 			EsMessagePostRemote(process->handle, message);
 		}
-	} else if (message->type == ES_MSG_SET_SCREEN_RESOLUTION) {
-		if (desktop.setupDesktopUIComplete) {
-			DesktopSetup(); // Refresh desktop UI.
-		} else {
-			// The screen resolution will be correctly queried in DesktopSetup.
-		}
 	} else if (message->type == ES_MSG_KEY_DOWN) {
 		ProcessGlobalKeyboardShortcuts(nullptr, message);
 	} else if (message->type == MSG_SETUP_DESKTOP_UI || message->type == ES_MSG_UI_SCALE_CHANGED) {
diff --git a/desktop/prefix.h b/desktop/prefix.h
index cdb1e58..decdb82 100644
--- a/desktop/prefix.h
+++ b/desktop/prefix.h
@@ -399,7 +399,6 @@ extern "C" void *EsBufferWrite(EsBuffer *buffer, const void *source, size_t writ
 
 /* Desktop messages: */
 #define ES_MSG_EMBEDDED_WINDOW_DESTROYED 	((EsMessageType) (ES_MSG_SYSTEM_START + 0x001))
-#define ES_MSG_SET_SCREEN_RESOLUTION		((EsMessageType) (ES_MSG_SYSTEM_START + 0x002))
 #define ES_MSG_DESKTOP	                        ((EsMessageType) (ES_MSG_SYSTEM_START + 0x005))
 
 /* Messages sent from Desktop to application instances: */
diff --git a/help/Debugging.md b/help/Debugging.md
index 10d3c1c..6bac76e 100644
--- a/help/Debugging.md
+++ b/help/Debugging.md
@@ -3,3 +3,149 @@
 This file has not been written yet. `Notes from Discord.txt` may contain a few unorganized pointers, though.
 
 TODO
+
+## GDB commands
+
+Here are various commands for GDB for inspecting kernel state. Put these in your `.gdbinit` file if you want to use them. *Please note that some of these may be out of date.*
+
+The `ldcx` command is used to load the RIP, RSP and RBP registers from an `InterruptContext`.
+
+	define ldcx
+		set $new_rip = context->rip
+		set $new_rsp = context->rsp
+		set $new_rbp = context->rbp
+		set $rip = $new_rip
+		set $rsp = $new_rsp
+		set $rbp = $new_rbp
+		set $rip = $new_rip
+	end
+
+	define ldcx32
+		set $new_eip = context->eip
+		set $new_esp = context->esp
+		set $new_ebp = context->ebp
+		set $eip = $new_eip
+		set $esp = $new_esp
+		set $ebp = $new_ebp
+		set $eip = $new_eip
+	end
+
+The `print_page_list` command follows and prints a linked list from the physical memory manager's page frame database.
+
+	define print_page_list
+		set $index = $arg0
+		while $index
+			set $page_frame = pmm.pageFrames[$index]
+			p $index
+			p $page_frame
+			set $index = $page_frame.list.next
+		end
+	end
+
+Here are some Python commands. `PrintKernelMMSpaceRegions` prints the memory regions in the kernel's memory space. `PrintDeviceTree` prints all devices in the kernel's device tree, including the number of open reference to each device. `PrintBootFileSystemNodes` prints all the loaded nodes in the boot filesystem ("0:/"). `PrintCachedNodes` prints all the nodes that are in cached but have no open references. This also installs hooks for the gf GDB frontend watch window for `EsRectangle` and `Array<T>`.
+
+	py
+
+	def EsRectangleHook(item, field):
+		if field:
+			if field == '[width]':  return gdb.Value(int(item['r']) - item['l'])
+			if field == '[height]': return gdb.Value(int(item['b']) - item['t'])
+		else:
+			print('[width]')
+			print('[height]')
+			_gf_fields_recurse(item)
+
+	def EsArrayHook(item, field):
+		if field:
+			return item['array'][int(field[1:-1])]
+		else:
+			print('(d_arr)',ArrayLength(item['array']))
+
+	def ArrayLength(arrayBase):
+		if int(arrayBase):
+			return int(gdb.parse_and_eval('(_ArrayHeader*)' + str(int(arrayBase)) + '-1')['length'])
+		return 0
+
+	def PrintAVLKey(key, end='\n'):
+		charPointer = gdb.lookup_type('char').pointer()
+		nameBytes = int(key['longKeyBytes'])
+		name = key['longKey'].cast(charPointer)
+		print(name.string('utf-8', 'strict', nameBytes), end=end)
+
+	def PrintNodePath(node, end='\n'):
+		if isinstance(node, str):
+			node = gdb.parse_and_eval(node)
+		parent = node['directoryEntry']['parent']
+		if int(parent):
+			PrintNodePath(parent, end='/')
+		key = node['directoryEntry']['item']['key']
+		PrintAVLKey(key, end)
+
+	def PrintCachedNodes():
+		item = gdb.parse_and_eval('fs.cachedNodes.firstItem')
+		offset = int(gdb.parse_and_eval('(uintptr_t)(&((KNode*)0)->cacheItem)-(uintptr_t)((KNode*)0)'))
+		while int(item):
+			node = gdb.parse_and_eval('(KNode*)' + str(int(item) - offset))
+			PrintNodePath(node)
+			item = item['nextItem']
+
+	def PrintDirectoryEntryAndChildren(directoryEntry, indent=''):
+		if isinstance(directoryEntry, str):
+			directoryEntry = gdb.parse_and_eval(directoryEntry)
+		print('"', end='')
+		PrintAVLKey(directoryEntry['item']['key'], end='"')
+		node = directoryEntry['node']
+		recurse = None
+		if int(node):
+			print(', node H' + str(int(node['handles'])), end='')
+			if int(directoryEntry['type']) == 0x10:
+				print(', dir', end='')
+				fsDirectory = node.cast(gdb.lookup_type('FSDirectory').pointer())
+				recurse = fsDirectory['entries']['root']
+		print('')
+		if recurse:
+			RecurseIntoNodeAVLTree(recurse, indent + '\t')
+		
+	def RecurseIntoNodeAVLTree(item, indent):
+		if not int(item):
+			return
+		RecurseIntoNodeAVLTree(item['children'][0], indent)
+		RecurseIntoNodeAVLTree(item['children'][1], indent)
+		print(indent, end='')
+		PrintDirectoryEntryAndChildren(item['thisItem'], indent)
+		
+	def PrintBootFileSystemNodes():
+		PrintDirectoryEntryAndChildren('fs.bootFileSystem.rootDirectory.directoryEntry')
+
+	def RecurseIntoDeviceTree(device, indent):
+		print(indent, device['cDebugName'].string('utf-8'), device['handles'])
+		childCount = ArrayLength(device['children']['array'])
+		for i in range(childCount):
+			RecurseIntoDeviceTree(device['children']['array'][i], indent + ' | ')
+
+	def PrintDeviceTree():
+		RecurseIntoDeviceTree(gdb.parse_and_eval('deviceTreeRoot'), '')
+
+	def PrintMemoryRegion(region):
+		print('region: ' + hex(int(region)))
+		print('\tbaseAddress: ' + hex(int(region['baseAddress'])))
+		print('\tsize: ' + str(4 * int(region['pageCount'])) + ' KB')
+		flags = int(region['flags'])
+		print('\tflags: ' + hex(flags))
+		if flags & 0x200:
+			print('\tnormal region')
+			print('\t\tcommit: ' + str(4 * int(region['data']['normal']['commitPageCount'])) + ' KB')
+		
+	def RecurseIntoMMSpaceAVLTree(item):
+		if not int(item):
+			return
+		RecurseIntoMMSpaceAVLTree(item['children'][0])
+		RecurseIntoMMSpaceAVLTree(item['children'][1])
+		PrintMemoryRegion(item['thisItem'])
+		
+	def PrintKernelMMSpaceRegions():
+		RecurseIntoMMSpaceAVLTree(gdb.parse_and_eval('_kernelMMSpace.usedRegions.root'))
+
+	gf_hooks = { 'EsRectangle': EsRectangleHook, 'Array': EsArrayHook }
+
+	end
diff --git a/help/Notes from Discord.txt b/help/Notes from Discord.txt
index c97e671..6f6997c 100644
--- a/help/Notes from Discord.txt	
+++ b/help/Notes from Discord.txt	
@@ -355,3 +355,13 @@ nakst
 Today at 8:57 AM
 [gs:0] points to the CPULocalStorage, [gs:8] contains the address of the kernel stack (so it can be switched to in a syscall), [gs:16] points to the current thread
 You can't get the thread from the CPULocalStorage in most situations (i.e. with interrupts enabled), because it will change if the thread is rescheduled on a different CPU, and then you'd read the wrong thread 
+
+= Format specifiers for EsStringFormat/EsPrint/EsPanic/KernelPanic/KernelLog =
+
+%d formats a long
+%i formats a int
+%x formats a uintptr_t
+%c formats a char
+%z formats a zero-terminated const char *
+%s formats a non-zero-terminated string; pass the length ptrdiff_t first, then the pointer to base of the string const char *
+%F formats a double
diff --git a/kernel/graphics.cpp b/kernel/graphics.cpp
index c9ab039..f3f9fdf 100644
--- a/kernel/graphics.cpp
+++ b/kernel/graphics.cpp
@@ -30,6 +30,7 @@ struct Graphics {
 	Surface frameBuffer;
 	bool debuggerActive;
 	size_t totalSurfaceBytes;
+	KMutex registerFirstGraphicsTargetMutex;
 };
 
 void GraphicsUpdateScreen(K_USER_BUFFER void *bits = nullptr, EsRectangle *bounds = nullptr, uintptr_t stride = 0);
@@ -102,14 +103,17 @@ bool KGraphicsIsTargetRegistered() {
 }
 
 void KRegisterGraphicsTarget(KGraphicsTarget *target) {
-	// TODO Locking.
-	if (graphics.target) return;
+	// TODO Multi-monitor support.
 
-	graphics.target = target;
+	KMutexAcquire(&graphics.registerFirstGraphicsTargetMutex);
+	bool isFirst = graphics.target == nullptr;
+	if (isFirst) graphics.target = target;
+	KMutexRelease(&graphics.registerFirstGraphicsTargetMutex);
+	if (!isFirst) return;
 
+	KDeviceOpenHandle(target);
 	graphics.width = target->screenWidth;
 	graphics.height = target->screenHeight;
-
 	graphics.frameBuffer.Resize(graphics.width, graphics.height);
 
 #ifdef START_DEBUG_OUTPUT
@@ -117,11 +121,7 @@ void KRegisterGraphicsTarget(KGraphicsTarget *target) {
 	EsPrint("Hello\n");
 #else
 	windowManager.Initialise();
-
-	_EsMessageWithObject m;
-	EsMemoryZero(&m, sizeof(m));
-	m.message.type = ES_MSG_SET_SCREEN_RESOLUTION;
-	DesktopSendMessage(&m);
+	KDeviceSendConnectedMessage(target, ES_DEVICE_GRAPHICS_TARGET);
 #endif
 }