assemblygraphicsbootloaderosdevgrub

GRUB not Switching to Graphics Mode in Custom OS Development


I am developing a custom operating system and using the GRUB bootloader to load my kernel. In my development process, I've encountered an issue where I am unable to switch the display mode from text to graphics mode. Despite configuring GRUB to start in a graphics mode (1024x768x32), the system appears to remain in text mode during booting.

The assembly bootloader and the section of the kernel where I try to use graphics mode are provided below: ; bootloader.asm

[bits 32]

section .multiboot

align 4
dd 0x1BADB002            ; Multiboot magic number
dd 0x00                  ; Flags
dd -(0x1BADB002 + 0x00)  ; Checksum (must be such that all the dword sums to zero)

section .bootstrap_stack
align 16
stack_bottom: 
times 16384 db 0 ; Declare a stack of 16KB
stack_top:

section .text

extern kmain ; This should be in your kernel code
global start

start:

    mov esp, stack_top ; Set up the stack
    call kmain         ; Call the main function in the kernel
    cli                ; Disable interrupts
.hang:
    hlt                ; Halt the CPU
    jmp .hang          ; Infinite loop just in case

my kernel.c file looks like: // kernel.c

#define VIDEO_MEMORY 0xA0000

void print(char *str, int x, int y) {
    volatile char *video = (volatile char*)(0xB8000 + 2*x + 160*y);
    while (*str != 0) {
        *video = *str;
        video += 2;
        str++;
    }
}

#define VIDEO_MEMORY 0xA0000

void fill_screen(unsigned char color) {
    unsigned char* video_memory = (unsigned char*)VIDEO_MEMORY;
    int i;
    for (i = 0; i < 320 * 200; i++)
        video_memory[i] = color;
}

void draw_pixel(int x, int y, unsigned char color) {
    unsigned char* video_memory = (unsigned char*)VIDEO_MEMORY;
    int offset = y * 320 + x;
    video_memory[offset] = color;
}

void fillRect(int x, int y, int width, int height, unsigned char color) {
    unsigned char* video_memory = (unsigned char*)0xA0000;
    int i, j;
    for (i = y; i < y + height; i++) {
        for (j = x; j < x + width; j++) {
            draw_pixel(x+i,y+j, color);
        }
    }
}

void kmain() {
    print("hello", 0, 0);
    
        fill_screen(0x0F);     // Fill the screen with white color (0x0F)
        draw_pixel(160, 100, 0x0);   // Draw a black pixel at (160, 100)
    fillRect(5,10,20,20,0xFF);
    uint8_t* framebuffer = (uint8_t*)0xA0000; // Replace with your framebuffer address
    unsigned int bytes_per_pixel = 3; // Replace with your bytes per pixel
    unsigned int bytes_per_scanline = 1920; // Replace with your bytes per scanline
    unsigned int x = 10; // The x coordinate of the pixel
    unsigned int y = 10; // The y coordinate of the pixel

    uint8_t* pixel_address = framebuffer + y * bytes_per_scanline + x * bytes_per_pixel;
    pixel_address[0] = 0xFF; // Red
    pixel_address[1] = 0x00; // Green
    pixel_address[2] = 0x00; // Blue

}

Here's the configuration in my grub.cfg file:

set default=0
set timeout=0

menuentry "My OS" {
    multiboot /boot/myos.bin
    set gfxpayload=1024x768x32
    insmod all_video
    boot
}

In my kernel, I implemented a print function that prints text to the screen. My expectation was that, if the system had successfully switched to graphics mode, this function would fail as it is designed for text mode. However, the print function worked as if the system was still in text mode, which suggests that the system never made the switch to graphics mode.

I've tried this setup on several virtual machines and physical machines, and I am consistently experiencing the same issue. I am currently using GRUB version 2.06.

I am stumped as to why the switch to graphics mode isn't happening, despite being specified in the GRUB configuration. Any advice or pointers would be greatly appreciated!

Please remember to replace the comments "More drawing functions" and "More drawing code" with the actual drawing functions and code you are using. This will help others diagnose your issue more accurately.


Solution

  • Instead of gfxpayload in grub.cfg to change the resolution, you can use the framebuffer tag in your Multiboot2 header.

    To do this, after checksum add this:

    dw 5
    dw 0 ;instead of 0, you can specify your flags
    dd 20
    dd 1024 ;instead of 1024, you can specify your width
    dd 768 ;instead of 768, you can specify your height
    dd 32 ;instead of 32, you can specify your BPP
    

    Also don't forget that 0xA0000 has a limit of 64,000 bytes and that you can paint over a maximum of 16,000 pixels at 32 BPP.