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
