:: commit 1166de64c86c519d15e42d657cfed3f29b0def8e

mintsuki <mintsuki@protonmail.com> — 2020-03-26 02:13

parents: 26bccc6308

Add initial VBE driver

diff --git a/src/drivers/vbe.c b/src/drivers/vbe.c
new file mode 100644
index 00000000..100b3184
--- /dev/null
+++ b/src/drivers/vbe.c
@@ -0,0 +1,157 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <drivers/vbe.h>
+#include <lib/blib.h>
+#include <lib/real.h>
+
+struct vbe_info_struct {
+    char     signature[4];
+    uint8_t  version_min;
+    uint8_t  version_maj;
+    uint16_t oem_off;
+    uint16_t oem_seg;
+    uint32_t capabilities;
+    uint16_t vid_modes_off;
+    uint16_t vid_modes_seg;
+    uint16_t vid_mem_blocks;
+    uint16_t software_rev;
+    uint16_t vendor_off;
+    uint16_t vendor_seg;
+    uint16_t prod_name_off;
+    uint16_t prod_name_seg;
+    uint16_t prod_rev_off;
+    uint16_t prod_rev_seg;
+    uint8_t  reserved[222];
+    uint8_t  oem_data[256];
+} __attribute__((packed));
+
+struct vbe_mode_info_struct {
+    uint8_t pad0[16];
+    uint16_t pitch;
+    uint16_t res_x;
+    uint16_t res_y;
+    uint8_t pad1[3];
+    uint8_t bpp;
+    uint8_t pad2[14];
+    uint32_t framebuffer;
+    uint8_t pad3[212];
+} __attribute__((packed));
+
+static void get_vbe_info(struct vbe_info_struct *buf) {
+    struct rm_regs r = {0};
+
+    r.eax = 0x4f00;
+    r.edi = (uint32_t)buf;
+    rm_int(0x10, &r, &r);
+}
+
+static void get_vbe_mode_info(struct vbe_mode_info_struct *buf,
+                              uint16_t mode) {
+    struct rm_regs r = {0};
+
+    r.eax = 0x4f01;
+    r.ecx = (uint32_t)mode;
+    r.edi = (uint32_t)buf;
+    rm_int(0x10, &r, &r);
+}
+
+static void set_vbe_mode(uint16_t mode) {
+    struct rm_regs r = {0};
+
+    r.eax = 0x4f02;
+    r.ebx = (uint32_t)mode | (1 << 14);
+    rm_int(0x10, &r, &r);
+}
+
+struct edid_info_struct {
+    uint8_t padding[8];
+    uint16_t manufacturer_id_be;
+    uint16_t edid_id_code;
+    uint32_t serial_num;
+    uint8_t man_week;
+    uint8_t man_year;
+    uint8_t edid_version;
+    uint8_t edid_revision;
+    uint8_t video_input_type;
+    uint8_t max_hor_size;
+    uint8_t max_ver_size;
+    uint8_t gamma_factor;
+    uint8_t dpms_flags;
+    uint8_t chroma_info[10];
+    uint8_t est_timings1;
+    uint8_t est_timings2;
+    uint8_t man_res_timing;
+    uint16_t std_timing_id[8];
+    uint8_t det_timing_desc1[18];
+    uint8_t det_timing_desc2[18];
+    uint8_t det_timing_desc3[18];
+    uint8_t det_timing_desc4[18];
+    uint8_t unused;
+    uint8_t checksum;
+} __attribute__((packed));
+
+static int get_edid_info(struct edid_info_struct *buf) {
+    struct rm_regs r = {0};
+
+    r.eax = 0x4f15;
+    r.ebx = 0x0001;
+    r.edi = (uint32_t)buf;
+    rm_int(0x10, &r, &r);
+
+    if ((r.eax & 0x00ff) == 0x4f)
+        return -1;
+    if ((r.eax & 0xff00) != 0)
+        return -1;
+
+    return 0;
+}
+
+int init_vbe(uint64_t *framebuffer, uint16_t *pitch, uint16_t *target_width, uint16_t *target_height) {
+    print("vbe: Initialising...\n");
+
+    struct vbe_info_struct vbe_info;
+    get_vbe_info(&vbe_info);
+
+    print("vbe: Version: %u.%u\n", vbe_info.version_maj, vbe_info.version_min);
+    print("vbe: OEM: %s\n", (char *)rm_desegment(vbe_info.oem_seg, vbe_info.oem_off));
+    print("vbe: Graphics vendor: %s\n", (char *)rm_desegment(vbe_info.vendor_seg, vbe_info.vendor_off));
+    print("vbe: Product name: %s\n", (char *)rm_desegment(vbe_info.prod_name_seg, vbe_info.prod_name_off));
+    print("vbe: Product revision: %s\n", (char *)rm_desegment(vbe_info.prod_rev_seg, vbe_info.prod_rev_off));
+
+    struct edid_info_struct edid_info;
+    if (!*target_width || !*target_height) {
+        if (get_edid_info(&edid_info)) {
+            print("vbe: EDID unavailable, defaulting to 1024x768\n");
+            *target_width  = 1024;
+            *target_height = 768;
+        } else {
+            print("vbe: EDID detected screen resolution of %ux%u\n");
+            *target_width   = (int)edid_info.det_timing_desc1[2];
+            *target_width  += ((int)edid_info.det_timing_desc1[4] & 0xf0) << 4;
+            *target_height  = (int)edid_info.det_timing_desc1[5];
+            *target_height += ((int)edid_info.det_timing_desc1[7] & 0xf0) << 4;
+        }
+    } else {
+        print("vbe: Requested resolution of %ux%u\n", *target_width, *target_height);
+    }
+
+    uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
+                                                   vbe_info.vid_modes_off);
+
+    for (size_t i = 0; vid_modes[i] != 0xffff; i++) {
+        struct vbe_mode_info_struct vbe_mode_info;
+        get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
+        if  (vbe_mode_info.res_x == *target_width
+          && vbe_mode_info.res_y == *target_height
+          /*&& vbe_mode_info.bpp   == *target_bpp*/) {
+            print("vbe: Found matching mode %x, attempting to set\n", vid_modes[i]);
+            *framebuffer = (uint64_t)vbe_mode_info.framebuffer;
+            *pitch       = (int)vbe_mode_info.pitch;
+            print("vbe: Framebuffer address: %x\n", vbe_mode_info.framebuffer);
+            set_vbe_mode(vid_modes[i]);
+            return 0;
+        }
+    }
+
+    return -1;
+}
diff --git a/src/drivers/vbe.h b/src/drivers/vbe.h
new file mode 100644
index 00000000..d7765595
--- /dev/null
+++ b/src/drivers/vbe.h
@@ -0,0 +1,6 @@
+#ifndef __DRIVERS__VBE_H__
+#define __DRIVERS__VBE_H__
+
+int init_vbe(uint64_t *framebuffer, uint16_t *pitch, uint16_t *target_width, uint16_t *target_height);
+
+#endif
diff --git a/src/lib/real.h b/src/lib/real.h
index 6dcd37af..d577b3c8 100644
--- a/src/lib/real.h
+++ b/src/lib/real.h
@@ -1,11 +1,13 @@
-#ifndef __REAL_H__
-#define __REAL_H__
+#ifndef __LIB__REAL_H__
+#define __LIB__REAL_H__
 
 #include <stdint.h>
 
 #define rm_seg(x) (unsigned short)(((int)x & 0xFFFF0) >> 4)
 #define rm_off(x) (unsigned short)(((int)x & 0x0000F) >> 0)
 
+#define rm_desegment(seg, off) (((uint32_t)(seg) << 4) + (uint32_t)(off))
+
 #define EFLAGS_CF (1 << 0)
 
 struct rm_regs {
diff --git a/src/protos/stivale.c b/src/protos/stivale.c
index e11baaeb..350744f4 100644
--- a/src/protos/stivale.c
+++ b/src/protos/stivale.c
@@ -4,10 +4,13 @@
 #include <lib/elf.h>
 #include <lib/blib.h>
 #include <lib/acpi.h>
+#include <drivers/vbe.h>
 
 struct stivale_header {
     uint64_t stack;
     uint8_t  video_mode;  // 0 = default at boot (CGA text mode). 1 = graphical VESA
+    uint16_t framebuffer_width;
+    uint16_t framebuffer_height;
 } __attribute__((packed));
 
 struct stivale_module {
@@ -59,6 +62,16 @@ void stivale_load(struct echfs_file_handle *fd) {
     stivale_struct.rsdp = (uint64_t)(size_t)get_rsdp();
     print("stivale: RSDP at %X\n", stivale_struct.rsdp);
 
+    stivale_struct.framebuffer_width  = stivale_hdr.framebuffer_width;
+    stivale_struct.framebuffer_height = stivale_hdr.framebuffer_height;
+
+    if (stivale_hdr.video_mode == 1) {
+        init_vbe(&stivale_struct.framebuffer_addr,
+                 &stivale_struct.framebuffer_pitch,
+                 &stivale_struct.framebuffer_width,
+                 &stivale_struct.framebuffer_height);
+    }
+
     volatile struct {
         uint64_t pml4[512];
         uint64_t pml3_lo[512];
diff --git a/test/test.asm b/test/test.asm
index f3785f0d..283d5bf9 100644
--- a/test/test.asm
+++ b/test/test.asm
@@ -4,8 +4,10 @@
 section .stivalehdr
 
 stivale_header:
-    dq stack.top
-    db 0
+    dq stack.top    ; rsp
+    db 1            ; video mode
+    dw 640          ; fb_width
+    dw 480          ; fb_height
 
 section .bss
 
tab: 248 wrap: offon