#define HAL_UART_H
void hal_uart_init(void);
+int hal_uart_is_present(void);
void hal_uart_drain_rx(void);
void hal_uart_poll_rx(void);
void hal_uart_putc(char c);
} else {
// If Exception (0-31), Panic
if (regs->int_no < 32) {
+ // User-mode exceptions (ring 3): deliver SIGSEGV instead of panicking.
+ // Handles #GP (13), #PF (14), and other faults from user code.
+ if ((regs->cs & 3U) == 3U && regs->int_no != 14) {
+ const int SIG_SEGV = 11;
+ if (current_process) {
+ current_process->last_fault_addr = regs->eip;
+ current_process->sig_pending_mask |= (1U << (uint32_t)SIG_SEGV);
+ }
+ deliver_signals_to_usermode(regs);
+ return;
+ }
+
if (regs->int_no == 14) {
// If page fault came from ring3, convert it into a SIGSEGV delivery.
// Default action for SIGSEGV will terminate the process, but a user
lapic_write(LAPIC_TIMER_DCR, LAPIC_TIMER_DIV_16);
lapic_write(LAPIC_TIMER_ICR, ticks_per_interrupt);
- kprintf("[LAPIC] Timer started at %uHz (ticks=0x%x)\n",
+ kprintf("[LAPIC] Timer started at %uHz (ticks=%u)\n",
(unsigned)frequency_hz, ticks_per_interrupt);
}
__asm__ volatile(
"cli\n"
+ "mov $0x23, %%ax\n" /* user data segment (GDT entry 4, RPL=3) */
+ "mov %%ax, %%ds\n"
+ "mov %%ax, %%es\n"
+ "mov %%ax, %%fs\n"
+ "mov %%ax, %%gs\n"
"pushl $0x23\n" /* ss */
"pushl %[esp]\n" /* esp */
"pushl $0x202\n" /* eflags: IF=1 */
"iret\n"
:
: [eip] "r"(user_eip), [esp] "r"(user_esp)
- : "memory"
+ : "memory", "eax"
);
__builtin_unreachable();
void hal_uart_init(void) {
}
+int hal_uart_is_present(void) {
+ return 1;
+}
+
void hal_uart_drain_rx(void) {
}
/* Minimal init: assume firmware/QEMU defaults are usable */
}
+int hal_uart_is_present(void) {
+ return 1;
+}
+
void hal_uart_drain_rx(void) {
while (mmio_read8(UART_BASE + 5) & 0x01)
(void)mmio_read8(UART_BASE);
mmio_write8(UART_BASE + 1, 0x01);
}
+int hal_uart_is_present(void) {
+ return 1;
+}
+
void hal_uart_drain_rx(void) {
while (mmio_read8(UART_BASE + 5) & 0x01)
(void)mmio_read8(UART_BASE);
#define UART_BASE 0x3F8
+static int uart_present = 0;
static void (*uart_rx_cb)(char) = 0;
static void uart_irq_handler(struct registers* regs) {
}
void hal_uart_init(void) {
+ /* Detect UART hardware via scratch register (offset 7).
+ * Write a test value, read it back. If no 16550 is present the
+ * floating ISA bus returns 0xFF for all reads, so the test fails. */
+ outb(UART_BASE + 7, 0xA5);
+ if (inb(UART_BASE + 7) != 0xA5) {
+ uart_present = 0;
+ return; /* No UART — skip all configuration */
+ }
+ outb(UART_BASE + 7, 0x5A);
+ if (inb(UART_BASE + 7) != 0x5A) {
+ uart_present = 0;
+ return;
+ }
+ uart_present = 1;
+
outb(UART_BASE + 1, 0x00); /* Disable all interrupts */
outb(UART_BASE + 3, 0x80); /* Enable DLAB */
outb(UART_BASE + 0, 0x03); /* Baud 38400 */
outb(UART_BASE + 1, 0x01);
}
+int hal_uart_is_present(void) {
+ return uart_present;
+}
+
void hal_uart_drain_rx(void) {
+ if (!uart_present) return;
/* Full UART interrupt reinitialisation for IOAPIC hand-off.
*
* hal_uart_init() runs under the legacy PIC and enables IER bit 0
}
void hal_uart_poll_rx(void) {
+ if (!uart_present) return;
/* Timer-driven fallback: drain any pending characters from the
* UART FIFO via polling. Called from the timer tick handler so
* serial input works even if the IOAPIC edge-triggered IRQ for
}
void hal_uart_putc(char c) {
+ if (!uart_present) return;
int timeout = 100000;
while ((inb(UART_BASE + 5) & 0x20) == 0 && --timeout > 0) { }
outb(UART_BASE, (uint8_t)c);
}
int hal_uart_try_getc(void) {
+ if (!uart_present) return -1;
if (inb(UART_BASE + 5) & 0x01) {
return (int)inb(UART_BASE);
}
void console_init(void) {
spinlock_init(&g_console_lock);
g_console_uart_enabled = 1;
- g_console_vga_enabled = 0;
+ /* If no UART hardware is present, auto-enable VGA so boot messages
+ * are visible instead of being silently dropped. */
+ if (!hal_uart_is_present()) {
+ g_console_vga_enabled = 1;
+ }
}
void console_enable_uart(int enabled) {
(void)sys_sigaction(SIGALRM, alrm_handler, 0);
got_alrm = 0;
(void)sys_alarm(1);
- for (volatile uint32_t i = 0; i < 20000000U && !got_alrm; i++) { }
+ /* Wait up to 2 seconds for the alarm to fire. A busy-loop may
+ * complete too quickly on fast CPUs (e.g. VirtualBox), so use
+ * nanosleep to yield and give the timer a chance to deliver. */
+ for (int _w = 0; _w < 40 && !got_alrm; _w++) {
+ struct timespec _ts = {0, 50000000}; /* 50ms */
+ (void)sys_nanosleep(&_ts, 0);
+ }
if (!got_alrm) {
sys_write(1, "[init] alarm/SIGALRM not delivered\n", (uint32_t)(sizeof("[init] alarm/SIGALRM not delivered\n") - 1));
sys_exit(1);