diff --git a/grub/grub.cfg b/grub/grub.cfg index 7b9a8d4b..befcd5a7 100644 --- a/grub/grub.cfg +++ b/grub/grub.cfg @@ -22,7 +22,7 @@ else fi menuentry "Stellux 2.0" { - multiboot2 /boot/stellux -- "debug=true use-pci-serial=true gfxmode=compositor" + multiboot2 /boot/stellux -- "debug=true use-pci-serial=true" module2 /boot/initrd "initrd" boot } diff --git a/kernel/include/drivers/usb/xhci/xhci.h b/kernel/include/drivers/usb/xhci/xhci.h index efe3f176..770797c8 100644 --- a/kernel/include/drivers/usb/xhci/xhci.h +++ b/kernel/include/drivers/usb/xhci/xhci.h @@ -263,6 +263,7 @@ class xhci_driver : public pci_device_driver { kstl::vector m_port_status_change_events; kstl::vector m_command_completion_events; kstl::vector m_transfer_completion_events; + kstl::vector m_events_buffer; // Preallocated buffer for IRQ events volatile uint8_t m_command_irq_completed = 0; volatile uint8_t m_transfer_irq_completed = 0; diff --git a/kernel/include/drivers/usb/xhci/xhci_usb_hid_mouse_driver.h b/kernel/include/drivers/usb/xhci/xhci_usb_hid_mouse_driver.h index 4f3d270d..4b471126 100644 --- a/kernel/include/drivers/usb/xhci/xhci_usb_hid_mouse_driver.h +++ b/kernel/include/drivers/usb/xhci/xhci_usb_hid_mouse_driver.h @@ -19,7 +19,11 @@ class xhci_usb_hid_mouse_driver : public xhci_usb_hid_driver { uint16_t x_axis_size; uint16_t y_axis_offset; uint16_t y_axis_size; + uint16_t scroll_offset; + uint16_t scroll_size; } m_input_layout; + + uint32_t m_previous_button_state; // Track previous button state for press/release detection void _initialize_input_field( hid::hid_report_layout& layout, @@ -27,6 +31,8 @@ class xhci_usb_hid_mouse_driver : public xhci_usb_hid_driver { uint16_t& offset, uint16_t& size, const char* field_name ); + + void _emit_input_event(uint32_t event_type, uint32_t udata1, uint32_t udata2, int32_t sdata1, int32_t sdata2); }; #endif // XHCI_USB_HID_MOUSE_DRIVER_H diff --git a/kernel/include/input/input_event.h b/kernel/include/input/input_event.h index 649c83b3..f4018f7d 100644 --- a/kernel/include/input/input_event.h +++ b/kernel/include/input/input_event.h @@ -2,8 +2,7 @@ #define INPUT_EVENT_H #include -#define INPUT_QUEUE_ID_KBD 0x0001 -#define INPUT_QUEUE_ID_POINTER 0x0002 +#define INPUT_QUEUE_ID_SYSTEM 0x0001 // Handles both, kbd and pointer events namespace input { enum input_event_type : uint32_t { @@ -26,6 +25,78 @@ struct input_event_t { int32_t sdata1; // Event-specific signed data 1 int32_t sdata2; // Event-specific signed data 2 } __attribute__((packed)); + +/** + * @brief Keyboard key pressed event structure. + */ +struct keyboard_key_pressed_event_t { + uint32_t id; // Event-specific ID + input_event_type type; // Always KBD_EVT_KEY_PRESSED + uint32_t keycode; // Key code of the pressed key + uint32_t modifiers; // Modifier keys state (Ctrl, Alt, Shift, etc.) + int32_t ascii_char; // ASCII character representation + int32_t reserved; // Reserved for future use +} __attribute__((packed)); + +/** + * @brief Keyboard key released event structure. + */ +struct keyboard_key_released_event_t { + uint32_t id; // Event-specific ID + input_event_type type; // Always KBD_EVT_KEY_RELEASED + uint32_t keycode; // Key code of the released key + uint32_t modifiers; // Modifier keys state (Ctrl, Alt, Shift, etc.) + int32_t reserved1; // Reserved for future use + int32_t reserved2; // Reserved for future use +} __attribute__((packed)); + +/** + * @brief Mouse movement event structure. + */ +struct pointer_mouse_moved_event_t { + uint32_t id; // Event-specific ID + input_event_type type; // Always POINTER_EVT_MOUSE_MOVED + uint32_t x_pos; // Current X position of mouse cursor + uint32_t y_pos; // Current Y position of mouse cursor + int32_t delta_x; // Change in X position since last event + int32_t delta_y; // Change in Y position since last event +} __attribute__((packed)); + +/** + * @brief Mouse button pressed event structure. + */ +struct pointer_mouse_btn_pressed_event_t { + uint32_t id; // Event-specific ID + input_event_type type; // Always POINTER_EVT_MOUSE_BTN_PRESSED + uint32_t button; // Button that was pressed (1=left, 2=right, 3=middle, etc.) + uint32_t x_pos; // X position when button was pressed + int32_t y_pos; // Y position when button was pressed + int32_t reserved; // Reserved for future use +} __attribute__((packed)); + +/** + * @brief Mouse button released event structure. + */ +struct pointer_mouse_btn_released_event_t { + uint32_t id; // Event-specific ID + input_event_type type; // Always POINTER_EVT_MOUSE_BTN_RELEASED + uint32_t button; // Button that was released (1=left, 2=right, 3=middle, etc.) + uint32_t x_pos; // X position when button was released + int32_t y_pos; // Y position when button was released + int32_t reserved; // Reserved for future use +} __attribute__((packed)); + +/** + * @brief Mouse scroll event structure. + */ +struct pointer_mouse_scrolled_event_t { + uint32_t id; // Event-specific ID + input_event_type type; // Always POINTER_EVT_MOUSE_SCROLLED + uint32_t scroll_type; // Type of scroll (0=vertical, 1=horizontal) + uint32_t x_pos; // X position when scroll occurred + int32_t y_pos; // Y position when scroll occurred + int32_t scroll_delta; // Scroll amount (positive=up/right, negative=down/left) +} __attribute__((packed)); } // namespace input #endif // INPUT_EVENT_H diff --git a/kernel/include/input/system_input_manager.h b/kernel/include/input/system_input_manager.h index d74b2044..8e76216b 100644 --- a/kernel/include/input/system_input_manager.h +++ b/kernel/include/input/system_input_manager.h @@ -49,6 +49,12 @@ class system_input_manager { */ bool push_event(uint32_t queue_id, const input_event_t& event); + /** + * @brief Gets the system input queue (ID: INPUT_QUEUE_ID_SYSTEM). + * @return Pointer to the system input queue, or nullptr if not found. + */ + input_queue* get_system_input_queue(); + private: kstl::hashmap m_input_queues; // Stores input queues mapped by ID. }; diff --git a/kernel/include/ipc/shm.h b/kernel/include/ipc/shm.h index d4aa0b57..25a0e5e9 100644 --- a/kernel/include/ipc/shm.h +++ b/kernel/include/ipc/shm.h @@ -16,7 +16,15 @@ using shm_handle_t = uint64_t; enum class shm_access { READ_ONLY, // Region is read-only. READ_WRITE, // Region is readable and writable. - COPY_ON_WRITE // Region starts read-only, create private copy on write. +}; + +/** + * @enum shm_mapping_context + * @brief Describes the context in which shared memory is being mapped. + */ +enum class shm_mapping_context { + KERNEL, // Kernel thread mapping - use vmm directly + USERLAND // Userland process mapping - use VMA system }; /** @@ -50,10 +58,10 @@ class shared_memory { static bool destroy(shm_handle_t handle); /** Map a shared memory region into the caller address space. */ - static void* map(shm_handle_t handle, uint64_t flags); + static void* map(shm_handle_t handle, uint64_t flags, shm_mapping_context context); /** Unmap a previously mapped shared memory region. */ - static bool unmap(shm_handle_t handle, void* addr); + static bool unmap(shm_handle_t handle, void* addr, shm_mapping_context context); private: static shm_region* get_region(shm_handle_t handle); diff --git a/kernel/include/net/unix_socket.h b/kernel/include/net/unix_socket.h new file mode 100644 index 00000000..db8d08d7 --- /dev/null +++ b/kernel/include/net/unix_socket.h @@ -0,0 +1,205 @@ +#ifndef UNIX_SOCKET_H +#define UNIX_SOCKET_H + +#include +#include +#include +#include +#include +#include + +// Uncomment the line below to enable verbose Unix socket debugging +// #define STELLUX_UNIX_SOCKET_DEBUG + +#ifdef STELLUX_UNIX_SOCKET_DEBUG +#include +#define UNIX_SOCKET_TRACE(...) kprint(__VA_ARGS__) +#else +#define UNIX_SOCKET_TRACE(...) do { } while(0) +#endif + +namespace net { + +/** + * @enum unix_socket_state + * @brief Represents the state of a Unix socket. + */ +enum class unix_socket_state : uint32_t { + INVALID = 0, // Socket is invalid/uninitialized + CREATED, // Socket created but not bound/connected + BOUND, // Server socket bound to path + LISTENING, // Server socket listening for connections + CONNECTING, // Client socket attempting to connect + CONNECTED, // Connected socket (client or accepted connection) + DISCONNECTED, // Socket disconnected but not closed + CLOSED // Socket closed and resources freed +}; + +/** + * @class unix_stream_socket + * @brief Unix domain stream socket implementation. + * + * Provides reliable, ordered, connection-based communication between processes + * on the same machine using filesystem paths as addresses. + */ +class unix_stream_socket { +public: + /** + * @brief Constructs a new Unix stream socket. + */ + unix_stream_socket(); + + /** + * @brief Destructor - cleans up socket resources. + */ + ~unix_stream_socket(); + + // Non-copyable, non-movable for thread safety + unix_stream_socket(const unix_stream_socket&) = delete; + unix_stream_socket& operator=(const unix_stream_socket&) = delete; + unix_stream_socket(unix_stream_socket&&) = delete; + unix_stream_socket& operator=(unix_stream_socket&&) = delete; + + /** + * @brief Binds the socket to a filesystem path. + * + * Creates a socket file at the specified path that clients can connect to. + * Only server sockets need to bind to a path. + * + * @param path Filesystem path to bind to (e.g., "/tmp/my_socket") + * @return 0 on success, negative error code on failure + */ + int bind(const kstl::string& path); + + /** + * @brief Puts the socket into listening mode. + * + * Marks the socket as passive, ready to accept incoming connections. + * Must be called after bind() and before accept(). + * + * @param backlog Maximum number of pending connections (default: 5) + * @return 0 on success, negative error code on failure + */ + int listen(int backlog = 5); + + /** + * @brief Accepts an incoming connection (blocking). + * + * Blocks until a client connects, then returns a new socket for that connection. + * The original socket remains in listening state for more connections. + * + * @return Shared pointer to new socket for the connection, or nullptr on error + */ + kstl::shared_ptr accept(); + + /** + * @brief Connects to a server socket (blocking). + * + * Attempts to establish a connection to a server socket at the given path. + * Blocks until connection is established or fails. + * + * @param path Path to connect to + * @return 0 on success, negative error code on failure + */ + int connect(const kstl::string& path); + + /** + * @brief Reads data from the socket (blocking). + * + * Blocks until data is available or the connection is closed. + * For stream sockets, may return fewer bytes than requested. + * + * @param buffer Buffer to read data into + * @param size Maximum number of bytes to read + * @return Number of bytes read, 0 for EOF, negative for error + */ + ssize_t read(void* buffer, size_t size); + + /** + * @brief Writes data to the socket. + * + * Attempts to write data to the connected peer. May write fewer bytes + * than requested if the peer's receive buffer is full. + * + * @param data Data to write + * @param size Number of bytes to write + * @return Number of bytes written, negative for error + */ + ssize_t write(const void* data, size_t size); + + /** + * @brief Closes the socket. + * + * Closes the socket and releases all associated resources. + * Any pending operations will be interrupted. + * + * @return 0 on success, negative error code on failure + */ + int close(); + + /** + * @brief Registers this socket with the manager (for server sockets). + * + * Must be called after bind() for server sockets to make them discoverable. + * This is a separate step to avoid shared_ptr issues in bind(). + * + * @param self Shared pointer to this socket instance + * @return 0 on success, negative error code on failure + */ + int register_with_manager(kstl::shared_ptr self); + + // State and property getters + unix_socket_state get_state() const { return m_state.load(); } + const kstl::string& get_path() const { return m_path; } + bool is_server() const { return m_is_server; } + bool is_connected() const { return get_state() == unix_socket_state::CONNECTED; } + bool is_listening() const { return get_state() == unix_socket_state::LISTENING; } + bool is_nonblocking() const { return m_nonblocking; } + + /** + * @brief Sets the socket to blocking or non-blocking mode. + * + * @param nonblocking True for non-blocking, false for blocking + * @return 0 on success, negative error code on failure + */ + int set_nonblocking(bool nonblocking); + +private: + // Core socket state + atomic m_state = atomic(unix_socket_state::CREATED); + kstl::string m_path; // Bound path (for server sockets) + bool m_is_server = false; // True if this is a server socket + bool m_nonblocking = false; // True if socket is in non-blocking mode + int m_backlog = 0; // Listen backlog size + + // Connection management + kstl::shared_ptr m_peer; // Connected peer socket + kstl::vector> m_pending_connections; // Pending accept queue + + // Data buffers + kstl::shared_ptr m_recv_buffer; // Incoming data buffer + kstl::shared_ptr m_send_buffer; // Outgoing data buffer + + // Synchronization + mutable mutex m_socket_lock = mutex(); // Protects socket state + mutex m_accept_lock = mutex(); // Protects pending connections + + // Private helper methods + void _change_state(unix_socket_state new_state); + bool _can_accept() const; + bool _can_read() const; + bool _can_write() const; + void _cleanup_resources(); + void _setup_buffers(); + int _add_pending_connection(kstl::shared_ptr client); + kstl::shared_ptr _get_pending_connection(); + void _set_peer(kstl::shared_ptr peer); + + // Allow manager to access private members for connection setup + friend class unix_socket_manager; +}; + +} // namespace net + +#endif // UNIX_SOCKET_H + diff --git a/kernel/include/net/unix_socket_buffer.h b/kernel/include/net/unix_socket_buffer.h new file mode 100644 index 00000000..44a1ef64 --- /dev/null +++ b/kernel/include/net/unix_socket_buffer.h @@ -0,0 +1,131 @@ +#ifndef UNIX_SOCKET_BUFFER_H +#define UNIX_SOCKET_BUFFER_H + +#include +#include +#include + +namespace net { + +/** + * @class unix_socket_buffer + * @brief Thread-safe circular buffer optimized for Unix stream socket data transfer. + * + * This buffer provides efficient, thread-safe data transfer between socket endpoints. + * It uses a fixed-size circular buffer with atomic operations for size tracking + * and mutex protection for buffer operations. + */ +class unix_socket_buffer { +public: + static constexpr size_t DEFAULT_BUFFER_SIZE = 8192; // 8KB default + + /** + * @brief Constructs a socket buffer with specified capacity. + * @param capacity Buffer size in bytes (must be > 0) + */ + explicit unix_socket_buffer(size_t capacity = DEFAULT_BUFFER_SIZE); + + /** + * @brief Destructor - frees allocated buffer memory. + */ + ~unix_socket_buffer(); + + // Non-copyable, non-movable for thread safety + unix_socket_buffer(const unix_socket_buffer&) = delete; + unix_socket_buffer& operator=(const unix_socket_buffer&) = delete; + unix_socket_buffer(unix_socket_buffer&&) = delete; + unix_socket_buffer& operator=(unix_socket_buffer&&) = delete; + + /** + * @brief Writes data to the buffer (non-blocking). + * + * Attempts to write as much data as possible to the buffer without blocking. + * If the buffer is full, only partial data may be written. + * + * @param data Pointer to data to write + * @param size Number of bytes to write + * @return Number of bytes actually written (0 if buffer is full) + */ + size_t write(const void* data, size_t size); + + /** + * @brief Reads data from the buffer (non-blocking). + * + * Attempts to read as much data as available from the buffer without blocking. + * If the buffer is empty, returns 0. + * + * @param buffer Buffer to read data into + * @param size Maximum number of bytes to read + * @return Number of bytes actually read (0 if buffer is empty) + */ + size_t read(void* buffer, size_t size); + + /** + * @brief Checks if data is available for reading. + * @return True if at least one byte is available for reading + */ + bool has_data() const; + + /** + * @brief Checks if buffer has space for writing. + * @return True if at least one byte of space is available + */ + bool has_space() const; + + /** + * @brief Gets the number of bytes available for reading. + * @return Number of bytes that can be read + */ + size_t available_bytes() const; + + /** + * @brief Gets the number of bytes available for writing. + * @return Number of bytes that can be written + */ + size_t free_space() const; + + /** + * @brief Gets the total capacity of the buffer. + * @return Buffer capacity in bytes + */ + size_t capacity() const { return m_capacity; } + + /** + * @brief Clears all data from the buffer. + * + * Resets the buffer to empty state. This operation is thread-safe. + */ + void clear(); + +private: + uint8_t* m_buffer; // Circular buffer memory + size_t m_capacity; // Total buffer capacity + size_t m_head; // Write position (producer index) + size_t m_tail; // Read position (consumer index) + atomic m_size; // Current number of bytes in buffer + mutable mutex m_lock = mutex(); // Protects buffer operations + + /** + * @brief Calculates the next index in the circular buffer. + * @param index Current index + * @return Next index, wrapping around if necessary + */ + size_t _next_index(size_t index) const; + + /** + * @brief Calculates available contiguous write space from current head. + * @return Number of contiguous bytes that can be written + */ + size_t _contiguous_write_space() const; + + /** + * @brief Calculates available contiguous read space from current tail. + * @return Number of contiguous bytes that can be read + */ + size_t _contiguous_read_space() const; +}; + +} // namespace net + +#endif // UNIX_SOCKET_BUFFER_H + diff --git a/kernel/include/net/unix_socket_manager.h b/kernel/include/net/unix_socket_manager.h new file mode 100644 index 00000000..6e6775ad --- /dev/null +++ b/kernel/include/net/unix_socket_manager.h @@ -0,0 +1,119 @@ +#ifndef UNIX_SOCKET_MANAGER_H +#define UNIX_SOCKET_MANAGER_H + +#include +#include +#include +#include + +namespace net { +/** + * @class unix_socket_manager + * @brief Manages Unix domain sockets and their filesystem bindings. + * + * Singleton class that handles socket registration, lookup, and connection + * establishment between client and server sockets. Provides a centralized + * registry for all Unix domain sockets in the system. + */ +class unix_socket_manager { +public: + /** + * @brief Gets the singleton instance of the socket manager. + * @return Reference to the singleton instance + */ + static unix_socket_manager& get(); + + /** + * @brief Initializes the socket manager. + * + * Must be called once during kernel initialization before using sockets. + * Sets up internal data structures. + */ + void init(); + + /** + * @brief Registers a socket with a filesystem path. + * + * Associates a server socket with a filesystem path so clients can find it. + * The path must be unique - only one socket can be bound to each path. + * + * @param path Filesystem path (e.g., "/tmp/my_socket") + * @param socket Socket to register + * @return True on success, false if path already exists or manager not initialized + */ + bool register_socket(const kstl::string& path, kstl::shared_ptr socket); + + /** + * @brief Unregisters a socket from a filesystem path. + * + * Removes the association between a path and socket, making the path + * available for other sockets to bind to. + * + * @param path Filesystem path to unregister + * @return True on success, false if path not found or manager not initialized + */ + bool unregister_socket(const kstl::string& path); + + /** + * @brief Finds a socket by filesystem path. + * + * Looks up a server socket that is bound to the specified path. + * Used by clients to find servers to connect to. + * + * @param path Filesystem path to search for + * @return Socket pointer or nullptr if not found or manager not initialized + */ + kstl::shared_ptr find_socket(const kstl::string& path); + + /** + * @brief Creates a new Unix stream socket. + * + * Factory method that creates a properly initialized socket instance. + * + * @return New socket instance + */ + kstl::shared_ptr create_socket(); + + /** + * @brief Gets the number of registered sockets. + * @return Number of sockets currently registered with paths + */ + size_t get_socket_count() const; + + /** + * @brief Checks if the manager is initialized. + * @return True if initialized, false otherwise + */ + bool is_initialized() const { return m_initialized; } + + /** + * @brief Cleans up all registered sockets. + * + * Used during system shutdown to clean up all socket resources. + * Should only be called during kernel shutdown. + */ + void cleanup(); + +private: + static unix_socket_manager s_singleton; + + unix_socket_manager() = default; + + // Non-copyable, non-movable + unix_socket_manager(const unix_socket_manager&) = delete; + unix_socket_manager& operator=(const unix_socket_manager&) = delete; + unix_socket_manager(unix_socket_manager&&) = delete; + unix_socket_manager& operator=(unix_socket_manager&&) = delete; + + bool m_initialized = false; // Manager initialization state + kstl::hashmap> m_bound_sockets; // Path -> Socket mapping + mutable mutex m_manager_lock = mutex(); // Protects manager state + + // Private helper methods + bool _is_valid_path(const kstl::string& path) const; + void _log_socket_operation(const char* operation, const kstl::string& path, bool success) const; +}; +} // namespace net + +#endif // UNIX_SOCKET_MANAGER_H + diff --git a/kernel/include/process/process_env.h b/kernel/include/process/process_env.h index 25f9e29f..f5575977 100644 --- a/kernel/include/process/process_env.h +++ b/kernel/include/process/process_env.h @@ -80,6 +80,7 @@ enum class handle_type : uint32_t { SEMAPHORE, // Semaphore handle EVENT, // Event handle SHARED_MEM, // Shared memory handle + UNIX_SOCKET, // Unix domain socket handle }; /** diff --git a/kernel/include/syscall/handlers/sys_arch.h b/kernel/include/syscall/handlers/sys_arch.h index a87ba3fc..44a34ae5 100644 --- a/kernel/include/syscall/handlers/sys_arch.h +++ b/kernel/include/syscall/handlers/sys_arch.h @@ -6,6 +6,7 @@ // Declare all architecture-specific syscall handlers DECLARE_SYSCALL_HANDLER(set_thread_area); DECLARE_SYSCALL_HANDLER(set_tid_address); +DECLARE_SYSCALL_HANDLER(reboot); DECLARE_SYSCALL_HANDLER(elevate); diff --git a/kernel/include/syscall/handlers/sys_graphics.h b/kernel/include/syscall/handlers/sys_graphics.h index e849a786..2d28c54f 100644 --- a/kernel/include/syscall/handlers/sys_graphics.h +++ b/kernel/include/syscall/handlers/sys_graphics.h @@ -7,7 +7,9 @@ enum gfx_operations { GFX_OP_GET_INFO = 0x01, GFX_OP_MAP_FRAMEBUFFER = 0x02, - GFX_OP_UNMAP_FRAMEBUFFER = 0x03 + GFX_OP_UNMAP_FRAMEBUFFER = 0x03, + GFX_OP_DISABLE_PREEMPT = 0x04, + GFX_OP_ENABLE_PREEMPT = 0x05 }; // Framebuffer information structure (kernel-side copy) diff --git a/kernel/include/syscall/handlers/sys_io.h b/kernel/include/syscall/handlers/sys_io.h index 2e35fae0..1e7e2006 100644 --- a/kernel/include/syscall/handlers/sys_io.h +++ b/kernel/include/syscall/handlers/sys_io.h @@ -9,7 +9,10 @@ DECLARE_SYSCALL_HANDLER(write); DECLARE_SYSCALL_HANDLER(writev); DECLARE_SYSCALL_HANDLER(open); DECLARE_SYSCALL_HANDLER(close); +DECLARE_SYSCALL_HANDLER(fcntl); DECLARE_SYSCALL_HANDLER(lseek); DECLARE_SYSCALL_HANDLER(ioctl); +DECLARE_SYSCALL_HANDLER(read_input_evt); + #endif // SYS_IO_H \ No newline at end of file diff --git a/kernel/include/syscall/handlers/sys_net.h b/kernel/include/syscall/handlers/sys_net.h new file mode 100644 index 00000000..5cbe7ddd --- /dev/null +++ b/kernel/include/syscall/handlers/sys_net.h @@ -0,0 +1,15 @@ +#ifndef SYS_NET_H +#define SYS_NET_H + +#include + +// Declare all network-related syscall handlers +DECLARE_SYSCALL_HANDLER(socket); +DECLARE_SYSCALL_HANDLER(connect); +DECLARE_SYSCALL_HANDLER(accept); +DECLARE_SYSCALL_HANDLER(bind); +DECLARE_SYSCALL_HANDLER(listen); +DECLARE_SYSCALL_HANDLER(sendto); +DECLARE_SYSCALL_HANDLER(recvfrom); + +#endif // SYS_NET_H diff --git a/kernel/include/syscall/handlers/sys_shm.h b/kernel/include/syscall/handlers/sys_shm.h new file mode 100644 index 00000000..7c5a0cce --- /dev/null +++ b/kernel/include/syscall/handlers/sys_shm.h @@ -0,0 +1,13 @@ +#ifndef SYS_SHM_H +#define SYS_SHM_H + +#include + +// Declare all shared memory-related syscall handlers +DECLARE_SYSCALL_HANDLER(shm_create); +DECLARE_SYSCALL_HANDLER(shm_open); +DECLARE_SYSCALL_HANDLER(shm_destroy); +DECLARE_SYSCALL_HANDLER(shm_map); +DECLARE_SYSCALL_HANDLER(shm_unmap); + +#endif // SYS_SHM_H diff --git a/kernel/include/syscall/handlers/sys_time.h b/kernel/include/syscall/handlers/sys_time.h new file mode 100644 index 00000000..c2e7bd5e --- /dev/null +++ b/kernel/include/syscall/handlers/sys_time.h @@ -0,0 +1,10 @@ +#ifndef SYS_TIME_H +#define SYS_TIME_H + +#include + +// Declare all time-related syscall handlers +DECLARE_SYSCALL_HANDLER(nanosleep); +DECLARE_SYSCALL_HANDLER(clock_gettime); + +#endif // SYS_TIME_H diff --git a/kernel/include/syscall/syscalls.h b/kernel/include/syscall/syscalls.h index f9c16b98..129aae06 100644 --- a/kernel/include/syscall/syscalls.h +++ b/kernel/include/syscall/syscalls.h @@ -2,20 +2,32 @@ #define SYSCALL_H #include -#define ENOSYS 1 // Invalid system call number -#define EINVAL 22 // Invalid argument -#define EFAULT 14 // Bad address -#define ENOMEM 12 // Out of memory -#define EACCES 13 // Invalid access -#define ENOTTY 25 // Invalid tty -#define ENOENT 2 // No such file or directory -#define EEXIST 17 // File exists -#define EISDIR 21 // Is a directory -#define EMFILE 24 // Too many open files -#define EIO 5 // I/O error -#define EBADF 9 // Bad file descriptor -#define ESPIPE 29 // Illegal seek -#define ENOPRIV 72 // Invalid privilege permissions +#define EPERM 1 // Operation not permitted +#define ENOENT 2 // No such file or directory +#define EIO 5 // I/O error +#define EBADF 9 // Bad file descriptor +#define EAGAIN 11 // Try again (resource temporarily unavailable) +#define EWOULDBLOCK EAGAIN // Operation would block (same as EAGAIN) +#define ENOMEM 12 // Out of memory +#define EACCES 13 // Invalid access +#define EFAULT 14 // Bad address +#define EEXIST 17 // File exists +#define ENODEV 19 // No such device +#define EISDIR 21 // Is a directory +#define EINVAL 22 // Invalid argument +#define EMFILE 24 // Too many open files +#define ENOTTY 25 // Invalid tty +#define ESPIPE 29 // Illegal seek +#define EPIPE 32 // Broken pipe +#define ENOSYS 38 // Invalid system call number +#define EOPNOTSUPP 95 // Operation not supported on transport endpoint +#define EADDRINUSE 98 // Address already in use +#define EAFNOSUPPORT 102 // Address family not supported +#define ENOTCONN 107 // Transport endpoint is not connected +#define ECONNREFUSED 111 // Connection refused +#define EINPROGRESS 115 // Operation now in progress +#define EPROTONOSUPPORT 135 // Protocol not supported +#define ENOPRIV 720 // Invalid privilege permissions #define SYSCALL_SYS_READ 0 #define SYSCALL_SYS_WRITE 1 @@ -27,19 +39,36 @@ #define SYSCALL_SYS_BRK 12 #define SYSCALL_SYS_IOCTL 16 #define SYSCALL_SYS_WRITEV 20 +#define SYSCALL_SYS_NANOSLEEP 35 #define SYSCALL_SYS_GETPID 39 +#define SYSCALL_SYS_SOCKET 41 +#define SYSCALL_SYS_CONNECT 42 +#define SYSCALL_SYS_ACCEPT 43 +#define SYSCALL_SYS_SENDTO 44 +#define SYSCALL_SYS_RECVFROM 45 +#define SYSCALL_SYS_BIND 49 +#define SYSCALL_SYS_LISTEN 50 #define SYSCALL_SYS_EXIT 60 +#define SYSCALL_SYS_FCNTL 72 #define SYSCALL_SYS_SET_THREAD_AREA 158 +#define SYSCALL_SYS_REBOOT 169 #define SYSCALL_SYS_SET_TID_ADDRESS 218 +#define SYSCALL_SYS_CLOCK_GETTIME 228 #define SYSCALL_SYS_EXIT_GROUP 231 -#define SYSCALL_SYS_PROC_CREATE 706 -#define SYSCALL_SYS_PROC_WAIT 707 -#define SYSCALL_SYS_PROC_CLOSE 708 +#define SYSCALL_SYS_PROC_CREATE 706 +#define SYSCALL_SYS_PROC_WAIT 707 +#define SYSCALL_SYS_PROC_CLOSE 708 +#define SYSCALL_SYS_SHM_CREATE 709 +#define SYSCALL_SYS_SHM_OPEN 710 +#define SYSCALL_SYS_SHM_DESTROY 711 +#define SYSCALL_SYS_SHM_MAP 712 +#define SYSCALL_SYS_SHM_UNMAP 713 -#define SYSCALL_SYS_ELEVATE 790 +#define SYSCALL_SYS_ELEVATE 790 -#define SYSCALL_SYS_GRAPHICS_FRAMEBUFFER_OP 800 +#define SYSCALL_SYS_GRAPHICS_FRAMEBUFFER_OP 800 +#define SYSCALL_SYS_READ_INPUT_EVENT 801 // Uncomment this if you want to see strace-style logs for every issued syscall // #define STELLUX_STRACE_ENABLED diff --git a/kernel/src/arch/x86/arch_init.cpp b/kernel/src/arch/x86/arch_init.cpp index 9174ec3f..9e0d7f71 100644 --- a/kernel/src/arch/x86/arch_init.cpp +++ b/kernel/src/arch/x86/arch_init.cpp @@ -17,7 +17,7 @@ #include #include -uint8_t g_default_bsp_system_stack[PAGE_SIZE]; +uint8_t g_default_bsp_system_stack[PAGE_SIZE * 4]; namespace arch { __PRIVILEGED_CODE @@ -56,6 +56,7 @@ void arch_init() { bsp_idle_core->state = process_state::RUNNING; bsp_idle_core->hw_state.cpu = BSP_CPU_ID; bsp_idle_core->hw_state.elevated = 1; + bsp_idle_core->stacks.task_stack_top = bsp_system_stack_top; bsp_idle_core->stacks.system_stack_top = bsp_system_stack_top; // Create the BSP's idle process diff --git a/kernel/src/arch/x86/asm/asm_interrupts.S b/kernel/src/arch/x86/asm/asm_interrupts.S index 89531c32..643cc802 100644 --- a/kernel/src/arch/x86/asm/asm_interrupts.S +++ b/kernel/src/arch/x86/asm/asm_interrupts.S @@ -102,7 +102,7 @@ asm_common_isr_entry: # Get the top of the current system stack this_cpu_read rax, current_system_stack - sub rax, 0x40 + sub rax, 0x48 # Need 72 bytes (9 * 8) for: r15, rax, intno, error, rip, cs, rflags, rsp, ss # Copy pushed r15 mov r15, [rsp + 0x00] diff --git a/kernel/src/arch/x86/idt/idt.cpp b/kernel/src/arch/x86/idt/idt.cpp index ccbe95d3..3c72fa00 100644 --- a/kernel/src/arch/x86/idt/idt.cpp +++ b/kernel/src/arch/x86/idt/idt.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #define MAX_IRQS 64 @@ -249,10 +250,16 @@ void common_isr_entry(ptregs regs) { int original_elevate_status = current->get_core()->hw_state.elevated; process* original_task = current; + // Memory barrier to prevent compiler reordering + memory_barrier(); + // Fake out being elevated if needed because the code has to // be able to tell if it's running in privileged mode or not. original_task->get_core()->hw_state.elevated = 1; + // Memory barrier after privilege elevation + memory_barrier(); + // Check whether the interrupt is an IRQ or a trap/exception if (regs.intno >= IRQ0) { common_irq_entry(®s); @@ -260,8 +267,14 @@ void common_isr_entry(ptregs regs) { common_exc_entry(®s); } + // Memory barrier before privilege restoration + memory_barrier(); + // Restore the original elevate status original_task->get_core()->hw_state.elevated = original_elevate_status; + + // Final memory barrier + memory_barrier(); } __PRIVILEGED_CODE @@ -446,7 +459,17 @@ bool is_valid_address(void* addr) { constexpr uint64_t upper_bound = 0xFFFFFFFFFFFF; uint64_t address = (uint64_t)addr; - return (address >= lower_bound && address <= upper_bound); + bool valid_range = (address >= lower_bound && address <= upper_bound); + + if (!valid_range) { + return false; + } + + if (paging::get_physical_address((void*)addr, paging::get_pml4()) == 0) { + return false; + } + + return true; } void print_backtrace(ptregs* regs) { @@ -462,13 +485,13 @@ void print_backtrace(ptregs* regs) { while (rbp) { // Validate that the pointer is in a reasonable range if (!is_valid_address(rbp)) { - kprint(" [Invalid RBP: 0x%llx]\n", (uint64_t)rbp); break; } uint64_t next_rip = *(rbp + 1); // Next instruction pointer (return address) - if (next_rip == 0x0) + if (!is_valid_address((void*)next_rip)) { break; + } kprint(" -> 0x%llx\n", next_rip); @@ -517,7 +540,6 @@ void panic(ptregs* regs) { arch::x86::g_cpu_exception_strings[regs->intno], regs->error); kprint(" CPU: %i\n", current->get_core()->hw_state.cpu); kprint(" Thread Name: '%s'\n", current->get_core()->identity.name); - kprint(" Privilege: %s\n", current->get_core()->hw_state.elevated ? "elevated" : "lowered"); // Control registers uint64_t cr0, cr2, cr3, cr4; @@ -529,6 +551,10 @@ void panic(ptregs* regs) { kprint("\nControl Registers:\n"); kprint(" CR0: 0x%016llx CR2: 0x%016llx\n", cr0, cr2); kprint(" CR3: 0x%016llx CR4: 0x%016llx\n", cr3, cr4); + kprint("\n"); + + process_core* core = current->get_core(); + dbg_print_vma_regions(&core->mm_ctx, core->identity.name); // Final separator kprint("============================================================\n"); diff --git a/kernel/src/arch/x86/smp/smp.cpp b/kernel/src/arch/x86/smp/smp.cpp index b3018162..0ca24c1a 100644 --- a/kernel/src/arch/x86/smp/smp.cpp +++ b/kernel/src/arch/x86/smp/smp.cpp @@ -31,11 +31,11 @@ #define IPI_STARTUP_DELAY 20 #define IPI_RETRY_DELAY 100 -#define AP_TASK_STACK_PAGES 2 -#define AP_TASK_STACK_SIZE 0x2000 - 0x10 +#define AP_TASK_STACK_PAGES 6 +#define AP_TASK_STACK_SIZE 0x6000 - 0x10 -#define AP_SYSTEM_STACK_PAGES 2 -#define AP_SYSTEM_STACK_SIZE 0x2000 - 0x10 +#define AP_SYSTEM_STACK_PAGES 6 +#define AP_SYSTEM_STACK_SIZE 0x6000 - 0x10 EXTERN_C __PRIVILEGED_CODE void asm_ap_startup(); @@ -81,6 +81,7 @@ void ap_startup_entry(uint64_t lapicid, uint64_t acpi_cpu_index) { // Initialize the AP's idle process core process_core* ap_idle_task_core = sched::get_idle_process_core(acpi_cpu_index); + ap_idle_task_core->stacks.task_stack_top = ap_system_stack_top; ap_idle_task_core->stacks.system_stack_top = ap_system_stack_top; // Create the AP's idle process diff --git a/kernel/src/boot/init.cpp b/kernel/src/boot/init.cpp index 1f24d392..2a9b6af1 100644 --- a/kernel/src/boot/init.cpp +++ b/kernel/src/boot/init.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #ifdef BUILD_UNIT_TESTS @@ -129,6 +130,10 @@ void load_initrd() { auto& vfs = fs::virtual_filesystem::get(); vfs.mount("/", kstl::make_shared()); + // Create a /tmp/ directory + vfs.create("/tmp", fs::vfs_node_type::directory, 0755); + + // Load the initial ramdisk into /initrd fs::load_cpio_initrd(reinterpret_cast(vaddr), mod_size, "/initrd"); } @@ -182,6 +187,9 @@ void init(unsigned int magic, void* mbi) { // Load the initrd if it's available load_initrd(); + // Initialize the Unix Domain Socket subsystem + net::unix_socket_manager::get().init(); + // Calibrate architecture-specific CPU timer to a tickrate of 4ms kernel_timer::calibrate_cpu_timer(4); diff --git a/kernel/src/drivers/usb/xhci/xhci.cpp b/kernel/src/drivers/usb/xhci/xhci.cpp index 42b007a5..9456d912 100644 --- a/kernel/src/drivers/usb/xhci/xhci.cpp +++ b/kernel/src/drivers/usb/xhci/xhci.cpp @@ -41,6 +41,20 @@ bool xhci_driver::init_device() { // Parse the extended capabilities _parse_extended_capability_registers(); + // Preallocate vectors to prevent memory allocation during ISR + m_port_status_change_events.resize(256); + m_command_completion_events.resize(256); + m_transfer_completion_events.resize(256); + m_port_connection_events.resize(256); + m_events_buffer.resize(256); + + // Clear the vectors to start with empty state but keep the allocated capacity + m_port_status_change_events.clear(); + m_command_completion_events.clear(); + m_transfer_completion_events.clear(); + m_port_connection_events.clear(); + m_events_buffer.clear(); + // Create a table mapping each slot to a device object for (size_t i = 0; i < m_max_device_slots; i++) { if (i >= sizeof(m_connected_devices) / sizeof(xhci_device*)) { @@ -531,18 +545,20 @@ xhci_transfer_completion_trb_t* xhci_driver::_start_control_endpoint_transfer(xh } void xhci_driver::_process_events() { + // Clear the preallocated events buffer + m_events_buffer.clear(); + // Poll the event ring for the command completion event - kstl::vector events; if (m_event_ring->has_unprocessed_events()) { - m_event_ring->dequeue_events(events); + m_event_ring->dequeue_events(m_events_buffer); } uint8_t port_change_event_status = 0; uint8_t command_completion_status = 0; uint8_t transfer_completion_status = 0; - for (size_t i = 0; i < events.size(); i++) { - xhci_trb_t* event = events[i]; + for (size_t i = 0; i < m_events_buffer.size(); i++) { + xhci_trb_t* event = m_events_buffer[i]; switch (event->trb_type) { case XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT: { port_change_event_status = 1; diff --git a/kernel/src/drivers/usb/xhci/xhci_usb_hid_kbd_driver.cpp b/kernel/src/drivers/usb/xhci/xhci_usb_hid_kbd_driver.cpp index db3a1664..fbb163a5 100644 --- a/kernel/src/drivers/usb/xhci/xhci_usb_hid_kbd_driver.cpp +++ b/kernel/src/drivers/usb/xhci/xhci_usb_hid_kbd_driver.cpp @@ -1,17 +1,6 @@ #include #include -// Global variables to track arrow key states -bool g_arrow_up_pressed = false; -bool g_arrow_down_pressed = false; -bool g_arrow_left_pressed = false; -bool g_arrow_right_pressed = false; - -static constexpr uint8_t KEY_UP = 0x52; -static constexpr uint8_t KEY_DOWN = 0x51; -static constexpr uint8_t KEY_LEFT = 0x50; -static constexpr uint8_t KEY_RIGHT = 0x4F; - // Max keys tracked in standard HID boot protocol constexpr size_t MAX_KEYS = 6; @@ -58,27 +47,6 @@ void xhci_usb_hid_kbd_driver::on_device_init() { } void xhci_usb_hid_kbd_driver::on_device_event(uint8_t* data) { - bool new_arrow_up_pressed = false; - bool new_arrow_down_pressed = false; - bool new_arrow_left_pressed = false; - bool new_arrow_right_pressed = false; - - for (int i = 2; i < 8; i++) { // Check the key press array - switch (data[i]) { - case KEY_UP: new_arrow_up_pressed = true; break; - case KEY_DOWN: new_arrow_down_pressed = true; break; - case KEY_LEFT: new_arrow_left_pressed = true; break; - case KEY_RIGHT: new_arrow_right_pressed = true; break; - default: break; - } - } - - // Update global states - g_arrow_up_pressed = new_arrow_up_pressed; - g_arrow_down_pressed = new_arrow_down_pressed; - g_arrow_left_pressed = new_arrow_left_pressed; - g_arrow_right_pressed = new_arrow_right_pressed; - const uint8_t* current_keys = &data[2]; uint8_t modifier_byte = data[0]; @@ -147,12 +115,20 @@ void xhci_usb_hid_kbd_driver::_process_input_report( void xhci_usb_hid_kbd_driver::_emit_key_event( uint8_t key, input::input_event_type type, uint32_t modifiers ) { - input::input_event_t evt{}; - evt.id = 0; - evt.type = type; - evt.udata1 = modifiers; - evt.sdata1 = translate_hid_usage_to_ascii(key, modifiers); - + int32_t sdata1 = (type == input::KBD_EVT_KEY_PRESSED) ? + translate_hid_usage_to_ascii(key, modifiers) : + 0; + + + input::input_event_t event = { + .id = 0, // Event ID (can be used for tracking) + .type = type, + .udata1 = static_cast(key), // keycode + .udata2 = modifiers, // modifiers (Ctrl, Alt, Shift, etc.) + .sdata1 = sdata1, // ASCII character / reserved + .sdata2 = 0 // reserved + }; + auto& input_manager = input::system_input_manager::get(); - input_manager.push_event(INPUT_QUEUE_ID_KBD, evt); + input_manager.push_event(INPUT_QUEUE_ID_SYSTEM, event); } diff --git a/kernel/src/drivers/usb/xhci/xhci_usb_hid_mouse_driver.cpp b/kernel/src/drivers/usb/xhci/xhci_usb_hid_mouse_driver.cpp index 0a207a88..03518121 100644 --- a/kernel/src/drivers/usb/xhci/xhci_usb_hid_mouse_driver.cpp +++ b/kernel/src/drivers/usb/xhci/xhci_usb_hid_mouse_driver.cpp @@ -1,9 +1,12 @@ #include #include +#include int64_t g_mouse_cursor_pos_x = 100; int64_t g_mouse_cursor_pos_y = 100; -bool g_mouse_button_pressed = false; + +int64_t g_max_mouse_cursor_pos_x = 4095; +int64_t g_max_mouse_cursor_pos_y = 4095; static int32_t parse_signed_field(const uint8_t* data, uint16_t bit_offset, uint16_t bit_size) { int32_t value = 0; @@ -26,6 +29,11 @@ void xhci_usb_hid_mouse_driver::on_device_init() { m_input_layout.buttons_size = 1; m_input_layout.x_axis_size = 1; m_input_layout.y_axis_size = 1; + m_input_layout.scroll_offset = 3; + m_input_layout.scroll_size = 1; + + // Initialize button state tracking + m_previous_button_state = 0; kstl::vector hid_report_items; bool success = hid::hid_report_parser::parse_descriptor( @@ -52,16 +60,100 @@ void xhci_usb_hid_mouse_driver::on_device_init() { _initialize_input_field(layout, static_cast(hid::usage_page::generic_desktop), static_cast(hid::generic_desktop_usage::y_axis), m_input_layout.y_axis_offset, m_input_layout.y_axis_size, "Y-axis"); + + _initialize_input_field(layout, static_cast(hid::usage_page::generic_desktop), + static_cast(hid::generic_desktop_usage::wheel), + m_input_layout.scroll_offset, m_input_layout.scroll_size, "Scroll wheel"); } void xhci_usb_hid_mouse_driver::on_device_event(uint8_t* data) { + // Parse input data from HID report int32_t dx = parse_signed_field(data, m_input_layout.x_axis_offset, m_input_layout.x_axis_size); int32_t dy = parse_signed_field(data, m_input_layout.y_axis_offset, m_input_layout.y_axis_size); - int32_t buttons = parse_signed_field(data, m_input_layout.buttons_offset, m_input_layout.buttons_size); + uint32_t buttons = static_cast(parse_signed_field(data, m_input_layout.buttons_offset, m_input_layout.buttons_size)); + int32_t scroll = parse_signed_field(data, m_input_layout.scroll_offset, m_input_layout.scroll_size); + // Update global cursor position g_mouse_cursor_pos_x += dx; g_mouse_cursor_pos_y += dy; - g_mouse_button_pressed = buttons & 1; + + // Clamp cursor position to reasonable bounds (0-4095 for now) + if (g_mouse_cursor_pos_x < 0) g_mouse_cursor_pos_x = 0; + if (g_mouse_cursor_pos_y < 0) g_mouse_cursor_pos_y = 0; + if (g_mouse_cursor_pos_x > g_max_mouse_cursor_pos_x) g_mouse_cursor_pos_x = g_max_mouse_cursor_pos_x; + if (g_mouse_cursor_pos_y > g_max_mouse_cursor_pos_y) g_mouse_cursor_pos_y = g_max_mouse_cursor_pos_y; + + // Emit mouse movement event if there was movement + if (dx != 0 || dy != 0) { + _emit_input_event( + input::POINTER_EVT_MOUSE_MOVED, + static_cast(g_mouse_cursor_pos_x), // x_pos + static_cast(g_mouse_cursor_pos_y), // y_pos + dx, // delta_x + dy // delta_y + ); + } + + // Handle button press/release events + uint32_t button_changes = buttons ^ m_previous_button_state; + if (button_changes != 0) { + // Check each button bit (up to 8 buttons) + for (int button_num = 1; button_num <= 8; button_num++) { + uint32_t button_mask = 1U << (button_num - 1); + + if (button_changes & button_mask) { + bool is_pressed = (buttons & button_mask) != 0; + + if (is_pressed) { + // Button pressed + _emit_input_event( + input::POINTER_EVT_MOUSE_BTN_PRESSED, + button_num, // button number + static_cast(g_mouse_cursor_pos_x), // x_pos + static_cast(g_mouse_cursor_pos_y), // y_pos + 0 // reserved + ); + } else { + // Button released + _emit_input_event( + input::POINTER_EVT_MOUSE_BTN_RELEASED, + button_num, // button number + static_cast(g_mouse_cursor_pos_x), // x_pos + static_cast(g_mouse_cursor_pos_y), // y_pos + 0 // reserved + ); + } + } + } + + // Update previous button state + m_previous_button_state = buttons; + } + + // Handle scroll wheel events + if (scroll != 0) { + _emit_input_event( + input::POINTER_EVT_MOUSE_SCROLLED, + 0, // scroll_type (0=vertical) + static_cast(g_mouse_cursor_pos_x), // x_pos + static_cast(g_mouse_cursor_pos_y), // y_pos + scroll // scroll_delta + ); + } +} + +void xhci_usb_hid_mouse_driver::_emit_input_event(uint32_t event_type, uint32_t udata1, uint32_t udata2, int32_t sdata1, int32_t sdata2) { + input::input_event_t event = { + .id = 0, // Event ID (can be used for tracking) + .type = static_cast(event_type), + .udata1 = udata1, + .udata2 = udata2, + .sdata1 = sdata1, + .sdata2 = sdata2 + }; + + auto& input_mgr = input::system_input_manager::get(); + input_mgr.push_event(INPUT_QUEUE_ID_SYSTEM, event); } void xhci_usb_hid_mouse_driver::_initialize_input_field( diff --git a/kernel/src/input/serial_irq.cpp b/kernel/src/input/serial_irq.cpp index e908738e..4df39a1e 100644 --- a/kernel/src/input/serial_irq.cpp +++ b/kernel/src/input/serial_irq.cpp @@ -11,14 +11,17 @@ irqreturn_t __com1_irq_handler(ptregs*, void*) { // Read the input character from serial port char input_char = serial::read(SERIAL_PORT_BASE_COM1); - // Create an input event structure - input::input_event_t evt; - zeromem(&evt, sizeof(input::input_event_t)); + // Create a keyboard key pressed event structure + input::input_event_t evt{}; + evt.id = 0; evt.type = input::KBD_EVT_KEY_PRESSED; + evt.udata1 = static_cast(input_char); + evt.udata2 = 0; // No modifiers for serial input evt.sdata1 = static_cast(input_char); + evt.sdata2 = 0; // Push the event into the kernel input queue - input_manager.push_event(INPUT_QUEUE_ID_KBD, evt); + input_manager.push_event(INPUT_QUEUE_ID_SYSTEM, *reinterpret_cast(&evt)); return IRQ_HANDLED; } diff --git a/kernel/src/input/system_input_manager.cpp b/kernel/src/input/system_input_manager.cpp index afb43aa6..aeec6d71 100644 --- a/kernel/src/input/system_input_manager.cpp +++ b/kernel/src/input/system_input_manager.cpp @@ -12,8 +12,7 @@ void system_input_manager::init() { m_input_queues = kstl::hashmap(); // Initialize default input queues - create_queue(INPUT_QUEUE_ID_KBD, 128); - create_queue(INPUT_QUEUE_ID_POINTER, 128); + create_queue(INPUT_QUEUE_ID_SYSTEM, 512); } bool system_input_manager::create_queue(uint32_t queue_id, size_t capacity) { @@ -38,5 +37,13 @@ bool system_input_manager::push_event(uint32_t queue_id, const input_event_t& ev return false; // Queue does not exist } return queue->push_event(event); -} +} + +/** + * @brief Gets the system input queue (ID: INPUT_QUEUE_ID_SYSTEM). + * @return Pointer to the system input queue, or nullptr if not found. + */ +input_queue* system_input_manager::get_system_input_queue() { + return m_input_queues[INPUT_QUEUE_ID_SYSTEM]; +} } // namespace input diff --git a/kernel/src/ipc/shm.cpp b/kernel/src/ipc/shm.cpp index 606595f1..198f6964 100644 --- a/kernel/src/ipc/shm.cpp +++ b/kernel/src/ipc/shm.cpp @@ -1,7 +1,12 @@ #include #include #include +#include #include +#include +#include +#include +#include namespace ipc { @@ -99,41 +104,109 @@ bool shared_memory::destroy(shm_handle_t handle) { return true; } -void* shared_memory::map(shm_handle_t handle, uint64_t flags) { +void* shared_memory::map(shm_handle_t handle, uint64_t flags, shm_mapping_context context) { shm_region* region = get_region(handle); if (!region) { return nullptr; } mutex_guard region_guard(region->lock); - const size_t page_count = region->pages.size(); - void* virt_base = nullptr; - RUN_ELEVATED({ - virt_base = vmm::alloc_contiguous_virtual_pages(page_count, flags); - }); - - if (!virt_base) { - return nullptr; - } + if (context == shm_mapping_context::KERNEL) { + // Kernel mapping path - use existing implementation + void* virt_base = nullptr; + + RUN_ELEVATED({ + virt_base = vmm::alloc_contiguous_virtual_pages(page_count, flags); + }); + + if (!virt_base) { + return nullptr; + } + + RUN_ELEVATED({ + for (size_t i = 0; i < page_count; ++i) { + paging::map_page( + reinterpret_cast(virt_base) + i * PAGE_SIZE, + region->pages[i], + flags, + paging::get_pml4() + ); + } + }); + + region->ref_count++; + return virt_base; + } else { + // Userland mapping path - similar to mmap implementation + if (!current || !current->get_core()) { + return nullptr; + } + + mm_context* mm_ctx = ¤t->get_core()->mm_ctx; + size_t mapping_size = page_count * PAGE_SIZE; + + // Find free virtual address range in userland space + uintptr_t target_addr = find_free_vma_range(mm_ctx, mapping_size, 0, 0); + if (!target_addr) { + return nullptr; // No free address range found + } + + // Convert shm_access policy to VMA protection flags + uint64_t vma_prot = 0; + if (region->policy == shm_access::READ_ONLY || region->policy == shm_access::READ_WRITE) { + vma_prot |= VMA_PROT_READ; + } + if (region->policy == shm_access::READ_WRITE) { + vma_prot |= VMA_PROT_WRITE; + } + + // Create VMA for the shared memory region + uint64_t vma_type = VMA_TYPE_SHARED | VMA_TYPE_ANONYMOUS; + vma_area* vma = create_vma(mm_ctx, target_addr, mapping_size, vma_prot, vma_type); + if (!vma) { + return nullptr; // Failed to create VMA + } + + // Convert protection flags to page table flags + uint64_t page_flags = PTE_PRESENT | PTE_US; // User accessible + if (region->policy == shm_access::READ_WRITE) { + page_flags |= PTE_RW; + } + // Note: We don't set PTE_NX since shared memory should be non-executable - RUN_ELEVATED({ - for (size_t i = 0; i < page_count; ++i) { - paging::map_page( - reinterpret_cast(virt_base) + i * PAGE_SIZE, - region->pages[i], - flags, - paging::get_pml4() - ); + // Map the shared memory pages into userland virtual memory + paging::page_table* page_table = reinterpret_cast(mm_ctx->root_page_table); + bool mapping_failed = false; + + RUN_ELEVATED({ + for (size_t i = 0; i < page_count; ++i) { + uintptr_t virt_addr = target_addr + (i * PAGE_SIZE); + uintptr_t phys_addr = region->pages[i]; + + paging::map_page(virt_addr, phys_addr, page_flags, page_table); + } + }); + + if (mapping_failed) { + // Clean up: unmap any successfully mapped pages and remove VMA + RUN_ELEVATED({ + for (size_t i = 0; i < page_count; ++i) { + uintptr_t virt_addr = target_addr + (i * PAGE_SIZE); + paging::unmap_page(virt_addr, page_table); + } + }); + remove_vma(mm_ctx, vma); + return nullptr; } - }); - region->ref_count++; - return virt_base; + region->ref_count++; + return reinterpret_cast(target_addr); + } } -bool shared_memory::unmap(shm_handle_t handle, void* addr) { +bool shared_memory::unmap(shm_handle_t handle, void* addr, shm_mapping_context context) { shm_region* region = get_region(handle); if (!region || !addr) { return false; @@ -142,9 +215,40 @@ bool shared_memory::unmap(shm_handle_t handle, void* addr) { mutex_guard region_guard(region->lock); size_t page_count = region->pages.size(); - RUN_ELEVATED({ - vmm::unmap_contiguous_virtual_pages(reinterpret_cast(addr), page_count); - }); + if (context == shm_mapping_context::KERNEL) { + // Kernel unmapping path - use existing implementation + RUN_ELEVATED({ + vmm::unmap_contiguous_virtual_pages(reinterpret_cast(addr), page_count); + }); + } else { + // Userland unmapping path - similar to munmap implementation + if (!current || !current->get_core()) { + return false; + } + + mm_context* mm_ctx = ¤t->get_core()->mm_ctx; + uintptr_t virt_addr = reinterpret_cast(addr); + size_t mapping_size = page_count * PAGE_SIZE; + + // Find the VMA containing this address + vma_area* vma = find_vma(mm_ctx, virt_addr); + if (!vma || vma->start != virt_addr || (vma->end - vma->start) != mapping_size) { + return false; // Invalid address or size mismatch + } + + // Unmap the pages from userland virtual memory + paging::page_table* page_table = reinterpret_cast(mm_ctx->root_page_table); + + RUN_ELEVATED({ + for (size_t i = 0; i < page_count; ++i) { + uintptr_t page_addr = virt_addr + (i * PAGE_SIZE); + paging::unmap_page(page_addr, page_table); + } + }); + + // Remove the VMA + remove_vma(mm_ctx, vma); + } if (region->ref_count > 0) { region->ref_count--; diff --git a/kernel/src/net/unix_socket.cpp b/kernel/src/net/unix_socket.cpp new file mode 100644 index 00000000..d7c319a7 --- /dev/null +++ b/kernel/src/net/unix_socket.cpp @@ -0,0 +1,432 @@ +#include +#include +#include +#include +#include + +namespace net { + +unix_stream_socket::unix_stream_socket() { + _setup_buffers(); +} + +unix_stream_socket::~unix_stream_socket() { + // Only close if not already closed to avoid double-close deadlock + if (m_state.load() != unix_socket_state::CLOSED) { + close(); + } +} + +void unix_stream_socket::_setup_buffers() { + m_recv_buffer = kstl::shared_ptr(new unix_socket_buffer()); + m_send_buffer = kstl::shared_ptr(new unix_socket_buffer()); +} + +void unix_stream_socket::_change_state(unix_socket_state new_state) { + unix_socket_state old_state = m_state.exchange(new_state); + __unused old_state; + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Socket state changed: %u -> %u\n", + static_cast(old_state), static_cast(new_state)); +} + +bool unix_stream_socket::_can_accept() const { + return m_state.load() == unix_socket_state::LISTENING && m_is_server; +} + +bool unix_stream_socket::_can_read() const { + unix_socket_state state = m_state.load(); + return state == unix_socket_state::CONNECTED || state == unix_socket_state::DISCONNECTED; +} + +bool unix_stream_socket::_can_write() const { + unix_socket_state state = m_state.load(); + return state == unix_socket_state::CONNECTED; +} + +void unix_stream_socket::_cleanup_resources() { + // NOTE: This method assumes m_socket_lock is already held by the caller + + // Clear peer connection + m_peer = kstl::shared_ptr(nullptr); + + // Clear pending connections + { + mutex_guard accept_guard(m_accept_lock); + m_pending_connections.clear(); + } + + // Clear buffers + if (m_recv_buffer) { + m_recv_buffer->clear(); + } + if (m_send_buffer) { + m_send_buffer->clear(); + } +} + +int unix_stream_socket::_add_pending_connection(kstl::shared_ptr client) { + mutex_guard guard(m_accept_lock); + + if (static_cast(m_pending_connections.size()) >= m_backlog) { + return -ECONNREFUSED; // Queue is full + } + + m_pending_connections.push_back(client); + return 0; +} + +kstl::shared_ptr unix_stream_socket::_get_pending_connection() { + mutex_guard guard(m_accept_lock); + + if (m_pending_connections.empty()) { + return kstl::shared_ptr(nullptr); + } + + auto client = m_pending_connections[0]; + m_pending_connections.erase(0); + return client; +} + +void unix_stream_socket::_set_peer(kstl::shared_ptr peer) { + mutex_guard guard(m_socket_lock); + m_peer = peer; +} + +int unix_stream_socket::bind(const kstl::string& path) { + mutex_guard guard(m_socket_lock); + + if (m_state.load() != unix_socket_state::CREATED) { + return -EINVAL; // Socket already bound or in use + } + + if (path.empty()) { + return -EINVAL; // Invalid path + } + + m_path = path; + m_is_server = true; + _change_state(unix_socket_state::BOUND); + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Socket bound to path: %s (registration deferred)\n", path.c_str()); + return 0; +} + +int unix_stream_socket::listen(int backlog) { + mutex_guard guard(m_socket_lock); + + if (m_state.load() != unix_socket_state::BOUND) { + return -EINVAL; // Must bind before listening + } + + if (backlog <= 0) { + backlog = 5; // Default backlog + } + + m_backlog = backlog; + _change_state(unix_socket_state::LISTENING); + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Socket listening with backlog: %d\n", backlog); + return 0; +} + +kstl::shared_ptr unix_stream_socket::accept() { + if (!_can_accept()) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Cannot accept on this socket\n"); + return kstl::shared_ptr(nullptr); + } + + // Try to get a pending connection immediately + auto client = _get_pending_connection(); + if (client) { + // Create a new socket for this connection + auto connection_socket = kstl::shared_ptr(new unix_stream_socket()); + if (!connection_socket) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Failed to create connection socket\n"); + return kstl::shared_ptr(nullptr); + } + + // Set up the bidirectional connection + connection_socket->m_peer = client; + client->m_peer = connection_socket; + + // Both sockets are now connected + connection_socket->_change_state(unix_socket_state::CONNECTED); + client->_change_state(unix_socket_state::CONNECTED); + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Accepted connection\n"); + return connection_socket; + } + + // No pending connections available + if (m_nonblocking) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] No pending connections (non-blocking)\n"); + return kstl::shared_ptr(nullptr); // Will be converted to EAGAIN by fcntl wrapper + } + + // Blocking mode - wait for a connection + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Waiting for incoming connections (blocking)...\n"); + while (true) { + client = _get_pending_connection(); + if (client) { + // Create a new socket for this connection + auto connection_socket = kstl::shared_ptr(new unix_stream_socket()); + if (!connection_socket) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Failed to create connection socket\n"); + return kstl::shared_ptr(nullptr); + } + + // Set up the bidirectional connection + connection_socket->m_peer = client; + client->m_peer = connection_socket; + + // Both sockets are now connected + connection_socket->_change_state(unix_socket_state::CONNECTED); + client->_change_state(unix_socket_state::CONNECTED); + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Accepted connection\n"); + return connection_socket; + } + + // No pending connections, yield and try again + sched::yield(); + + // Check if we're still listening + if (!_can_accept()) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Socket no longer accepting connections\n"); + return kstl::shared_ptr(nullptr); + } + } +} + +int unix_stream_socket::connect(const kstl::string& path) { + mutex_guard guard(m_socket_lock); + + if (m_state.load() != unix_socket_state::CREATED) { + return -EINVAL; // Socket already connected or in use + } + + if (path.empty()) { + return -EINVAL; // Invalid path + } + + _change_state(unix_socket_state::CONNECTING); + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Attempting to connect to: %s\n", path.c_str()); + + // Find the server socket via socket manager + auto& manager = unix_socket_manager::get(); + auto server = manager.find_socket(path); + if (!server) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Server socket not found at path: %s\n", path.c_str()); + _change_state(unix_socket_state::CREATED); + return -ECONNREFUSED; + } + + // For now, directly add ourselves to the server's pending queue + // The shared_ptr issue will be resolved when sockets are created via manager + int result = server->_add_pending_connection(kstl::shared_ptr(this)); + if (result != 0) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Failed to add to server's pending queue: %d\n", result); + _change_state(unix_socket_state::CREATED); + return result; + } + + // For Unix domain sockets, connection is typically instant, but check mode + if (m_nonblocking) { + // Non-blocking mode: check if connection completed immediately + if (m_state.load() == unix_socket_state::CONNECTED) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Successfully connected to: %s (immediate)\n", path.c_str()); + return 0; + } else { + // Connection in progress - for Unix sockets this is usually instant + // but we'll return EINPROGRESS to be semantically correct + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Connection in progress (non-blocking)\n"); + return -EINPROGRESS; + } + } + + // Blocking mode: wait for the server to accept us (our state will change to CONNECTED) + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Waiting for server to accept connection (blocking)...\n"); + while (m_state.load() == unix_socket_state::CONNECTING) { + sched::yield(); + } + + if (m_state.load() == unix_socket_state::CONNECTED) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Successfully connected to: %s\n", path.c_str()); + return 0; + } else { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Connection failed or was rejected\n"); + _change_state(unix_socket_state::CREATED); + return -ECONNREFUSED; + } +} + +ssize_t unix_stream_socket::read(void* buffer, size_t size) { + if (!buffer || size == 0) { + return -EINVAL; + } + + unix_socket_state current_state = m_state.load(); + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] read() called, socket state: %u\n", static_cast(current_state)); + + // If socket is not connected and not disconnected, it's an error + if (current_state != unix_socket_state::CONNECTED && + current_state != unix_socket_state::DISCONNECTED) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] read() returning ENOTCONN for state %u\n", static_cast(current_state)); + return -ENOTCONN; + } + + if (!m_recv_buffer) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] read() returning EBADF - no recv buffer\n"); + return -EBADF; // Invalid socket state + } + + // Check if we have any data to read + if (m_recv_buffer->has_data()) { + // Data is available, read it + size_t bytes_read = m_recv_buffer->read(buffer, size); + return static_cast(bytes_read); + } + + // No data available + if (current_state == unix_socket_state::DISCONNECTED) { + // Peer has disconnected and no more data - return EOF + return 0; + } + + // Still connected but no data available + if (m_nonblocking) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] No data available (non-blocking)\n"); + return -EAGAIN; // Non-blocking mode: return immediately + } + + // Blocking mode - wait until data arrives or disconnection + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Blocking until data arrives...\n"); + while (true) { + current_state = m_state.load(); + + // Check for disconnection + if (current_state == unix_socket_state::DISCONNECTED) { + // Check one more time for any remaining data + if (m_recv_buffer->has_data()) { + size_t bytes_read = m_recv_buffer->read(buffer, size); + return static_cast(bytes_read); + } + return 0; // EOF - peer disconnected and no more data + } + + // Check if connection is broken + if (current_state != unix_socket_state::CONNECTED) { + return -ENOTCONN; + } + + // Check for new data + if (m_recv_buffer->has_data()) { + size_t bytes_read = m_recv_buffer->read(buffer, size); + return static_cast(bytes_read); + } + + // No data available, yield and try again + sched::yield(); + } +} + +ssize_t unix_stream_socket::write(const void* data, size_t size) { + if (!data || size == 0) { + return -EINVAL; + } + + if (!_can_write()) { + return -ENOTCONN; + } + + // Get peer's receive buffer + auto peer = m_peer; + if (!peer || !peer->m_recv_buffer) { + return -EPIPE; // Broken pipe + } + + // Try to write to peer's receive buffer + size_t bytes_written = peer->m_recv_buffer->write(data, size); + + if (bytes_written == 0 && size > 0) { + // Peer's buffer is full + if (m_nonblocking) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Peer buffer full (non-blocking)\n"); + return -EAGAIN; // Non-blocking mode: return immediately + } else { + // Blocking mode: for now, we'll return 0 to indicate no bytes written + // In a full implementation, we might want to block until space is available + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Warning: Peer buffer full, no bytes written (blocking)\n"); + return 0; + } + } + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Wrote %llu bytes to peer\n", bytes_written); + return static_cast(bytes_written); +} + +int unix_stream_socket::close() { + mutex_guard guard(m_socket_lock); + + unix_socket_state current_state = m_state.load(); + if (current_state == unix_socket_state::CLOSED) { + return 0; // Already closed + } + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Closing socket (path: %s)\n", + m_path.empty() ? "none" : m_path.c_str()); + + // Unregister from manager if we're a bound server socket + if (m_is_server && !m_path.empty()) { + auto& manager = unix_socket_manager::get(); + manager.unregister_socket(m_path); + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Unregistered socket from manager\n"); + } + + // Notify peer of disconnection + if (m_peer) { + m_peer->_change_state(unix_socket_state::DISCONNECTED); + m_peer->m_peer = kstl::shared_ptr(nullptr); + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Notified peer of disconnection\n"); + } + + _cleanup_resources(); + _change_state(unix_socket_state::CLOSED); + + return 0; +} + +int unix_stream_socket::register_with_manager(kstl::shared_ptr self) { + if (!m_is_server || m_path.empty()) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Only bound server sockets can be registered\n"); + return -EINVAL; + } + + if (m_state.load() != unix_socket_state::BOUND) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Socket must be bound before registration\n"); + return -EINVAL; + } + + auto& manager = unix_socket_manager::get(); + if (!manager.register_socket(m_path, self)) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Error: Failed to register socket with manager\n"); + return -EADDRINUSE; + } + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Socket registered with manager at path: %s\n", m_path.c_str()); + return 0; +} + +int unix_stream_socket::set_nonblocking(bool nonblocking) { + mutex_guard guard(m_socket_lock); + + m_nonblocking = nonblocking; + + UNIX_SOCKET_TRACE("[UNIX_SOCKET] Socket set to %s mode\n", + nonblocking ? "non-blocking" : "blocking"); + return 0; +} + +} // namespace net \ No newline at end of file diff --git a/kernel/src/net/unix_socket_buffer.cpp b/kernel/src/net/unix_socket_buffer.cpp new file mode 100644 index 00000000..51788569 --- /dev/null +++ b/kernel/src/net/unix_socket_buffer.cpp @@ -0,0 +1,195 @@ +#include +#include +#include + +namespace net { + +unix_socket_buffer::unix_socket_buffer(size_t capacity) + : m_capacity(capacity), m_head(0), m_tail(0), m_size(0) { + + if (capacity == 0) { + kprint("[UNIX_SOCKET] Error: Buffer capacity cannot be zero\n"); + m_capacity = DEFAULT_BUFFER_SIZE; + } + + m_buffer = new uint8_t[m_capacity]; + if (!m_buffer) { + kprint("[UNIX_SOCKET] Error: Failed to allocate buffer of size %llu\n", m_capacity); + // This is a critical error - we can't function without a buffer + m_capacity = 0; + return; + } + + // Initialize buffer to zero for debugging + memset(m_buffer, 0, m_capacity); +} + +unix_socket_buffer::~unix_socket_buffer() { + if (m_buffer) { + delete[] m_buffer; + m_buffer = nullptr; + } +} + +size_t unix_socket_buffer::write(const void* data, size_t size) { + if (!data || size == 0 || !m_buffer) { + return 0; + } + + mutex_guard guard(m_lock); + + size_t current_size = m_size.load(); + size_t available_space = m_capacity - current_size; + + if (available_space == 0) { + return 0; // Buffer is full + } + + // Limit write size to available space + size_t bytes_to_write = (size > available_space) ? available_space : size; + const uint8_t* src = static_cast(data); + size_t bytes_written = 0; + + // Handle circular buffer wrapping - may need to write in two parts + while (bytes_written < bytes_to_write) { + size_t contiguous_space = _contiguous_write_space(); + if (contiguous_space == 0) { + break; // Should not happen, but safety check + } + + size_t chunk_size = bytes_to_write - bytes_written; + if (chunk_size > contiguous_space) { + chunk_size = contiguous_space; + } + + // Copy data to buffer + memcpy(m_buffer + m_head, src + bytes_written, chunk_size); + + // Update head position + m_head = (m_head + chunk_size) % m_capacity; + bytes_written += chunk_size; + } + + // Update size atomically + m_size.fetch_add(bytes_written); + + return bytes_written; +} + +size_t unix_socket_buffer::read(void* buffer, size_t size) { + if (!buffer || size == 0 || !m_buffer) { + return 0; + } + + mutex_guard guard(m_lock); + + size_t current_size = m_size.load(); + if (current_size == 0) { + return 0; // Buffer is empty + } + + // Limit read size to available data + size_t bytes_to_read = (size > current_size) ? current_size : size; + uint8_t* dest = static_cast(buffer); + size_t bytes_read = 0; + + // Handle circular buffer wrapping - may need to read in two parts + while (bytes_read < bytes_to_read) { + size_t contiguous_data = _contiguous_read_space(); + if (contiguous_data == 0) { + break; // Should not happen, but safety check + } + + size_t chunk_size = bytes_to_read - bytes_read; + if (chunk_size > contiguous_data) { + chunk_size = contiguous_data; + } + + // Copy data from buffer + memcpy(dest + bytes_read, m_buffer + m_tail, chunk_size); + + // Update tail position + m_tail = (m_tail + chunk_size) % m_capacity; + bytes_read += chunk_size; + } + + // Update size atomically + m_size.fetch_sub(bytes_read); + + return bytes_read; +} + +bool unix_socket_buffer::has_data() const { + return m_size.load() > 0; +} + +bool unix_socket_buffer::has_space() const { + return m_size.load() < m_capacity; +} + +size_t unix_socket_buffer::available_bytes() const { + return m_size.load(); +} + +size_t unix_socket_buffer::free_space() const { + return m_capacity - m_size.load(); +} + +void unix_socket_buffer::clear() { + mutex_guard guard(m_lock); + + m_head = 0; + m_tail = 0; + m_size.store(0); + + // Optional: Clear buffer memory for security + if (m_buffer) { + memset(m_buffer, 0, m_capacity); + } +} + +size_t unix_socket_buffer::_next_index(size_t index) const { + return (index + 1) % m_capacity; +} + +size_t unix_socket_buffer::_contiguous_write_space() const { + size_t current_size = m_size.load(); + size_t available_space = m_capacity - current_size; + + if (available_space == 0) { + return 0; + } + + // If head is before tail, we can write until we reach tail + // If head is at or after tail, we can write until end of buffer + if (m_head < m_tail) { + return m_tail - m_head; + } else { + // Write to end of buffer, unless we would wrap to tail + size_t space_to_end = m_capacity - m_head; + if (m_tail == 0 && current_size > 0) { + // Special case: can't write to position 0 if tail is there + return space_to_end - 1; + } + return space_to_end; + } +} + +size_t unix_socket_buffer::_contiguous_read_space() const { + size_t current_size = m_size.load(); + + if (current_size == 0) { + return 0; + } + + // If tail is before head, we can read until we reach head + // If tail is at or after head, we can read until end of buffer + if (m_tail < m_head) { + return m_head - m_tail; + } else { + // Read to end of buffer + return m_capacity - m_tail; + } +} + +} // namespace net diff --git a/kernel/src/net/unix_socket_manager.cpp b/kernel/src/net/unix_socket_manager.cpp new file mode 100644 index 00000000..bc9a62ce --- /dev/null +++ b/kernel/src/net/unix_socket_manager.cpp @@ -0,0 +1,204 @@ +#include +#include +#include +#include + +namespace net { + +// Error codes for Unix socket manager +#define ENOTINIT 125 // Manager not initialized +#define EADDRINUSE 98 // Address already in use +#define ENOENT 2 // No such file or directory +#define EINVAL 22 // Invalid argument +#define ECONNREFUSED 111 // Connection refused + +unix_socket_manager unix_socket_manager::s_singleton; + +unix_socket_manager& unix_socket_manager::get() { + return s_singleton; +} + +void unix_socket_manager::init() { + mutex_guard guard(m_manager_lock); + + if (m_initialized) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Warning: Manager already initialized\n"); + return; + } + + // Initialize the hashmap + m_bound_sockets = kstl::hashmap>(); + + m_initialized = true; +} + +bool unix_socket_manager::register_socket(const kstl::string& path, + kstl::shared_ptr socket) { + if (!m_initialized) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Manager not initialized\n"); + return false; + } + + if (!socket) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Cannot register null socket\n"); + return false; + } + + if (!_is_valid_path(path)) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Invalid path: %s\n", path.c_str()); + return false; + } + + mutex_guard guard(m_manager_lock); + + // Check if path is already in use + if (m_bound_sockets.find(path)) { + _log_socket_operation("register", path, false); + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Path already in use: %s\n", path.c_str()); + return false; + } + + // Register the socket + bool success = m_bound_sockets.insert(path, socket); + _log_socket_operation("register", path, success); + + if (!success) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Failed to register socket at path: %s\n", path.c_str()); + } + + return success; +} + +bool unix_socket_manager::unregister_socket(const kstl::string& path) { + if (!m_initialized) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Manager not initialized\n"); + return false; + } + + if (!_is_valid_path(path)) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Invalid path: %s\n", path.c_str()); + return false; + } + + mutex_guard guard(m_manager_lock); + + bool success = m_bound_sockets.remove(path); + _log_socket_operation("unregister", path, success); + + if (!success) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Warning: Path not found for unregistration: %s\n", path.c_str()); + } + + return success; +} + +kstl::shared_ptr unix_socket_manager::find_socket(const kstl::string& path) { + if (!m_initialized) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Manager not initialized\n"); + return kstl::shared_ptr(nullptr); + } + + if (!_is_valid_path(path)) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Invalid path: %s\n", path.c_str()); + return kstl::shared_ptr(nullptr); + } + + mutex_guard guard(m_manager_lock); + + auto* socket_ptr = m_bound_sockets.get(path); + if (socket_ptr) { + _log_socket_operation("find", path, true); + return *socket_ptr; + } + + _log_socket_operation("find", path, false); + return kstl::shared_ptr(nullptr); +} + +kstl::shared_ptr unix_socket_manager::create_socket() { + if (!m_initialized) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Manager not initialized\n"); + return kstl::shared_ptr(nullptr); + } + + // Create a new socket instance + auto socket = kstl::shared_ptr(new unix_stream_socket()); + if (!socket) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Error: Failed to create socket\n"); + return kstl::shared_ptr(nullptr); + } + + return socket; +} + +size_t unix_socket_manager::get_socket_count() const { + if (!m_initialized) { + return 0; + } + + mutex_guard guard(m_manager_lock); + return m_bound_sockets.size(); +} + +void unix_socket_manager::cleanup() { + mutex_guard guard(m_manager_lock); + + if (!m_initialized) { + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] Warning: Manager not initialized, nothing to cleanup\n"); + return; + } + + // Get all keys before clearing (to avoid iterator invalidation) + auto paths = m_bound_sockets.keys(); + + // Close all registered sockets + for (const auto& path : paths) { + auto* socket_ptr = m_bound_sockets.get(path); + if (socket_ptr && *socket_ptr) { + (*socket_ptr)->close(); + } + } + + // Clear the hashmap + m_bound_sockets = kstl::hashmap>(); + + m_initialized = false; +} + +bool unix_socket_manager::_is_valid_path(const kstl::string& path) const { + // Basic path validation + if (path.empty()) { + return false; + } + + // Unix socket paths should start with '/' for absolute paths + // or be relative paths (not starting with '/') + // For now, accept any non-empty path + const char* path_str = path.c_str(); + size_t len = strlen(path_str); + + // Check for reasonable length limits + if (len > 255) { // Typical filesystem path limit + return false; + } + + // Check for null bytes within the path + for (size_t i = 0; i < len; i++) { + if (path_str[i] == '\0') { + return false; + } + } + + return true; +} + +void unix_socket_manager::_log_socket_operation(const char* operation, + const kstl::string& path, + bool success) const { + const char* status = success ? "SUCCESS" : "FAILED"; + __unused status; __unused operation; __unused path; + UNIX_SOCKET_TRACE("[UNIX_SOCKET_MGR] %s operation %s for path: %s\n", + operation, status, path.c_str()); +} + +} // namespace net \ No newline at end of file diff --git a/kernel/src/syscall/handlers/sys_arch.cpp b/kernel/src/syscall/handlers/sys_arch.cpp index 756b5783..c16489b0 100644 --- a/kernel/src/syscall/handlers/sys_arch.cpp +++ b/kernel/src/syscall/handlers/sys_arch.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #define ARCH_SET_FS 0x1002 #define ARCH_GET_FS 0x1003 @@ -41,6 +42,48 @@ DECLARE_SYSCALL_HANDLER(set_tid_address) { return static_cast(current->get_core()->identity.pid); } +DECLARE_SYSCALL_HANDLER(reboot) { + uint64_t magic1 = arg1; + uint64_t magic2 = arg2; + uint64_t cmd = arg3; __unused cmd; + uint64_t arg = arg4; __unused arg; + + // Linux reboot syscall magic numbers + const uint64_t LINUX_REBOOT_MAGIC1 = 0xfee1dead; + const uint64_t LINUX_REBOOT_MAGIC2 = 0x28121969; + const uint64_t LINUX_REBOOT_MAGIC2A = 0x05121996; + const uint64_t LINUX_REBOOT_MAGIC2B = 0x16041998; + const uint64_t LINUX_REBOOT_MAGIC2C = 0x20112000; + + // Check magic numbers + if (magic1 != LINUX_REBOOT_MAGIC1) { + SYSCALL_TRACE("reboot(0x%llx, 0x%llx, %llu, %llu) = -EINVAL (invalid magic1)\n", + magic1, magic2, cmd, arg); + return -EINVAL; + } + + if (magic2 != LINUX_REBOOT_MAGIC2 && + magic2 != LINUX_REBOOT_MAGIC2A && + magic2 != LINUX_REBOOT_MAGIC2B && + magic2 != LINUX_REBOOT_MAGIC2C) { + SYSCALL_TRACE("reboot(0x%llx, 0x%llx, %llu, %llu) = -EINVAL (invalid magic2)\n", + magic1, magic2, cmd, arg); + return -EINVAL; + } + + SYSCALL_TRACE("reboot(0x%llx, 0x%llx, %llu, %llu) = 0\n", magic1, magic2, cmd, arg); + + // For now, ust reboot regardless of the command + // TO-DO: we'd handle different reboot commands + kprint("[*] System reboot requested via syscall\n"); + + // Trigger ACPI reboot + acpi::fadt::get().reboot(); + + // This should never return, but just in case + return 0; +} + DECLARE_SYSCALL_HANDLER(elevate) { int original_elevate_status = static_cast(arg1); @@ -57,4 +100,4 @@ DECLARE_SYSCALL_HANDLER(elevate) { } return 0; -} \ No newline at end of file +} diff --git a/kernel/src/syscall/handlers/sys_graphics.cpp b/kernel/src/syscall/handlers/sys_graphics.cpp index d86cfe7a..33e1e478 100644 --- a/kernel/src/syscall/handlers/sys_graphics.cpp +++ b/kernel/src/syscall/handlers/sys_graphics.cpp @@ -8,11 +8,15 @@ #include #include #include +#include #include