Browse Source

Support mode setting from EFI

K. Lange 2 years ago
parent
commit
e7ccc61d62
8 changed files with 205 additions and 2 deletions
  1. 8 0
      base/usr/include/kernel/multiboot.h
  2. 33 2
      boot/cstuff.c
  3. 4 0
      boot/kbd.h
  4. 114 0
      boot/moremultiboot.h
  5. 7 0
      boot/multiboot.h
  6. 24 0
      boot/text.h
  7. 1 0
      kernel/main.c
  8. 14 0
      modules/lfbvideo.c

+ 8 - 0
base/usr/include/kernel/multiboot.h

@@ -18,6 +18,7 @@
 #define MULTIBOOT_FLAG_LOADER  0x200
 #define MULTIBOOT_FLAG_APM     0x400
 #define MULTIBOOT_FLAG_VBE     0x800
+#define MULTIBOOT_FLAG_FB     0x1000
 
 struct multiboot
 {
@@ -45,6 +46,13 @@ struct multiboot
 	uintptr_t vbe_interface_seg;
 	uintptr_t vbe_interface_off;
 	uintptr_t vbe_interface_len;
+	uintptr_t framebuffer_addr;
+	uintptr_t framebuffer_pitch;
+	uintptr_t framebuffer_width;
+	uintptr_t framebuffer_height;
+	uint8_t   framebuffer_bpp;
+	uint8_t   framebuffer_type;
+	/* Palette stuff goes here but we don't use it */
 } __attribute__ ((packed));
 
 typedef struct {

+ 33 - 2
boot/cstuff.c

@@ -38,6 +38,7 @@ EFI_HANDLE ImageHandleIn;
 #define DEFAULT_SINGLE_CMDLINE "start=terminal "
 #define DEFAULT_TEXT_CMDLINE "start=--vga "
 #define DEFAULT_VID_CMDLINE "vid=auto,1440,900 "
+#define DEFAULT_PRESET_VID_CMDLINE "vid=preset "
 #define DEFAULT_NETINIT_CMDLINE "init=/dev/ram0 _"
 #define MIGRATE_CMDLINE "start=--migrate _"
 #define DEBUG_LOG_CMDLINE "logtoserial=3 "
@@ -48,6 +49,10 @@ char * module_dir = "MOD";
 char * kernel_path = "KERNEL.";
 char * ramdisk_path = "RAMDISK.IMG";
 
+#ifdef EFI_PLATFORM
+int _efi_do_mode_set = 0;
+#endif
+
 /* Where to dump kernel data while loading */
 #define KERNEL_LOAD_START 0x300000
 
@@ -152,6 +157,24 @@ int kmain() {
 			"Downloads a userspace filesystem from a remote",
 			"server and extracts it at boot.");
 
+#ifdef EFI_PLATFORM
+	BOOT_OPTION(_efilargest,  0, "Prefer largest mode.",
+			"When using EFI mode setting, use the largest mode.",
+			NULL);
+
+	BOOT_OPTION(_efi1024,     0, "Prefer 1024x768",
+			"If a 1024x768x32 mode is found, set that.",
+			NULL);
+
+	BOOT_OPTION(_efi1080p,    0, "Prefer 1080p",
+			"If a 1920x1080 mode is found, set that.",
+			NULL);
+
+	BOOT_OPTION(_efiask,      0, "Ask for input on each mode.",
+			"Displays a y/n prompt for each possible mode.",
+			NULL);
+#endif
+
 	/* Loop over rendering the menu */
 	show_menu();
 
@@ -167,14 +190,22 @@ int kmain() {
 		}
 	}
 
+	char * _video_command_line = DEFAULT_VID_CMDLINE;
+#ifdef EFI_PLATFORM
+	_efi_do_mode_set = (_efilargest ? 1 : (_efi1024 ? 2 : (_efi1080p ? 3 : (_efiask ? 4 : 0))));
+	if (_efi_do_mode_set) {
+		_video_command_line = DEFAULT_PRESET_VID_CMDLINE;
+	}
+#endif
+
 	if (boot_mode == 1) {
 		strcat(cmdline, DEFAULT_GRAPHICAL_CMDLINE);
-		strcat(cmdline, DEFAULT_VID_CMDLINE);
+		strcat(cmdline, _video_command_line);
 	} else if (boot_mode == 2) {
 		strcat(cmdline, DEFAULT_TEXT_CMDLINE);
 	} else if (boot_mode == 3) {
 		strcat(cmdline, DEFAULT_SINGLE_CMDLINE);
-		strcat(cmdline, DEFAULT_VID_CMDLINE);
+		strcat(cmdline, _video_command_line);
 	} else if (boot_mode == 4) {
 		strcat(cmdline, DEFAULT_HEADLESS_CMDLINE);
 	}

+ 4 - 0
boot/kbd.h

@@ -30,6 +30,10 @@ static int read_scancode(void) {
 				case L'8':
 				case L'9':
 					return Key.UnicodeChar - L'1' + KBD_SCAN_1;
+				case L'y':
+					return 'y';
+				case L'n':
+					return 'n';
 				default:
 					return 0xFF;
 			}

+ 114 - 0
boot/moremultiboot.h

@@ -51,6 +51,11 @@ char * final_offset = NULL;
 
 extern char do_the_nasty[];
 
+#ifdef EFI_PLATFORM
+static EFI_GUID efi_graphics_output_protocol_guid =
+  {0x9042a9de,0x23dc,0x4a38,  {0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a}};
+#endif
+
 static void move_kernel(void) {
 	clear();
 	print("Relocating kernel...\n");
@@ -152,6 +157,115 @@ static void move_kernel(void) {
 	multiboot_header.mem_lower = 1024;
 	multiboot_header.mem_upper = upper_mem / 1024;
 
+	/* Set up framebuffer */
+	if (_efi_do_mode_set) {
+		UINTN count;
+		EFI_HANDLE * handles;
+		EFI_GRAPHICS_OUTPUT_PROTOCOL * gfx;
+		EFI_STATUS status;
+
+		status = uefi_call_wrapper(ST->BootServices->LocateHandleBuffer,
+				5, ByProtocol, &efi_graphics_output_protocol_guid, NULL, &count, &handles);
+		if (EFI_ERROR(status)) {
+			print_("Error getting graphics device handle.\n");
+			while (1) {};
+		}
+
+		status = uefi_call_wrapper(ST->BootServices->HandleProtocol,
+				3, handles[0], &efi_graphics_output_protocol_guid, (void **)&gfx);
+		if (EFI_ERROR(status)) {
+			print_("Error getting graphics device.\n");
+			while (1) {};
+		}
+
+#if 0
+		print_("Attempting to set a sane mode (32bit color, etc.)\n");
+		print_("There are 0x"); print_hex_(gfx->Mode->MaxMode); print_(" modes available.\n");
+		print_("This is mode 0x"); print_hex_(gfx->Mode->Mode); print_(".\n");
+#endif
+
+		int biggest = gfx->Mode->Mode;
+		int big_width = 0;
+		int big_height = 0;
+
+		clear_();
+
+		for (int i = 0; i < gfx->Mode->MaxMode; ++i) {
+			EFI_STATUS status;
+			UINTN size;
+			EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info;
+
+			status = uefi_call_wrapper(gfx->QueryMode,
+					4, gfx, i, &size, &info);
+
+			if (EFI_ERROR(status)) {
+				print_("Error getting gfx mode 0x"); print_hex_(i); print_("\n");
+			} else {
+				print_("Mode "); print_int_(i); print_(" "); print_int_(info->HorizontalResolution);
+				print_("x"); print_int_(info->VerticalResolution); print_(" ");
+				print_int_(info->PixelFormat); print_("\n");
+				if (_efi_do_mode_set == 1) {
+					if (info->PixelFormat == 1 && info->HorizontalResolution >= big_width) {
+						biggest = i;
+						big_width = info->HorizontalResolution;
+					}
+				} else if (_efi_do_mode_set == 2) {
+					if (info->PixelFormat == 1 && info->HorizontalResolution == 1024 &&
+						info->VerticalResolution == 768) {
+						biggest = i;
+						break;
+					}
+				} else if (_efi_do_mode_set == 3) {
+					if (info->PixelFormat == 1 && info->HorizontalResolution == 1920 &&
+						info->VerticalResolution == 1080) {
+						biggest = i;
+						break;
+					}
+				} else if (_efi_do_mode_set == 4) {
+					while (1) {
+						print_("y/n? ");
+						int resp = read_scancode();
+						if (resp == 'y') {
+							biggest = i;
+							goto done_video;
+						} else if (resp == 'n') {
+							break;
+						}
+					}
+				}
+			}
+		}
+
+done_video:
+		print_("Selected video mode was "); print_int_(biggest); print_("\n");
+		uefi_call_wrapper(gfx->SetMode, 2, gfx, biggest);
+
+		uint32_t high = gfx->Mode->FrameBufferBase >> 32;
+		uint32_t low  = gfx->Mode->FrameBufferBase & 0xFFFFFFFF;
+
+		print_("Framebuffer address is 0x"); print_hex_(high); print_hex_(low); print_("\n");
+
+		if (high) {
+			clear_();
+			print_("Framebuffer is outside of 32-bit memory range.\n");
+			print_("EFI mode setting is not available - and graphics may not work in general.\n");
+			while (1) {};
+		}
+
+		multiboot_header.flags |= (1 << 12); /* Enable framebuffer flag */
+		multiboot_header.framebuffer_addr = low;
+		multiboot_header.framebuffer_width  = gfx->Mode->Info->HorizontalResolution;
+		multiboot_header.framebuffer_height = gfx->Mode->Info->VerticalResolution;
+		multiboot_header.framebuffer_pitch = 0;
+
+		print_("Mode information passed to multiboot:\n");
+		print_("  Address: 0x"); print_hex_(multiboot_header.framebuffer_addr); print_("\n");
+		print_("  Width:   "); print_int_(multiboot_header.framebuffer_width); print_("\n");
+		print_("  Height:  "); print_int_(multiboot_header.framebuffer_height); print_("\n");
+		print_("\n");
+
+	}
+
 
 	memcpy(final_offset, cmdline, 1024);
 	multiboot_header.cmdline = (uintptr_t)final_offset;

+ 7 - 0
boot/multiboot.h

@@ -41,6 +41,13 @@ struct multiboot
 	uint32_t vbe_interface_seg;
 	uint32_t vbe_interface_off;
 	uint32_t vbe_interface_len;
+	uint32_t framebuffer_addr;
+	uint32_t framebuffer_pitch;
+	uint32_t framebuffer_width;
+	uint32_t framebuffer_height;
+	uint8_t   framebuffer_bpp;
+	uint8_t   framebuffer_type;
+	/* Palette stuff goes here but we don't use it */
 } __attribute__ ((packed));
 
 typedef struct {

+ 24 - 0
boot/text.h

@@ -99,6 +99,30 @@ static void print_banner(char * str) {
 	y++;
 }
 
+#ifdef EFI_PLATFORM
+static void print_int_(unsigned int value) {
+	unsigned int n_width = 1;
+	unsigned int i = 9;
+	while (value > i && i < UINT32_MAX) {
+		n_width += 1;
+		i *= 10;
+		i += 9;
+	}
+
+	char buf[n_width+1];
+	buf[n_width] = 0;
+	i = n_width;
+	while (i > 0) {
+		unsigned int n = value / 10;
+		int r = value % 10;
+		buf[i - 1] = r + '0';
+		i--;
+		value = n;
+	}
+	print_(buf);
+}
+#endif
+
 static void print_hex_(unsigned int value) {
 	char out[9] = {0};
 	for (int i = 7; i > -1; i--) {

+ 1 - 0
kernel/main.c

@@ -124,6 +124,7 @@ int kmain(struct multiboot *mboot, uint32_t mboot_mag, uintptr_t esp) {
 	if ((uintptr_t)mboot_ptr > last_mod) {
 		last_mod = (uintptr_t)mboot_ptr + sizeof(struct multiboot);
 	}
+	while (last_mod & 0x7FF) last_mod++;
 	kmalloc_startat(last_mod);
 
 	if (mboot_ptr->flags & MULTIBOOT_FLAG_MEM) {

+ 14 - 0
modules/lfbvideo.c

@@ -325,6 +325,20 @@ static void graphics_install_preset(uint16_t w, uint16_t h) {
 	debug_print(NOTICE, "Graphics were pre-configured (thanks, bootloader!), locating video memory...");
 	uint16_t b = 32; /* If you are 24 bit, go away, we really do not support you. */
 
+	if (mboot_ptr && (mboot_ptr->flags & (1 << 12))) {
+		/* hello world */
+		lfb_vid_memory = (void *)mboot_ptr->framebuffer_addr;
+		w = mboot_ptr->framebuffer_width;
+		h = mboot_ptr->framebuffer_height;
+
+		debug_print(WARNING, "Mode was set by bootloader: %dx%d bpp should be 32, framebuffer is at 0x%x", w, h, (uintptr_t)lfb_vid_memory);
+
+		for (uintptr_t i = (uintptr_t)lfb_vid_memory; i <= (uintptr_t)lfb_vid_memory + w * h * 4; i += 0x1000) {
+			dma_frame(get_page(i, 1, kernel_directory), 0, 1, i);
+		}
+		goto mem_found;
+	}
+
 	/* XXX: Massive hack */
 	uint32_t * herp = (uint32_t *)0xA0000;
 	herp[0] = 0xA5ADFACE;