diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..4cc06cb --- /dev/null +++ b/.gdbinit @@ -0,0 +1,13 @@ +# INNNNNNNNNNNTRODUCING: the gdb unfucker 9000 +printf "Setting up Lindows debug tools\n" + +# We operate in MODES, modes are guessed by what we're connecting to +set $XU_LWDBG_MODE = "INACTIVE" + +# Load guile code +source common/gdb/guile/core.scm + +# Create guile hooks +define target hookpost-remote + lwdbg internal handle-target-remote-post +end diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f4ec08..ef03dcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,8 @@ add_custom_command(OUTPUT lindows.img BYPRODUCTS disk.sfdisk ) +configure_file(config.h.in config.h) + add_custom_target(lindows ALL DEPENDS lindows.img) add_subdirectory(executive) diff --git a/README b/README index 0300269..2346a7a 100644 --- a/README +++ b/README @@ -1,3 +1,8 @@ +== STYLE == + +=== GUILE === +- Avoid direct use of (execute) + == NAMING == Mph: Metaprogramming helpers diff --git a/common/gdb/guile/core.scm b/common/gdb/guile/core.scm new file mode 100644 index 0000000..25729f7 --- /dev/null +++ b/common/gdb/guile/core.scm @@ -0,0 +1,150 @@ +(use-modules (gdb)) ; import gdb libraries + +; == Misc libraries == +(use-modules (srfi srfi-9)) ; provides define-record-type +(use-modules (ice-9 string-fun)) ; provides string-replace-substring + +; Types +(define btp-u8 (lookup-type "unsigned char")) +(define btp-u16 (lookup-type "unsigned short")) +(define btp-u32 (lookup-type "unsigned int")) +(define btp-u64 (lookup-type "unsigned long long")) +(define btp-i8 (lookup-type "char")) +(define btp-i16 (lookup-type "short")) +(define btp-i32 (lookup-type "int")) +(define btp-i64 (lookup-type "long long")) +(define btp-c8 (lookup-type "char")) +(define btp-bool btp-u8) +(define btp-uptr btp-u64) + +; == Execute wrappers == +; Be careful modifying this, other commands depend on it for multiline commands +(define gdbw-python (lambda (code) (execute (format #f "python ~a" code) #:to-string #t))) + +(define gdbw-remove-symbol-file (lambda (file) (execute (format #f "remove-symbol-file ~s" file)))) + +(define gdbw-add-symbol-file (lambda* (file #:key (offset '())) + (define cmd (format #f "add-symbol-file ~s" file)) + (when (number? offset) (set! cmd (string-append cmd (format #f " -o ~a" offset)))) + (execute cmd))) + +; HACK: gdb doesn't support multiline execute for guile, so we use execute to call python's execute +(define gdbw-commands (lambda (breakpoints cmds) + (for-each (lambda (breakpoint) + (define gdb-breakpoint breakpoint) + (when (breakpoint? gdb-breakpoint) (set! gdb-breakpoint (breakpoint-number gdb-breakpoint))) + ; HACK: This is a silly hack that abuses an unchecked cast from unsigned numbers to signed ones in the gdb shell, allowing us to bypass the "no negative numbers!" thing + (when (< gdb-breakpoint 0) (set! gdb-breakpoint (+ 4294967295 (+ 1 gdb-breakpoint)))) + (gdbw-python (format #f "gdb.execute(~s)" (format #f "commands ~a\n~a\nend" gdb-breakpoint (string-join cmds "\n"))))) + breakpoints))) + +; HACK: gdb doesn't support connection data for guile, so we have to implement it ourself with this incredible hack +(define-record-type + (make-gdbw-info-connections-result current number what description) + gdbw-info-connections-result? + (current gdbw-info-connections-result-current) + (number gdbw-info-connections-result-number) + (what gdbw-info-connections-result-what) + (description gdbw-info-connections-result-description)) +(define gdbw-info-connections (lambda () ; TODO: this looks awful, i'm positive there's a better way to do a lot of this, come back when i understand scheme + (define compact-gdb-table-row (lambda (raw) + (define crunch (lambda (pos chunks remain) + (if (< pos (string-length remain)) + (let ((nidx (string-index remain #\space pos))) + (if (< (+ nidx 1) (string-length remain)) + (if (char=? (string-ref remain (+ nidx 1)) #\space) + (let ((fidx (string-skip remain #\space nidx))) + (crunch 0 (append chunks (list (substring remain 0 nidx))) (substring remain fidx))) + (crunch (+ nidx 1) chunks remain)) + (append chunks (list (substring remain 0 (- (string-length remain) 1)))))) + (append chunks (list (substring remain 0 (- (string-length remain) 1))))))) + (crunch 0 '() raw))) + (map (lambda (l) (let ((crow (compact-gdb-table-row l))) + (make-gdbw-info-connections-result + (char=? (string-ref (car crow) 0) #\*) + (string->number (substring (car crow) 2)) + (car (cdr crow)) + (car (cdr (cdr crow)))))) + (list-head (list-tail (string-split (execute "info connections" #:to-string #t) #\newline) 1) 1)))) + +; == Debugging mode stuff == +(define DEBUG_MODE_KERNEL 'KERNEL) +(define DEBUG_MODE_INVALID 'INVALID) +(define DEBUG_MODE_NONE 'NONE) + +(define debug-mode DEBUG_MODE_NONE) + +(define debug-mode-transition (lambda (mode) ; Reconfigure internal state to debug a new target + ; Check for major fuckups + (when (equal? mode DEBUG_MODE_INVALID) (error "Transitioned into an invalid debug mode, this should never happen!")) + + ; Deinitialize the previous target's debug code + (cond + ((equal? debug-mode DEBUG_MODE_KERNEL) + (delete-breakpoint! kernel-debug-table-update-watcher))) + + ; Set the new debug mode + (set! debug-mode mode) + + ; Initialize the current target's debug code + (cond + ((equal? debug-mode DEBUG_MODE_KERNEL) + (register-breakpoint! kernel-debug-table-update-watcher) + (gdbw-commands (list kernel-debug-table-update-watcher) '("silent" "lwdbg kernel refresh-debug-tables" "continue")) + (process-kernel-debug-tables))))) + +; == Code for processing data in kernel debug tables == +(define cckernel-binary-location '()) +(define cckernel-binary-offset '()) +(define kernel-binary-location '()) +(define kernel-binary-offset '()) +(define kernel-debug-table-update-watcher (make-breakpoint "*(0x100000)" + #:type BP_WATCHPOINT + #:wp-class WP_WRITE + #:internal #t)) +(define process-kernel-debug-tables (lambda () + (define build-dir (value-dereference (value-cast (make-value #x100008) (type-pointer (type-pointer btp-c8))))) + (when (not (value=? build-dir (make-value 0))) + (set! build-dir (value->string build-dir)) + (if (value=? (value-dereference (value-cast (make-value #x100010) (type-pointer btp-bool))) 1) + ((lambda () (define new-cckernel-binary-offset (value->integer (value-dereference (value-cast (make-value #x100018) (type-pointer btp-uptr))))) + (define new-cckernel-binary-location (string-append build-dir "/lcrash/cckernel")) + (when (not (null? cckernel-binary-location)) + (gdbw-remove-symbol-file cckernel-binary-location) + (set! cckernel-binary-location '()) + (set! cckernel-binary-offset '())) + (gdbw-add-symbol-file new-cckernel-binary-location #:offset new-cckernel-binary-offset) + (set! cckernel-binary-location new-cckernel-binary-location) + (set! cckernel-binary-offset new-cckernel-binary-offset))) + (display "TODO: CCKERNEL UNLOADED\n")) + (if (value=? (value-dereference (value-cast (make-value #x100020) (type-pointer btp-bool))) 1) + ((lambda () (define new-kernel-binary-offset (value->integer (value-dereference (value-cast (make-value #x100028) (type-pointer btp-uptr))))) + (define new-kernel-binary-location (string-append build-dir "/lcrash/lcrashkern")) + (when (not (null? kernel-binary-location)) + (gdbw-remove-symbol-file kernel-binary-location) + (set! kernel-binary-location '()) + (set! kernel-binary-offset '())) + (gdbw-add-symbol-file new-kernel-binary-location #:offset new-kernel-binary-offset) + (set! kernel-binary-location new-kernel-binary-location) + (set! kernel-binary-offset new-kernel-binary-offset))) + (display "TODO: KERNEL UNLOADED\n"))))) + +; == Command prefixes == +(register-command! (make-command "lwdbg" #:prefix? #t)) +(register-command! (make-command "lwdbg internal" #:prefix? #t)) +(register-command! (make-command "lwdbg kernel" #:prefix? #t)) + +; Called by gdb hookpost for target remote +(register-command! (make-command "lwdbg internal handle-target-remote-post" + #:invoke (lambda (self arg from-tty) + (debug-mode-transition + (let ((target (string-split (gdbw-info-connections-result-what (car (filter gdbw-info-connections-result-current (gdbw-info-connections)))) #\space))) + (if (string=? (car target) "remote") + DEBUG_MODE_KERNEL + DEBUG_MODE_INVALID)))))) + +; Command to forcefully refresh the thing +(register-command! (make-command "lwdbg kernel refresh-debug-tables" + #:command-class COMMAND_USER + #:doc "Refresh the kernel debug tables" + #:invoke (lambda (self arg from-tty) (process-kernel-debug-tables)))) diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..c189499 --- /dev/null +++ b/config.h.in @@ -0,0 +1,3 @@ +#pragma once + +#define LW_CONFIG_PROJECT_OUTPUT_DIR "@CMAKE_BINARY_DIR@" diff --git a/lcrash/CMakeLists.txt b/lcrash/CMakeLists.txt index 66ef418..84b0fe0 100644 --- a/lcrash/CMakeLists.txt +++ b/lcrash/CMakeLists.txt @@ -19,6 +19,7 @@ set_property(TARGET lcrashkern PROPERTY LINK_DEPENDS set_property(TARGET lcrashkern PROPERTY LINK_OPTIONS -nostdlib -Wl,-T,${CMAKE_CURRENT_SOURCE_DIR}/lcrash.ld ) target_compile_options(lcrashkern PRIVATE -ggdb -fpic -pie -Wall -fanalyzer) target_link_options(lcrashkern PRIVATE -fpic -pie) +target_include_directories(lcrashkern PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lcrashkern.gz @@ -54,6 +55,7 @@ set_property(TARGET cckernel PROPERTY LINK_DEPENDS ) set(COMPRESSED_KERNEL_PATH ${CMAKE_CURRENT_BINARY_DIR}/lcrashkern.gz) configure_file(setup/compressed/cckernel.S.in setup/compressed/cckernel.S) +target_include_directories(cckernel PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}) # This is the actual bzImage used by linux/grub lw_add_executable(lcrash diff --git a/lcrash/acpi/acpi.c b/lcrash/acpi/acpi.c index 82e4a1f..9fdfef8 100644 --- a/lcrash/acpi/acpi.c +++ b/lcrash/acpi/acpi.c @@ -10,7 +10,9 @@ bool AcpiIsPresent = false; -struct AcpiRSDP* AcpiTheRSDP = 0; +struct AcpiRSDP* AcpiTheRSDP = 0; +struct AcpiXSDT* AcpiTheXSDT = 0; + u32 AcpiXSDTLength = 0; void AcpiInitialize() { // Check boot params @@ -42,6 +44,15 @@ found: // Is this a valid ACPI table? for (int i = 0; i < sizeof(struct AcpiRSDP); i++) Sum += ((u8*)AcpiTheRSDP)[i]; if (Sum != 0) goto fail; + // Grab the XSDT + AcpiTheXSDT = AcpiTheRSDP->XSDTAddress; + + // Is this a valid XSDT? + if (!CompareMemory(AcpiTheXSDT->Header.Signature, "XSDT", 4)) goto fail; + + // Get the XSDT length + AcpiXSDTLength = (AcpiTheXSDT->Header.Length - sizeof(struct AcpiXSDT)) / 8; + AcpiIsPresent = true; return; @@ -51,3 +62,15 @@ fail: AcpiIsPresent = false; bool AcpiPresent() { return AcpiIsPresent; } struct AcpiRSDP* AcpiGetRSDP() { return AcpiTheRSDP; } +struct AcpiXSDT* AcpiGetXSDT() { return AcpiTheXSDT; } +u32 AcpiGetXSDTLength() { return AcpiXSDTLength; } + +void* AcpiGetTable(const char TableName[4]) { + // Let's not fuck this up this early + if (!AcpiPresent()) return NULL; + + // Alright let's see here... + for (int i = 0; i < AcpiXSDTLength; i++) { + + } +} diff --git a/lcrash/acpi/acpi.h b/lcrash/acpi/acpi.h index 9befbf2..9c03bb8 100644 --- a/lcrash/acpi/acpi.h +++ b/lcrash/acpi/acpi.h @@ -56,3 +56,23 @@ void AcpiInitialize(); * Check if ACPI is present */ bool AcpiPresent(); + +/** + * Get the ACPI RSDP, only call if ACPI is present + */ +struct AcpiRSDP* AcpiGetRSDP(); + +/** + * Get the ACPI XSDT, only call if ACPI is present + */ +struct AcpiXSDT* AcpiGetXSDT(); + +/** + * Get the ACPI XSDT entry count, only call if ACPI is present + */ +u32 AcpiGetXSDTLength(); + +/** + * Get the ACPI table with the specified name, returns NULL if not found + */ +void* AcpiGetTable(const char TableName[4]); diff --git a/lcrash/gdb/gdb.h b/lcrash/gdb/gdb.h index 2db8a4f..e78c243 100644 --- a/lcrash/gdb/gdb.h +++ b/lcrash/gdb/gdb.h @@ -1,5 +1,7 @@ #pragma once +#include "../types.h" + #define DEFINE_GDB_PY_SCRIPT(name) \ asm("\ .pushsection \".debug_gdb_scripts\", \"MS\", @progbits, 1\n\ @@ -7,3 +9,26 @@ .asciz \""name"\"\n\ .popsection \n\ "); + +/// Fixed data block placed at the address 0x100000 +/// Please update the initialization code in /lcrash/setup/compressed/main.c when this is changed +struct GdbDataBlock { + /// Update tick thing stored at the beginning of the fixed data block for debuggers + /// TODO: Optimization may break this, should this be volatile? + u64 Update; + + /// Base of the cmake build directory. This is needed for lwdbg to configure itself + c8* BuildDirectory; + + /// cckernel is present + bool CCKernelLoaded; + + /// Base address of the cckernel + void* CCKernelBase; + + /// Full kernel is present + bool KernelLoaded; + + /// Base address of the kernel + void* KernelBase; +}; diff --git a/lcrash/main.c b/lcrash/main.c index 78d8b16..1ef2047 100644 --- a/lcrash/main.c +++ b/lcrash/main.c @@ -1,6 +1,8 @@ /// lcrash entry point /// super awesome... +#include + #include "types.h" #include "lnxboot.h" @@ -11,12 +13,18 @@ void entry64(struct boot_params* BootParams) { BootBootParams = BootParams; BootSetupInfo = (void*)BootParams + 0x1f1; + // Notify the debugger that we're ready + struct GdbDataBlock* GdbDataBlock = (struct GdbDataBlock*)0x100000; + GdbDataBlock->KernelLoaded = true; + GdbDataBlock->KernelBase = BootSetupInfo->code32_start; + GdbDataBlock->Update++; + // Initialize EFI code if we had EFI EfiInitialize(); // Initialize ACPI code if we have ACPI AcpiInitialize(); - + // Hang :) while (1) {} } diff --git a/lcrash/setup/compressed/compressed.ld b/lcrash/setup/compressed/compressed.ld index ceaf344..b8f8afe 100644 --- a/lcrash/setup/compressed/compressed.ld +++ b/lcrash/setup/compressed/compressed.ld @@ -7,6 +7,10 @@ SECTIONS { *(.text) } + .rodata : { + *(.rodata) + } + .cckernel : { *(.cckernel) } diff --git a/lcrash/setup/compressed/main.c b/lcrash/setup/compressed/main.c index 37b92ee..ae4a6c0 100644 --- a/lcrash/setup/compressed/main.c +++ b/lcrash/setup/compressed/main.c @@ -1,4 +1,6 @@ #include "../../lnxboot.h" +#include "../../gdb/gdb.h" +#include #include "gzip.h" #include "elf.h" @@ -11,8 +13,18 @@ extern void* _CC_ecckernel; void ccmain(void* imgbase, struct boot_params* boot_params) { struct setup_info* setup_info = (struct setup_info*)((void*)boot_params + 0x1f1); + // Fill the lcrash debug data block + struct GdbDataBlock* GdbDataBlock = (struct GdbDataBlock*)0x100000; + GdbDataBlock->BuildDirectory = LW_CONFIG_PROJECT_OUTPUT_DIR + (uptr)imgbase; + GdbDataBlock->CCKernelLoaded = true; + GdbDataBlock->CCKernelBase = imgbase; + GdbDataBlock->KernelLoaded = false; + GdbDataBlock->KernelBase = NULL; + GdbDataBlock->Update++; + + // Determine the kernel load address char* kernel_load_addr = (char*)setup_info->code32_start; - if (kernel_load_addr == 0) kernel_load_addr = (char*)0x100000; + if (kernel_load_addr == 0) kernel_load_addr = (char*)0x101000; char* cckernel = imgbase + (uptr)&_CC_cckernel; //for (long i = 0; i < setup_info->payload_length; i++) { diff --git a/lcrash/types.h b/lcrash/types.h index 7ae1596..56f7ba1 100644 --- a/lcrash/types.h +++ b/lcrash/types.h @@ -27,3 +27,6 @@ typedef u8 bool; #else #error What the fuck? [pointers are not 4 or 8 bytes long] #endif + +/// Null may become part of the nullptr type, so it's defined here +#define NULL 0