Switching VGA mode with GRUB

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
Post Reply
readlnh
Posts: 10
Joined: Mon Apr 10, 2017 10:25 am

Switching VGA mode with GRUB

Post by readlnh »

hi everyone
I want to switch to the VGA graphic mode. For this I will use GRUB.I switch back to Real Mode for setting the graphic video mode, but I meet a few problems,it just reboots back to GRUB in qemu
i use the code from http://www.rohitab.com/discuss/topic/35 ... -mode/,but seems it dosen't work well.
here is my code:

Code: Select all

MBOOT_HEADER_MAGIC  equ 0x1BADB002
MBOOT_PAGE_ALIGN    equ 1 << 0
MBOOT_MEM_INFO      equ 1 << 1
;MBOOT_GRAPH_MODE    equ 1 << 2
MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO ;| MBOOT_GRAPH_MODE
MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)

;magic flags checksum (magic + flags + checksum = 0)

[BITS 32]
section .text

dd MBOOT_HEADER_MAGIC
dd MBOOT_HEADER_FLAGS
dd MBOOT_CHECKSUM

;dd 0
;dd 0
;dd 0
;dd 0 
;dd 0

;dd 0
;dd 640
;dd 480
;dd 16

[GLOBAL start]
[GLOBAL glb_mboot_ptr]
[EXTERN kern_entry]

start:
    cli

    mov esp, STACK_TOP
    mov ebp, 0
    and esp, 0FFFFFFF0H
    mov [glb_mboot_ptr] , ebx
    call kern_entry

stop:
    hlt
    jmp stop

section .bss
stack:
    resb 32768
glb_mboot_ptr:
    resb 4

STACK_TOP equ $ - stack - 1

Code: Select all

// define our structure
typedef struct __attribute__ ((packed)) {
	unsigned short di, si, bp, sp, bx, dx, cx, ax;
	unsigned short gs, fs, es, ds, eflags;
} regs16_t;

// tell compiler our int32 function is external
extern void int32(unsigned char intnum, regs16_t *regs);

// int32 test
void int32_test()
{
	int y;
	regs16_t regs;
	
	// switch to 320x200x256 graphics mode
	regs.ax = 0x0013;
	int32(0x10, &regs);

	// full screen with blue color (1)
	//memset((char *)0xA0000, 0xff, (320*200));
	
	// draw horizontal line from 100,80 to 100,240 in multiple colors
	//for(y = 0; y < 200; y++)
	//	memset((char *)0xA0000 + (y*320+80), y, 160);
	
	// wait for key
	/*regs.ax = 0x0000;
	int32(0x16, &regs);*/
	
	// switch to 80x25x16 text mode
	//regs.ax = 0x0003;
	//int32(0x10, &regs);
}

int kern_entry()
{
		
	//regs16_t regs;	
	//regs.ax = 0x0013;
	//int32(0x10, &regs);
	int32_test();	
	char* p;	
	int i;
	for(i = 0xA0000; i < 0xAFFFF; i++) {
		p = (char*)i;
		*p = 0xff;	
	}
	return 0;
}

Code: Select all

; 
; Protected Mode BIOS Call Functionailty v2.0 - by Napalm
; -------------------------------------------------------
; 
; This is code shows how its POSSIBLE to execute BIOS interrupts
; by switch out to real-mode and then back into protected mode.
; 
; If you wish to use all or part of this code you must agree
; to the license at the following URL.
; 
; License: http://creativecommons.org/licenses/by-sa/2.0/uk/
;         
; Notes: This file is in NASM syntax.
;        Turn off paging before calling these functions.
;        int32() resets all selectors.
;
; C Prototype:
;	void _cdelc int32(unsigned char intnum, regs16_t *regs);
; 
; Example of usage:
;   regs.ax = 0x0013;
;   int32(0x10, &regs);
;   memset((char *)0xA0000, 1, (320*200));
;   memset((char *)0xA0000 + (100*320+80), 14, 80);
;   regs.ax = 0x0000;
;   int32(0x16, &regs);
;   regs.ax = 0x0003;
;   int32(0x10, &regs);
; 
; 
[bits 32]

global int32, _int32

struc regs16_t
	.di	resw 1
	.si	resw 1
	.bp	resw 1
	.sp resw 1
	.bx	resw 1
	.dx	resw 1
	.cx	resw 1
	.ax	resw 1
	.gs	resw 1
	.fs	resw 1
	.es	resw 1
	.ds	resw 1
	.ef resw 1
endstruc

%define INT32_BASE                             0x7C00
%define REBASE(x)                              (((x) - reloc) + INT32_BASE)
%define GDTENTRY(x)                            ((x) << 3)
%define CODE32                                 GDTENTRY(1)	; 0x08
%define DATA32                                 GDTENTRY(2)	; 0x10
%define CODE16                                 GDTENTRY(3)	; 0x18
%define DATA16                                 GDTENTRY(4)	; 0x20
%define STACK16                                (INT32_BASE - regs16_t_size)


section .text
	int32: use32                               ; by Napalm
	_int32:
		cli                                    ; disable interrupts
		pusha                                  ; save register state to 32bit stack
		mov  esi, reloc                        ; set source to code below
		mov  edi, INT32_BASE                   ; set destination to new base address
		mov  ecx, (int32_end - reloc)          ; set copy size to our codes size
		cld                                    ; clear direction flag (so we copy forward)
		rep  movsb                             ; do the actual copy (relocate code to low 16bit space)
		jmp INT32_BASE                         ; jump to new code location
	reloc: use32                               ; by Napalm
		mov  [REBASE(stack32_ptr)], esp        ; save 32bit stack pointer
		sidt [REBASE(idt32_ptr)]               ; save 32bit idt pointer
		sgdt [REBASE(gdt32_ptr)]               ; save 32bit gdt pointer
		lgdt [REBASE(gdt16_ptr)]               ; load 16bit gdt pointer
		lea  esi, [esp+0x24]                   ; set position of intnum on 32bit stack
		lodsd                                  ; read intnum into eax
		mov  [REBASE(ib)], al                  ; set intrrupt immediate byte from our arguments 
		mov  esi, [esi]                        ; read regs pointer in esi as source
		mov  edi, STACK16                      ; set destination to 16bit stack
		mov  ecx, regs16_t_size                ; set copy size to our struct size
		mov  esp, edi                          ; save destination to as 16bit stack offset
		rep  movsb                             ; do the actual copy (32bit stack to 16bit stack)
		jmp  word CODE16:REBASE(p_mode16)      ; switch to 16bit selector (16bit protected mode)
	p_mode16: use16
		mov  ax, DATA16                        ; get our 16bit data selector
		mov  ds, ax                            ; set ds to 16bit selector
		mov  es, ax                            ; set es to 16bit selector
		mov  fs, ax                            ; set fs to 16bit selector
		mov  gs, ax                            ; set gs to 16bit selector
		mov  ss, ax                            ; set ss to 16bit selector
		mov  eax, cr0                          ; get cr0 so we can modify it
		and  al,  ~0x01                        ; mask off PE bit to turn off protected mode
		mov  cr0, eax                          ; set cr0 to result
		jmp  word 0x0000:REBASE(r_mode16)      ; finally set cs:ip to enter real-mode
	r_mode16: use16
		xor  ax, ax                            ; set ax to zero
		mov  ds, ax                            ; set ds so we can access idt16
		mov  ss, ax                            ; set ss so they the stack is valid
		lidt [REBASE(idt16_ptr)]               ; load 16bit idt
		mov  bx, 0x0870                        ; master 8 and slave 112
		call resetpic                          ; set pic's the to real-mode settings
		popa                                   ; load general purpose registers from 16bit stack
		pop  gs                                ; load gs from 16bit stack
		pop  fs                                ; load fs from 16bit stack
		pop  es                                ; load es from 16bit stack
		pop  ds                                ; load ds from 16bit stack
		sti                                    ; enable interrupts
		db 0xCD                                ; opcode of INT instruction with immediate byte
	ib: db 0x00
		cli                                    ; disable interrupts
		xor  sp, sp                            ; zero sp so we can reuse it
		mov  ss, sp                            ; set ss so the stack is valid
		mov  sp, INT32_BASE                    ; set correct stack position so we can copy back
		pushf                                  ; save eflags to 16bit stack
		push ds                                ; save ds to 16bit stack
		push es                                ; save es to 16bit stack
		push fs                                ; save fs to 16bit stack
		push gs                                ; save gs to 16bit stack
		pusha                                  ; save general purpose registers to 16bit stack
		mov  bx, 0x2028                        ; master 32 and slave 40
		call resetpic                          ; restore the pic's to protected mode settings
		mov  eax, cr0                          ; get cr0 so we can modify it
		inc  eax                               ; set PE bit to turn on protected mode
		mov  cr0, eax                          ; set cr0 to result
		jmp  dword CODE32:REBASE(p_mode32)     ; switch to 32bit selector (32bit protected mode)
	p_mode32: use32
		mov  ax, DATA32                        ; get our 32bit data selector
		mov  ds, ax                            ; reset ds selector
		mov  es, ax                            ; reset es selector
		mov  fs, ax                            ; reset fs selector
		mov  gs, ax                            ; reset gs selector
		mov  ss, ax                            ; reset ss selector
		lgdt [REBASE(gdt32_ptr)]               ; restore 32bit gdt pointer
		lidt [REBASE(idt32_ptr)]               ; restore 32bit idt pointer
		mov  esp, [REBASE(stack32_ptr)]        ; restore 32bit stack pointer
		mov  esi, STACK16                      ; set copy source to 16bit stack
		lea  edi, [esp+0x28]                   ; set position of regs pointer on 32bit stack
		mov  edi, [edi]                        ; use regs pointer in edi as copy destination
		mov  ecx, regs16_t_size                ; set copy size to our struct size
		cld                                    ; clear direction flag (so we copy forward)
		rep  movsb                             ; do the actual copy (16bit stack to 32bit stack)
		popa                                   ; restore registers
		sti                                    ; enable interrupts
		ret                                    ; return to caller
		
	resetpic:                                  ; reset's 8259 master and slave pic vectors
		push ax                                ; expects bh = master vector, bl = slave vector
		mov  al, 0x11                          ; 0x11 = ICW1_INIT | ICW1_ICW4
		out  0x20, al                          ; send ICW1 to master pic
		out  0xA0, al                          ; send ICW1 to slave pic
		mov  al, bh                            ; get master pic vector param
		out  0x21, al                          ; send ICW2 aka vector to master pic
		mov  al, bl                            ; get slave pic vector param
		out  0xA1, al                          ; send ICW2 aka vector to slave pic
		mov  al, 0x04                          ; 0x04 = set slave to IRQ2
		out  0x21, al                          ; send ICW3 to master pic
		shr  al, 1                             ; 0x02 = tell slave its on IRQ2 of master
		out  0xA1, al                          ; send ICW3 to slave pic
		shr  al, 1                             ; 0x01 = ICW4_8086
		out  0x21, al                          ; send ICW4 to master pic
		out  0xA1, al                          ; send ICW4 to slave pic
		pop  ax                                ; restore ax from stack
		ret                                    ; return to caller
		
	stack32_ptr:                               ; address in 32bit stack after we
		dd 0x00000000                          ;   save all general purpose registers
		
	idt32_ptr:                                 ; IDT table pointer for 32bit access
		dw 0x0000                              ; table limit (size)
		dd 0x00000000                          ; table base address
		
	gdt32_ptr:                                 ; GDT table pointer for 32bit access
		dw 0x0000                              ; table limit (size)
		dd 0x00000000                          ; table base address
		
	idt16_ptr:                                 ; IDT table pointer for 16bit access
		dw 0x03FF                              ; table limit (size)
		dd 0x00000000                          ; table base address
		
	gdt16_base:                                ; GDT descriptor table
		.null:                                 ; 0x00 - null segment descriptor
			dd 0x00000000                      ; must be left zero'd
			dd 0x00000000                      ; must be left zero'd
			
		.code32:                               ; 0x01 - 32bit code segment descriptor 0xFFFFFFFF
			dw 0xFFFF                          ; limit  0:15
			dw 0x0000                          ; base   0:15
			db 0x00                            ; base  16:23
			db 0x9A                            ; present, iopl/0, code, execute/read
			db 0xCF                            ; 4Kbyte granularity, 32bit selector; limit 16:19
			db 0x00                            ; base  24:31
			
		.data32:                               ; 0x02 - 32bit data segment descriptor 0xFFFFFFFF
			dw 0xFFFF                          ; limit  0:15
			dw 0x0000                          ; base   0:15
			db 0x00                            ; base  16:23
			db 0x92                            ; present, iopl/0, data, read/write
			db 0xCF                            ; 4Kbyte granularity, 32bit selector; limit 16:19
			db 0x00                            ; base  24:31
			
		.code16:                               ; 0x03 - 16bit code segment descriptor 0x000FFFFF
			dw 0xFFFF                          ; limit  0:15
			dw 0x0000                          ; base   0:15
			db 0x00                            ; base  16:23
			db 0x9A                            ; present, iopl/0, code, execute/read
			db 0x0F                            ; 1Byte granularity, 16bit selector; limit 16:19
			db 0x00                            ; base  24:31
			
		.data16:                               ; 0x04 - 16bit data segment descriptor 0x000FFFFF
			dw 0xFFFF                          ; limit  0:15
			dw 0x0000                          ; base   0:15
			db 0x00                            ; base  16:23
			db 0x92                            ; present, iopl/0, data, read/write
			db 0x0F                            ; 1Byte granularity, 16bit selector; limit 16:19
			db 0x00                            ; base  24:31
			
	gdt16_ptr:                                 ; GDT table pointer for 16bit access
		dw gdt16_ptr - gdt16_base - 1          ; table limit (size)
		dd gdt16_base                          ; table base address
		
	int32_end:                                 ; end marker (so we can copy the code)
	
	
can somenoe give me some advice?
Sorry for my poor English
User avatar
obiwac
Member
Member
Posts: 149
Joined: Fri Jan 27, 2017 12:15 pm
Libera.chat IRC: obiwac
Location: Belgium

Re: Switching VGA mode with GRUB

Post by obiwac »

First off, uncomment all the

Code: Select all

dd
lines at the beginning. These are the flags that pass the graphics mode information to grub, so that it can set your graphics mode before putting you into protected mode (more info: https://www.gnu.org/software/grub/manua ... iboot.html). Then, just before

Code: Select all

call kern_entry
, push ebx to the stack with

Code: Select all

push ebx
. You don't need to switch back to real mode. Then, to draw on the screen, you need to get the framebuffer pointer (in the grub command line, type the

Code: Select all

lspci -i
, and under VGA, get the last eight digits of the hex string), and you can start modifying the screen straight away!

Example:

Code: Select all

MBOOT_HEADER_MAGIC  equ 0x1BADB002
MBOOT_PAGE_ALIGN    equ 1 << 0
MBOOT_MEM_INFO      equ 1 << 1
;MBOOT_GRAPH_MODE    equ 1 << 2
MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO ;| MBOOT_GRAPH_MODE
MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)

;magic flags checksum (magic + flags + checksum = 0)

[BITS 32]
section .text

dd MBOOT_HEADER_MAGIC
dd MBOOT_HEADER_FLAGS
dd MBOOT_CHECKSUM

dd 0
dd 0
dd 0
dd 0 
dd 0

dd 0
dd 800 ; width
dd 600 ; height
dd 32 ; bpp

[GLOBAL start]
[GLOBAL glb_mboot_ptr]
[EXTERN kern_entry]

start:
    cli

    mov esp, STACK_TOP
    mov ebp, 0
    and esp, 0FFFFFFF0H
    mov [glb_mboot_ptr] , ebx

    push ebx
    call kern_entry

stop:
    hlt
    jmp stop

section .bss
stack:
    resb 32768
glb_mboot_ptr:
    resb 4

STACK_TOP equ $ - stack - 1

Code: Select all

extern void int32(unsigned char intnum, regs16_t *regs);

void kern_entry()
{   
   unsigned int* buffer = (unsigned int*) 0xE0000000;
   for (int i = 0; i < 800 * 600; i++)
      buffer[i] = 0x00FF00FF;
   while (1) {}
}
You won't need the other code anymore. Also, further down the line, you won't want to hard code the framebuffer pointer, as it varies from device to device, but grub gives you a vbe information structure, with the pointer for you to use. But that's for another topic.
Ask if you need more help, or if it doesn't work.
readlnh
Posts: 10
Joined: Mon Apr 10, 2017 10:25 am

Re: Switching VGA mode with GRUB

Post by readlnh »

obiwac wrote:First off, uncomment all the

Code: Select all

dd
lines at the beginning. These are the flags that pass the graphics mode information to grub, so that it can set your graphics mode before putting you into protected mode (more info: https://www.gnu.org/software/grub/manua ... iboot.html). Then, just before

Code: Select all

call kern_entry
, push ebx to the stack with

Code: Select all

push ebx
. You don't need to switch back to real mode. Then, to draw on the screen, you need to get the framebuffer pointer (in the grub command line, type the

Code: Select all

lspci -i
, and under VGA, get the last eight digits of the hex string), and you can start modifying the screen straight away!
thank you very much,but it still dosen't work. I'm wanting just a simple 320 x 200 8bit screen.I don't know what's wrong with it
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: Switching VGA mode with GRUB

Post by SpyderTL »

8-bits may not be an option. Try something a little more common, like 640x480x32 bits.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
User avatar
obiwac
Member
Member
Posts: 149
Joined: Fri Jan 27, 2017 12:15 pm
Libera.chat IRC: obiwac
Location: Belgium

Re: Switching VGA mode with GRUB

Post by obiwac »

If you're just wanting 320x200x8, then you can write a pure protected mode driver, that'll work for all graphics cards.


It's in C++, but if you want, I think I have the same code lying around in C, and I could post it here.
readlnh
Posts: 10
Joined: Mon Apr 10, 2017 10:25 am

Re: Switching VGA mode with GRUB

Post by readlnh »

obiwac wrote:If you're just wanting 320x200x8, then you can write a pure protected mode driver, that'll work for all graphics cards.


It's in C++, but if you want, I think I have the same code lying around in C, and I could post it here.
thank you very much
User avatar
zesterer
Member
Member
Posts: 59
Joined: Mon Feb 22, 2016 4:40 am
Libera.chat IRC: zesterer
Location: United Kingdom
Contact:

Re: Switching VGA mode with GRUB

Post by zesterer »

I might be talking through my arse here, but I had similar problems a few months back. Turns out you need to actually specify what mode you want in your grub.cfg before it'll actually be nice enough to change things.

Something like this, I believe:

Code: Select all

menuentry "MyOS" {
	multiboot /boot/myos.bin
	set gfxpayload=1024x768x32

	boot
}
Hopefully that'll get you what you want :-)
Current developing Tupai, a monolithic x86 operating system
http://zesterer.homenet.org/projects.shtml
User avatar
obiwac
Member
Member
Posts: 149
Joined: Fri Jan 27, 2017 12:15 pm
Libera.chat IRC: obiwac
Location: Belgium

Re: Switching VGA mode with GRUB

Post by obiwac »

zesterer wrote:I might be talking through my arse here, but I had similar problems a few months back. Turns out you need to actually specify what mode you want in your grub.cfg before it'll actually be nice enough to change things.

Something like this, I believe:

Code: Select all

menuentry "MyOS" {
	multiboot /boot/myos.bin
	set gfxpayload=1024x768x32

	boot
}
Hopefully that'll get you what you want :-)
Actually, that just changes the graphics mode for grub. But before switching to pmode, grub changes to 80x25 text mode. So, you have to tell it what mode you want with the multiboot flags.
User avatar
zesterer
Member
Member
Posts: 59
Joined: Mon Feb 22, 2016 4:40 am
Libera.chat IRC: zesterer
Location: United Kingdom
Contact:

Re: Switching VGA mode with GRUB

Post by zesterer »

obiwac wrote:
zesterer wrote:I might be talking through my arse here, but I had similar problems a few months back. Turns out you need to actually specify what mode you want in your grub.cfg before it'll actually be nice enough to change things.

Something like this, I believe:

Code: Select all

menuentry "MyOS" {
	multiboot /boot/myos.bin
	set gfxpayload=1024x768x32

	boot
}
Hopefully that'll get you what you want :-)
Actually, that just changes the graphics mode for grub. But before switching to pmode, grub changes to 80x25 text mode. So, you have to tell it what mode you want with the multiboot flags.
That's not the case. If you wish to change GRUB's graphics mode, you use "gfxmode". However, "gfxpayload" defines the graphics mode that is passed on to the OS before booting.
Current developing Tupai, a monolithic x86 operating system
http://zesterer.homenet.org/projects.shtml
User avatar
obiwac
Member
Member
Posts: 149
Joined: Fri Jan 27, 2017 12:15 pm
Libera.chat IRC: obiwac
Location: Belgium

Re: Switching VGA mode with GRUB

Post by obiwac »

Hmm... what version of grub are you using?
User avatar
zesterer
Member
Member
Posts: 59
Joined: Mon Feb 22, 2016 4:40 am
Libera.chat IRC: zesterer
Location: United Kingdom
Contact:

Re: Switching VGA mode with GRUB

Post by zesterer »

obiwac wrote:Hmm... what version of grub are you using?
GRUB 2.02~rc1. This was the primary problem I had when requesting graphics modes, and gfxpayload changes this.
Current developing Tupai, a monolithic x86 operating system
http://zesterer.homenet.org/projects.shtml
readlnh
Posts: 10
Joined: Mon Apr 10, 2017 10:25 am

Re: Switching VGA mode with GRUB

Post by readlnh »

Thank you all for your attention, i have sloved the problem as i do this http://forum.osdev.org/viewtopic.php?f=1&t=31524
Post Reply