#include #include #include #include #include #include #include #include #include "fbcon.h" #define VGA_PORT_CMD 0x3D4 #define VGA_PORT_DATA 0x3D5 #define VGA_CHAR(ch, attrib) ((uint16_t)(ch) | ((uint16_t)(attrib) << 8)) #define DEFAULT_ATTRIB 0x07 static uint16_t *g_console_fb = (uint16_t *)(VM_KERNEL_VOFFSET + 0xb8000); static const unsigned int k_console_width = 80; static const unsigned int k_console_height = 25; static unsigned int g_console_cursor_xpos = 0; static unsigned int g_console_cursor_ypos = 0; static void init_vga_cursor(void) { unsigned int start = 0, end = 15; outportb(VGA_PORT_CMD, 0x0A); outportb(VGA_PORT_DATA, (inportb(VGA_PORT_DATA) & 0xC0) | start); outportb(VGA_PORT_CMD, 0x0B); outportb(VGA_PORT_DATA, (inportb(VGA_PORT_DATA) & 0xE0) | end); } static void move_vga_cursor(unsigned int x, unsigned int y) { unsigned int offset = y * k_console_width + x; outportb(VGA_PORT_CMD, 0x0F); outportb(VGA_PORT_DATA, (uint8_t)(offset & 0xFF)); outportb(VGA_PORT_CMD, 0x0E); outportb(VGA_PORT_DATA, (uint8_t)((offset >> 8) & 0xFF)); } static void scroll_display(void) { uint16_t *src = g_console_fb + k_console_width; uint16_t *dst = g_console_fb; size_t n = k_console_width * (k_console_height - 1) * 2; memmove(dst, src, n); dst = g_console_fb + ((k_console_height - 1) * k_console_width); for (int i = 0; i < k_console_width; i++) { dst[i] = VGA_CHAR(0, DEFAULT_ATTRIB); } } static void handle_ctrl(int c) { switch (c) { case '\n': g_console_cursor_xpos = 0; g_console_cursor_ypos++; if (g_console_cursor_ypos >= k_console_height) { scroll_display(); g_console_cursor_ypos = k_console_height - 1; } break; default: break; } move_vga_cursor(g_console_cursor_xpos, g_console_cursor_ypos); } static void vgacon_putchar(int c) { if (iscntrl(c)) { handle_ctrl(c); return; } g_console_fb[(g_console_cursor_ypos * k_console_width) + g_console_cursor_xpos] = VGA_CHAR(c, DEFAULT_ATTRIB); g_console_cursor_xpos++; if (g_console_cursor_xpos >= k_console_width) { g_console_cursor_xpos = 0; g_console_cursor_ypos++; } if (g_console_cursor_ypos >= k_console_height) { scroll_display(); g_console_cursor_ypos = k_console_height - 1; g_console_cursor_xpos = 0; } move_vga_cursor(g_console_cursor_xpos, g_console_cursor_ypos); } static void vgacon_write(struct console *con, const char *s, unsigned int len) { for (unsigned int i = 0; i < len; i++) { vgacon_putchar(s[i]); } } static struct console early_vgacon = { .c_name = "vgacon", .c_flags = CON_BOOT, .c_write = vgacon_write, .c_lock = SPIN_LOCK_INIT, }; void early_vgacon_init(void) { g_console_cursor_xpos = 0; g_console_cursor_ypos = 0; for (int i = 0; i < k_console_width * k_console_height; i++) { g_console_fb[i] = DEFAULT_ATTRIB << 8; } init_vga_cursor(); move_vga_cursor(g_console_cursor_xpos, g_console_cursor_ypos); console_register(&early_vgacon); early_printk_init(&early_vgacon); } static void vgacon_init(struct device *dev) { } static void vgacon_deinit(struct device *dev) { } static void vgacon_clear(struct device *dev, int x, int y, int width, int height) { } static void vgacon_putc(struct device *dev, int c, int xpos, int ypos, tty_attrib_t attrib) { struct tty_device *ttydev = TTY_DEVICE(dev); struct fbcon_priv *priv = dev->dev_priv; priv->fb_cells[(ypos * ttydev->tty_xcells) + xpos] = VGA_CHAR(c, attrib); } static void vgacon_set_cursor(struct device *dev, enum tty_cursor cur) { } static void vgacon_move_cursor(struct device *dev, int x, int y) { move_vga_cursor(x, y); } static void vgacon_scroll(struct device *dev, enum tty_scroll_dir dir, int lines) { uint16_t *src = g_console_fb + (k_console_width * lines); uint16_t *dst = g_console_fb; size_t n = k_console_width * (k_console_height - lines) * 2; memmove(dst, src, n); dst = g_console_fb + ((k_console_height - lines) * k_console_width); for (int i = 0; i < k_console_width * lines; i++) { dst[i] = VGA_CHAR(0, DEFAULT_ATTRIB); } } static struct tty_driver_ops vgacon_ops = { .tty_init = vgacon_init, .tty_deinit = vgacon_deinit, .tty_clear = vgacon_clear, .tty_putc = vgacon_putc, .tty_set_cursor = vgacon_set_cursor, .tty_move_cursor = vgacon_move_cursor, .tty_scroll = vgacon_scroll, }; kern_status_t init_vgacon_console(struct device *tty, struct device *fb) { struct char_device *cdev = CHAR_DEVICE(tty); struct framebuffer_varinfo fb_mode; struct framebuffer_fixedinfo fixedinfo; struct tty_device *ttydev = cdev->c_tty; struct fbcon_priv *priv = kzalloc(sizeof *priv, VM_NORMAL); if (!priv) { return KERN_NO_MEMORY; } kern_status_t status = framebuffer_get_varinfo(fb, &fb_mode); if (status != KERN_OK) { kfree(priv); return status; } status = framebuffer_get_fixedinfo(fb, &fixedinfo); if (status != KERN_OK) { kfree(priv); return status; } ttydev->tty_xcells = fb_mode.fb_xcells; ttydev->tty_ycells = fb_mode.fb_ycells; ttydev->tty_xcur = g_console_cursor_xpos; ttydev->tty_ycur = g_console_cursor_ypos; ttydev->tty_curattrib = DEFAULT_ATTRIB; priv->fbdev = fb; priv->fb_pitch = fb_mode.fb_stride; priv->fb_cells = vm_phys_to_virt(fixedinfo.fb_baseptr); priv->tty_ops = &vgacon_ops; tty->dev_priv = priv; return KERN_OK; }