Browse Source

More experiments with loading Linux.

Giovanni Mascellani 5 years ago
parent
commit
cf89ff6192
3 changed files with 218 additions and 95 deletions
  1. 1 1
      contrib/ipxe
  2. 151 94
      http/continue2.c
  3. 66 0
      http/realmode.asm

+ 1 - 1
contrib/ipxe

@@ -1 +1 @@
-Subproject commit 279992a8f9a23ddb2db5bcb9000c30874e4877a0
+Subproject commit 4cd83cd88399b033b7ccd7b98a0e3d60a3f1df97

+ 151 - 94
http/continue2.c

@@ -5,97 +5,6 @@
 #include <string.h>
 #include <asmc_api.h>
 
-struct gdt_entry {
-    unsigned int limit_low : 16;
-    unsigned int base_low : 24;
-    // Access byte
-    unsigned int accessed : 1;
-    unsigned int read_write : 1; // Readable for code, writable for data
-    unsigned int conforming : 1; // Conforming for code, expand down for data
-    unsigned int code : 1; // 1 for code, 0 for data
-    unsigned int type : 1; // 1 for code/data, 0 for system segments (like TSS)
-    unsigned int DPL : 2; //priviledge level
-    unsigned int present : 1;
-    // Flags
-    unsigned int limit_high : 4;
-    unsigned int always_0 : 2;
-    unsigned int size : 1; // 1 for 32 bits, 0 for 16 bits
-    unsigned int gran : 1; // 1 for 4k page addressing, 0 for byte addressing
-    unsigned int base_high : 8;
-} __attribute__((packed));
-
-struct gdt_desc {
-    uint16_t size;
-    struct gdt_entry *offset;
-} __attribute__((packed));
-
-void reload_gdt(struct gdt_entry *gdt, int entry_num) {
-    struct gdt_desc desc;
-    desc.size = entry_num * 8 - 1;
-    desc.offset = gdt;
-    asm volatile("lgdt %0 \n\t"
-                 "push $0x10 \n\t"
-                 "push $reload_cs \n\t"
-                 // TCC does not know retf
-                 ".byte 0xcb \n\t"
-                 "reload_cs: \n\t"
-                 "mov $0x18, %%ax \n\t"
-                 // TCC generates a 0x66 prefix that does not look like right
-                 /*"mov %%ax, %%ds \n\t"
-                 "mov %%ax, %%es \n\t"
-                 "mov %%ax, %%fs \n\t"
-                 "mov %%ax, %%gs \n\t"
-                 "mov %%ax, %%ss \n\t"*/
-                 ".byte 0x8e, 0xd8 \n\t"
-                 ".byte 0x8e, 0xc0 \n\t"
-                 ".byte 0x8e, 0xe0 \n\t"
-                 ".byte 0x8e, 0xe8 \n\t"
-                 ".byte 0x8e, 0xd0 \n\t"
-                 : : "m" (desc));
-    printf("Still alive after reloading code and data segments!\n");
-}
-
-// Load GDT, code and data segments according to Linux Boot Protocol
-void load_linux_gdt() {
-    assert(sizeof(struct gdt_desc) == 6);
-    assert(sizeof(struct gdt_entry) == 8);
-    // Random address, hoping that it does not bother anybody
-    struct gdt_entry *gdt = (struct gdt_entry*) 0x800;
-    memset(gdt, 0, sizeof(gdt[0]) * 4);
-
-    // 0x10: code segment
-    gdt[2].limit_low = 0xffff;
-    gdt[2].base_low = 0x000000;
-    gdt[2].accessed = 0;
-    gdt[2].read_write = 1;
-    gdt[2].conforming = 0;
-    gdt[2].code = 1;
-    gdt[2].type = 1;
-    gdt[2].DPL = 0;
-    gdt[2].present = 1;
-    gdt[2].limit_high = 0xf;
-    gdt[2].size = 1;
-    gdt[2].gran = 1;
-    gdt[2].base_high = 0x00;
-
-    // 0x18: data segment
-    gdt[3].limit_low = 0xffff;
-    gdt[3].base_low = 0x000000;
-    gdt[3].accessed = 0;
-    gdt[3].read_write = 1;
-    gdt[3].conforming = 0;
-    gdt[3].code = 0;
-    gdt[3].type = 1;
-    gdt[3].DPL = 0;
-    gdt[3].present = 1;
-    gdt[3].limit_high = 0xf;
-    gdt[3].size = 1;
-    gdt[3].gran = 1;
-    gdt[3].base_high = 0x00;
-
-    reload_gdt(gdt, 4);
-}
-
 typedef uint8_t __u8;
 typedef uint16_t __u16;
 typedef uint32_t __u32;
@@ -429,6 +338,136 @@ struct boot_params {
 	__u8  _pad9[276];				/* 0xeec */
 } __attribute__((packed));
 
+struct gdt_entry {
+    unsigned int limit_low : 16;
+    unsigned int base_low : 24;
+    // Access byte
+    unsigned int accessed : 1;
+    unsigned int read_write : 1; // Readable for code, writable for data
+    unsigned int conforming : 1; // Conforming for code, expand down for data
+    unsigned int code : 1; // 1 for code, 0 for data
+    unsigned int type : 1; // 1 for code/data, 0 for system segments (like TSS)
+    unsigned int DPL : 2; //priviledge level
+    unsigned int present : 1;
+    // Flags
+    unsigned int limit_high : 4;
+    unsigned int always_0 : 2;
+    unsigned int size : 1; // 1 for 32 bits, 0 for 16 bits
+    unsigned int gran : 1; // 1 for 4k page addressing, 0 for byte addressing
+    unsigned int base_high : 8;
+} __attribute__((packed));
+
+struct gdt_desc {
+    uint16_t size;
+    struct gdt_entry *offset;
+} __attribute__((packed));
+
+void reload_gdt(struct gdt_entry *gdt, int entry_num) {
+    struct gdt_desc desc;
+    desc.size = entry_num * 8 - 1;
+    desc.offset = gdt;
+    asm volatile("lgdt %0 \n\t"
+                 "push $0x10 \n\t"
+                 "push $reload_cs \n\t"
+                 // TCC does not know retf
+                 ".byte 0xcb \n\t"
+                 "reload_cs: \n\t"
+                 "mov $0x18, %%ax \n\t"
+                 // TCC generates a 0x66 prefix that does not look like right
+                 /*"mov %%ax, %%ds \n\t"
+                 "mov %%ax, %%es \n\t"
+                 "mov %%ax, %%fs \n\t"
+                 "mov %%ax, %%gs \n\t"
+                 "mov %%ax, %%ss \n\t"*/
+                 ".byte 0x8e, 0xd8 \n\t"
+                 ".byte 0x8e, 0xc0 \n\t"
+                 ".byte 0x8e, 0xe0 \n\t"
+                 ".byte 0x8e, 0xe8 \n\t"
+                 ".byte 0x8e, 0xd0 \n\t"
+                 : : "m" (desc));
+    printf("Still alive after reloading code and data segments!\n");
+}
+
+// Load GDT, code and data segments according to Linux Boot Protocol
+void load_linux_gdt() {
+    assert(sizeof(struct gdt_desc) == 6);
+    assert(sizeof(struct gdt_entry) == 8);
+    // Random address, hoping that it does not bother anybody
+    struct gdt_entry *gdt = (struct gdt_entry*) 0x800;
+    memset(gdt, 0, sizeof(gdt[0]) * 4);
+
+    // 0x10: code segment
+    gdt[2].limit_low = 0xffff;
+    gdt[2].base_low = 0x000000;
+    gdt[2].accessed = 0;
+    gdt[2].read_write = 1;
+    gdt[2].conforming = 0;
+    gdt[2].code = 1;
+    gdt[2].type = 1;
+    gdt[2].DPL = 0;
+    gdt[2].present = 1;
+    gdt[2].limit_high = 0xf;
+    gdt[2].size = 1;
+    gdt[2].gran = 1;
+    gdt[2].base_high = 0x00;
+
+    // 0x18: data segment
+    gdt[3].limit_low = 0xffff;
+    gdt[3].base_low = 0x000000;
+    gdt[3].accessed = 0;
+    gdt[3].read_write = 1;
+    gdt[3].conforming = 0;
+    gdt[3].code = 0;
+    gdt[3].type = 1;
+    gdt[3].DPL = 0;
+    gdt[3].present = 1;
+    gdt[3].limit_high = 0xf;
+    gdt[3].size = 1;
+    gdt[3].gran = 1;
+    gdt[3].base_high = 0x00;
+
+    reload_gdt(gdt, 4);
+}
+
+// Prepare GDT to re-enter real mode
+void prepare_real_mode_gdt() {
+    assert(sizeof(struct gdt_desc) == 6);
+    assert(sizeof(struct gdt_entry) == 8);
+    // Random address, hoping that it does not bother anybody
+    struct gdt_entry *gdt = (struct gdt_entry*) 0x800;
+    memset(gdt, 0, sizeof(gdt[0]) * 4);
+
+    // 0x10: code segment
+    gdt[2].limit_low = 0xffff;
+    gdt[2].base_low = 0x000000;
+    gdt[2].accessed = 0;
+    gdt[2].read_write = 1;
+    gdt[2].conforming = 0;
+    gdt[2].code = 1;
+    gdt[2].type = 1;
+    gdt[2].DPL = 0;
+    gdt[2].present = 1;
+    gdt[2].limit_high = 0x0;
+    gdt[2].size = 0;
+    gdt[2].gran = 0;
+    gdt[2].base_high = 0x00;
+
+    // 0x18: data segment
+    gdt[3].limit_low = 0xffff;
+    gdt[3].base_low = 0x000000;
+    gdt[3].accessed = 0;
+    gdt[3].read_write = 1;
+    gdt[3].conforming = 0;
+    gdt[3].code = 0;
+    gdt[3].type = 1;
+    gdt[3].DPL = 0;
+    gdt[3].present = 1;
+    gdt[3].limit_high = 0x0;
+    gdt[3].size = 0;
+    gdt[3].gran = 0;
+    gdt[3].base_high = 0x00;
+}
+
 void *read_all_file(FILE *fin, size_t **size) {
     size_t buf_size = 16;
     char *buf = malloc(buf_size);
@@ -459,14 +498,15 @@ void prepare_boot_params(struct boot_params *real_mode) {
     hdr->loadflags = 0x81;
     hdr->ramdisk_image = 0;
     hdr->ramdisk_size = 0;
-    hdr->heap_end_ptr = 0;  // FIXME
-    hdr->cmd_line_ptr = 0;  // FIXME
+    hdr->heap_end_ptr = 0xde00;
+    hdr->cmd_line_ptr = 0x1e000;
 }
 
 #define SECT_SIZE 512
 
 void load_linux() {
     download_file("http://10.0.2.2:8080/bzImage", "/ram/cont/bzImage");
+    download_file("http://10.0.2.2:8080/realmode.bin", "/ram/cont/realmode.bin");
     printf("Loading Linux kernel\n");
     FILE *fin = fopen("/ram/cont/bzImage", "rb");
     struct boot_params *real_mode = malloc(SECT_SIZE);
@@ -487,7 +527,24 @@ void load_linux() {
     printf("Protected mode code/data is 0x%x bytes long\n", prot_mode_size);
     fclose(fin);
     prepare_boot_params(real_mode);
-    load_linux_gdt();
+    //load_linux_gdt();
+    fin = fopen("/ram/cont/realmode.bin", "rb");
+    size_t trampoline_size;
+    void *trampoline = read_all_file(fin, &trampoline_size);
+    printf("Trampoline is 0x%x bytes long\n", trampoline_size);
+    fclose(fin);
+    prepare_real_mode_gdt();
+    char *cmdline = "verbose";
+
+    // Here we really smash our operating system!
+    memset(0x10000, 0, 0x10000);
+    memcpy((void*) 0x500, trampoline, trampoline_size);
+    memcpy((void*) 0x10000, real_mode, real_mode_size);
+    memcpy((void*) 0x1e000, cmdline, strlen(cmdline));
+    memcpy((void*) 0x100000, prot_mode, prot_mode_size);
+    ((void (*)()) 0x500)();
+
+    free(trampoline);
     free(real_mode);
     free(prot_mode);
 }

+ 66 - 0
http/realmode.asm

@@ -0,0 +1,66 @@
+
+  ;; Inspired to https://wiki.osdev.org/Real_Mode
+
+  org 0x500
+
+  bits 32
+  ;; Interrupts should already be disabled, but who knows...
+  cli
+
+  ;; Load 16 bit GDT prepared by C code
+  lgdt [real_gdt]
+  jmp 0x10:disable_real_mode
+
+  bits 16
+disable_real_mode:
+  ;; Load 16 bit data segments
+  mov ax, 0x18
+  mov ds, ax
+  mov es, ax
+  mov fs, ax
+  mov gs, ax
+  mov ss, ax
+
+  ;; Disable protected mode (assume that paging is already disabled)
+  mov eax, cr0
+  and eax, 0xfffffffe
+  mov cr0, eax
+  jmp 0x0:real_mode
+
+real_mode:
+  ;; Reload segments and stack pointer
+  mov ax, 0
+  mov ds, ax
+  mov es, ax
+  mov fs, ax
+  mov gs, ax
+  mov ss, ax
+  mov sp, 0xe000
+
+  ;; Reload IDT
+  lidt [real_idt]
+
+  ;; Now load segments for Linux booting
+  mov ax, 0x1000
+  mov ds, ax
+  mov es, ax
+  mov fs, ax
+  mov gs, ax
+  mov ss, ax
+  mov sp, 0xe000  
+
+  ;; Boot Linux
+  jmp 0x1020:0x0000
+
+  align 4
+
+  ;; IDT data
+real_idt:
+  dw 0x3ff
+  dd 0x00000000
+
+  align 4
+
+real_gdt:
+  dw 31
+  dd 0x00000800