shell.c

Commit: 04a7ec25 Author: jokerz Raw Copy
1
/* ============================================================================
2
 * Pactor64 — Interactive Shell (x86-64)
3
 * Serial-based line editor with built-in commands
4
 * Includes "run" command for executing ELF64 userspace programs
5
 * ============================================================================ */
6
#include "../include/pactor64.h"
7
#include "ata.h"
8
/* Forward declaration */
9
static void cmd_run_elf(const unsigned char *elf_data, unsigned int elf_len, const char *name);
10
#include "hello_elf.h"
11
#include "pactor64_llc_bin.h"
12
#include "raw_test_bin.h"
13
#include "raw_stack_test_bin.h"
14
#include "hello_v2_bin.h"
15
#include "test_prog_bin.h"
16
#include "app_binaries.h"
17
#include "ext2.h"
18
#include "elf.h"
19
20
/* Embedded hello.elf binary (generated by xxd) */
21
22
/* Assembly functions for userspace transitions */
23
extern void enter_userspace(uint64_t user_rip, uint64_t user_rsp);
24
extern uint64_t kernel_stack_top;
25
26
#define CMD_BUF_SIZE 256
27
28
/* === Page table constants === */
29
#define PAGE_PRESENT    0x01
30
#define PAGE_WRITE      0x02
31
#define PAGE_USER       0x04    /* U/S bit: 1 = user accessible */
32
#define PAGE_SIZE_FLAG  0x80    /* PS bit for 2MB pages */
33
34
/* User stack: 64KB at virtual address 0x10000000 (256MB, well above kernel/heap) */
35
#define USER_STACK_BASE  0x10000000
36
#define USER_STACK_SIZE  0x10000  /* 64KB */
37
#define USER_STACK_TOP   (USER_STACK_BASE + USER_STACK_SIZE)
38
39
/* ============================================================================
40
 * Page table helpers — make pages user-accessible for ring 3 execution
41
 * The kernel uses 2MB pages with identity mapping (first 1GB).
42
 * We need to set the U/S bit on PML4, PDPT, and specific PD entries.
43
 * ============================================================================ */
44
45
/* Set the U/S bit on a 2MB PD entry covering a given virtual address */
46
static void pt_make_user_accessible(uint64_t vaddr) {
47
    uint64_t cr3;
48
    __asm__ volatile ("mov %%cr3, %0" : "=r"(cr3));
49
50
    /* PML4 index: bits [47:39] of vaddr */
51
    uint64_t pml4_idx = (vaddr >> 39) & 0x1FF;
52
    /* PDPT index: bits [38:30] */
53
    uint64_t pdpt_idx = (vaddr >> 30) & 0x1FF;
54
    /* PD index: bits [29:21] */
55
    uint64_t pd_idx   = (vaddr >> 21) & 0x1FF;
56
57
    uint64_t *pml4 = (uint64_t *)(cr3 & ~0xFFFULL);
58
59
    /* Set U/S on PML4 entry */
60
    pml4[pml4_idx] |= PAGE_USER;
61
62
    /* Get PDPT and set U/S */
63
    uint64_t *pdpt = (uint64_t *)(pml4[pml4_idx] & ~0xFFFULL);
64
    pdpt[pdpt_idx] |= PAGE_USER;
65
66
    /* Get PD and set U/S on the 2MB page entry */
67
    uint64_t *pd = (uint64_t *)(pdpt[pdpt_idx] & ~0xFFFULL);
68
    pd[pd_idx] |= PAGE_USER;
69
70
    kprintf("    PD[%d] for vaddr 0x%x → flags 0x%x\n", pd_idx, vaddr, pd[pd_idx]);
71
}
72
73
/* Flush the TLB by reloading CR3 */
74
static void tlb_flush(void) {
75
    uint64_t cr3;
76
    __asm__ volatile ("mov %%cr3, %0" : "=r"(cr3));
77
    __asm__ volatile ("mov %0, %%cr3" : : "r"(cr3) : "memory");
78
}
79
80
/* === Built-in Commands === */
81
static void cmd_ata_test(void) {
82
    uint8_t buf[512];
83
    kprintf("  Reading sector 0 (MBR)...\n");
84
    if (ata_read_sectors(0, 1, buf) != 0) {
85
        kprintf("  ERROR: Failed to read sector 0\n");
86
        return;
87
    }
88
    kprintf("  First 32 bytes of sector 0:\n    ");
89
    for (int i = 0; i < 32; i++) {
90
        if (buf[i] < 0x10) serial_putc('0');
91
        kprintf("%x ", buf[i]);
92
        if ((i & 15) == 15 && i < 31) kprintf("\n    ");
93
    }
94
    kprintf("\n");
95
    kprintf("  MBR signature: 0x%x 0x%x\n", buf[510], buf[511]);
96
}
97
98
static void cmd_diskinfo(void) {
99
    if (!ata_is_present()) {
100
        kprintf("  No ATA drive detected.\n");
101
        return;
102
    }
103
    kprintf("  ATA Drive Info:\n");
104
    kprintf("    Model:    %s\n", ata_get_model());
105
    kprintf("    Serial:   %s\n", ata_get_serial());
106
    kprintf("    Sectors:  %d\n", ata_get_sector_count());
107
    kprintf("    Capacity: %d MB\n", ata_get_sector_count() * 512 / (1024 * 1024));
108
}
109
110
/* Directory listing callback for ext2 */
111
static void dir_list_cb(const char *name, uint8_t name_len,
112
                         uint32_t inode, uint8_t file_type, void *ctx) {
113
    (void)ctx;
114
    const char *type_str = "?";
115
    switch (file_type) {
116
        case EXT2_FT_REG_FILE: type_str = "F"; break;
117
        case EXT2_FT_DIR:      type_str = "D"; break;
118
        case EXT2_FT_SYMLINK:  type_str = "L"; break;
119
    }
120
    /* kprintf doesn't support %.*s, so null-terminate into a temp buffer */
121
    char name_buf[256];
122
    kmemcpy(name_buf, name, name_len);
123
    name_buf[name_len] = '\0';
124
    kprintf("    [%s] inode=%d  %s\n", type_str, inode, name_buf);
125
}
126
127
static void cmd_mount(void) {
128
    static ext2_fs_t fs;
129
    /* MBR partition 1 starts at sector 2048 (default in mkdisk.sh) */
130
    if (ext2_mount(&fs, 2048) != 0) {
131
        kprintf("  Failed to mount ext2\n");
132
        return;
133
    }
134
    kprintf("  ext2 mounted successfully!\n");
135
    kprintf("  Volume: %.16s\n", fs.sb.s_volume_name);
136
137
    /* Try to list root directory */
138
    kprintf("  Root directory:\n");
139
    ext2_readdir(&fs, EXT2_ROOT_INO, dir_list_cb, NULL);
140
}
141
142
static void cmd_help(void) {
143
    kprintf("  Available commands:\n");
144
    kprintf("    help     - Show this help\n");
145
    kprintf("    run      - Run hello.elf in userspace (ring 3)\n");
146
    kprintf("    meminfo  - Memory information\n");
147
    kprintf("    ticks    - Timer tick count\n");
148
    kprintf("    ata      - ATA drive info\n");
149
    kprintf("    ata_test - Read sector 0 (MBR)\n");
150
    kprintf("    mount    - Mount ext2 filesystem\n");
151
    kprintf("    reboot   - Reboot the system\n");
152
    kprintf("    halt     - Halt the system\n");
153
}
154
155
static void cmd_meminfo(void) {
156
    uint64_t total = pmm_get_total_pages();
157
    uint64_t free  = pmm_get_free_pages();
158
    uint64_t used  = total - free;
159
    kprintf("  Physical Memory:\n");
160
    kprintf("    Total pages: %d (%d MB)\n", total, total * 4 / 1024);
161
    kprintf("    Free pages:  %d (%d MB)\n", free,  free * 4 / 1024);
162
    kprintf("    Used pages:  %d (%d MB)\n", used,  used * 4 / 1024);
163
}
164
165
static void cmd_ticks(void) {
166
    kprintf("  Timer ticks:   %d\n", timer_get_ticks());
167
    kprintf("  Uptime (sec):  %d\n", timer_get_seconds());
168
}
169
170
static void cmd_reboot(void) {
171
    kprintf("  Rebooting...\n");
172
    /* Triple fault: load an invalid IDT then trigger an interrupt */
173
    struct idtr bad = { 0, 0 };
174
    idt_load(&bad);
175
    __asm__ volatile ("int $0x03");
176
    /* Fallback: keyboard controller reset */
177
    outb(0x64, 0xFE);
178
    for (;;) hlt();
179
}
180
181
static void cmd_halt(void) {
182
    kprintf("  System halted.\n");
183
    cli();
184
    for (;;) hlt();
185
}
186
187
/* ============================================================================
188
 * cmd_run — Load and execute hello.elf in userspace (ring 3)
189
 *
190
 * Steps:
191
 *   1. Parse and load ELF segments
192
 *   2. Set up page table U/S bits for user code + stack
193
 *   3. Zero user stack pages
194
 *   4. Enter ring 3 via IRETQ
195
 *   5. When program calls exit(), return_to_kernel() brings us back
196
 * ============================================================================ */
197
static void cmd_run(void) { cmd_run_elf(hello_elf_data, hello_elf_data_len, "hello"); }
198
static void cmd_run_elf(const unsigned char *elf_data, unsigned int elf_len, const char *name) {
199
    kprintf("  Loading ELF64 binary: %s\n", name);
200
201
    /* Step 1: Load ELF */
202
    elf64_load_result_t result = elf64_load(elf_data, elf_len);
203
    if (!result.valid) {
204
        kprintf("  ERROR: Failed to load ELF\n");
205
        return;
206
    }
207
208
    kprintf("  Entry: 0x%x, Load: 0x%x-0x%x\n",
209
            result.entry, result.load_base, result.load_end);
210
211
    /* Step 2: Set up page table U/S bits for user pages */
212
    kprintf("  Setting up page tables for ring 3...\n");
213
214
    /* Make code pages user-accessible.
215
     * The ELF loads at 0x400000 which is in PD[2] (covers 4MB-6MB). */
216
    pt_make_user_accessible(result.load_base);
217
218
    /* Also make pages around the code user-accessible (in case it spans 2MB boundaries) */
219
    if (result.load_end > (result.load_base & ~0x1FFFFF) + 0x200000) {
220
        pt_make_user_accessible(result.load_end);
221
    }
222
223
    /* Make user stack pages user-accessible.
224
     * Stack at 0x10000000 is in PD[128] (covers 256MB-258MB). */
225
    pt_make_user_accessible(USER_STACK_BASE);
226
227
    /* Flush TLB so page table changes take effect */
228
    tlb_flush();
229
230
    /* Step 3: Zero user stack area */
231
    kmemset((void *)USER_STACK_BASE, 0, USER_STACK_SIZE);
232
    kprintf("  User stack: 0x%x - 0x%x (64KB)\n",
233
            USER_STACK_BASE, USER_STACK_TOP);
234
235
    /* Step 4: Update TSS RSP0 for SYSCALL entry from user mode */
236
    /* (Already set in entry.asm, but update to be safe) */
237
    extern char tss_struct[];
238
    *(uint64_t *)(tss_struct + 4) = (uint64_t)&kernel_stack_top;
239
240
    kprintf("  Entering userspace at RIP=0x%x RSP=0x%x...\n",
241
            result.entry, USER_STACK_TOP);
242
    kprintf("  ----------------------------------------\n");
243
244
    /* Step 5: Jump to ring 3!
245
     * enter_userspace saves kernel RSP and does IRETQ.
246
     * When the program calls exit(), return_to_kernel() will
247
     * restore the kernel stack and we'll return here. */
248
    outb(0x21, 0xFD);  /* Mask timer during userspace */
249
    enter_userspace(result.entry, USER_STACK_TOP);
250
    /* DEBUG: serial marker to confirm return */
251
252
    /* We get here when return_to_kernel() is called from sys_exit */
253
    kprintf("\n  ----------------------------------------\n");
254
    kprintf("  Returned to kernel shell.\n");
255
    outb(0x21, 0xFC);  /* Re-enable timer */
256
}
257
258
/* === Execute a command === */
259
static void execute_command(const char *cmd) {
260
    while (*cmd == ' ') cmd++;
261
    if (*cmd == '\0') return;
262
263
    if (kstrncmp(cmd, "execute ", 8) == 0) {
264
        cmd_execute(cmd + 8);
265
    } else if (kstrcmp(cmd, "apps") == 0) {
266
        cmd_apps();
267
    } else if (kstrcmp(cmd, "help") == 0) {
268
        cmd_help();
269
    } else if (kstrcmp(cmd, "run") == 0) {
270
        cmd_run();
271
    } else if (kstrcmp(cmd, "meminfo") == 0) {
272
        cmd_meminfo();
273
    } else if (kstrcmp(cmd, "ticks") == 0) {
274
        cmd_ticks();
275
    } else if (kstrcmp(cmd, "ata") == 0) {
276
        cmd_diskinfo();
277
    } else if (kstrcmp(cmd, "ata_test") == 0) {
278
        cmd_ata_test();
279
    } else if (kstrcmp(cmd, "mount") == 0) {
280
        cmd_mount();
281
    } else if (kstrcmp(cmd, "reboot") == 0) {
282
        cmd_reboot();
283
    } else if (kstrcmp(cmd, "halt") == 0) {
284
        cmd_halt();
285
    } else {
286
        kprintf("  Unknown: '%s'. Type 'apps' for applications.\n", cmd);
287
    }
288
}
289
290
/* === Initialize shell === */
291
void shell_init(void) {
292
    kprintf("\n  ========================================\n");
293
    kprintf("    Pactor64 Shell Ready\n");
294
    kprintf("    Type 'help' for available commands\n");
295
    kprintf("    Type 'run' to execute hello.elf\n");
296
    kprintf("  ========================================\n\n");
297
}
298
299
/* === Run the shell (main loop) === */
300
void shell_run(void) {
301
    char buf[CMD_BUF_SIZE];
302
    int  pos = 0;
303
304
    kprintf("pactor64> ");
305
306
    for (;;) {
307
        /* Poll serial for input */
308
        int c = serial_getc();
309
        if (c < 0) {
310
            /* Also check keyboard (PS/2) */
311
            c = keyboard_getchar();
312
            if (c < 0) {
313
                __asm__ volatile ("hlt");
314
                continue;
315
            }
316
        }
317
318
        char ch = (char)c;
319
320
        if (ch == '\n' || ch == '\r') {
321
            /* Enter: execute command */
322
            serial_puts("\r\n");
323
            buf[pos] = '\0';
324
            execute_command(buf);
325
            pos = 0;
326
            kprintf("pactor64> ");
327
        } else if (ch == '\b' || ch == 127) {
328
            /* Backspace */
329
            if (pos > 0) {
330
                pos--;
331
                serial_puts("\b \b");  /* erase character on terminal */
332
            }
333
        } else if (ch >= ' ' && ch < 127) {
334
            /* Printable character */
335
            if (pos < CMD_BUF_SIZE - 1) {
336
                buf[pos++] = ch;
337
                serial_putc(ch);  /* echo */
338
            }
339
        }
340
        /* Ignore other control characters */
341
    }
342
}
343