diff options
author | Slendi <slendi@socopon.com> | 2023-09-27 20:54:59 +0300 |
---|---|---|
committer | Slendi <slendi@socopon.com> | 2023-09-27 20:54:59 +0300 |
commit | 1a3b5f7e8027213f40f4afddeae6e6d63de85c42 (patch) | |
tree | 944312df892a4acf050b50dec03ab7f50147ae18 | |
parent | b98d83ae9fd330d1138b8a0768e8fc30f55ebcce (diff) |
Add GOP and demo for it.
Signed-off-by: Slendi <slendi@socopon.com>
-rw-r--r-- | efi.odin | 31 | ||||
-rw-r--r-- | efi_console.odin | 157 | ||||
-rw-r--r-- | main.odin | 84 | ||||
-rwxr-xr-x | run.sh | 2 |
4 files changed, 271 insertions, 3 deletions
@@ -2,6 +2,9 @@ package main import "core:runtime" +EFI_SUCCESS :: 0 +EFI_NOT_STARTED :: 19 + EfiHandle :: rawptr EfiSimpleTextInputProtocol :: struct {} @@ -458,6 +461,7 @@ EfiExit :: proc "c" ( exit_data_size: uint, exit_data: [^]u16, ) -> EfiStatus + // Terminates all boot services. // // Inputs: @@ -465,6 +469,29 @@ EfiExit :: proc "c" ( // - map_key: Key to the latest memory map. EfiExitBootServices :: proc "c" (image_handle: EfiHandle, map_key: uint) -> EfiStatus +// Induces a fine-grained stall. +// +// Inputs: +// microseconds: The number of microseconds to stall execution. +EfiStall :: proc "c" (microseconds: uint) + +// Returns the first protocol instance that matches the given protocol. +// +// Inputs: +// - protocol: Provides the protocol to search for. +// - registration: Optional registration key returned from +// EFI_BOOT_SERVICES.RegisterProtocolNotify() . If Registration is NULL, then +// it is ignored. +// +// Outputs: +// - interface: On return, a pointer to the first interface that matches +// Protocol and Registration. +EfiLocateProtocol :: proc "c" ( + protocol: ^GUID, + registration: rawptr, + interface: ^rawptr, +) -> EfiStatus + EFI_BOOT_SERVICES_SIGNATURE :: 0x56524553544f4f42 EFI_BOOT_SERVICES_REVISION :: EFI_SPECIFICATION_VERSION // Contains a table header and pointers to all of the boot services. @@ -510,7 +537,7 @@ EfiBootServices :: struct { // Miscellaneous Services get_next_monotonic_count: EfiUnimplementedFunction, // EFI 1.0+ - stall: EfiUnimplementedFunction, // EFI 1.0+ + stall: EfiStall, // EFI 1.0+ set_watchdog_timer: EfiUnimplementedFunction, // EFI 1.0+ // DriverSupport Services @@ -525,7 +552,7 @@ EfiBootServices :: struct { // Library Services protocols_per_handle: EfiUnimplementedFunction, // EFI 1.1+ locate_handle_buffer: EfiUnimplementedFunction, // EFI 1.1+ - locate_protocol: EfiUnimplementedFunction, // EFI 1.1+ + locate_protocol: EfiLocateProtocol, // EFI 1.1+ install_multiple_protocol_interfaces: EfiUnimplementedFunction, // EFI 1.1+ uninstall_multiple_protocol_interfaces: EfiUnimplementedFunction, // EFI 1.1+ diff --git a/efi_console.odin b/efi_console.odin new file mode 100644 index 0000000..c1a6d26 --- /dev/null +++ b/efi_console.odin @@ -0,0 +1,157 @@ +package main + +EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID: GUID : { + 0x9042a9de, + 0x23dc, + 0x4a38, + {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}, +} + +EFI_GRAPHICS_PIXEL_FORMAT :: enum { + // A pixel is 32-bits and byte zero represents red, byte one represents + // green, byte two represents blue, and byte three is reserved. This is the + // definition for the physical frame buffer. The byte values for the red, + // green, and blue components represent the color intensity. This color + // intensity value range from a minimum intensity of 0 to maximum intensity + // of 255. + PixelRedGreenBlueReserved8BitPerColor, + // A pixel is 32-bits and byte zero represents blue, byte one represents + // green, byte two represents red, and byte three is reserved. This is the + // definition for the physical frame buffer. The byte values for the red, + // green, and blue components represent the color intensity. This color + // intensity value range from a minimum intensity of 0 to maximum intensity + // of 255. + PixelBlueGreenRedReserved8BitPerColor, + // The Pixel definition of the physical frame buffer. + PixelBitMask, + // This mode does not support a physical frame buffer. + PixelBltOnly, + // Valid EFI_GRAPHICS_PIXEL_FORMAT enum values are less than this value. + PixelFormatMax, +} + +EFI_PIXEL_BITMASK :: struct { + RedMask: u32, + GreenMask: u32, + BlueMask: u32, + ReservedMask: u32, +} + +EFI_GRAPHICS_OUTPUT_MODE_INFORMATION :: struct { + // The version of this data structure. A value of zero represents the + // EFI_GRAPHICS_OUTPUT_MODE_INFORMATION structure as defined in this + // specification. + Version: u32, + // The size of video screen in pixels in the X dimension. + HorizontalResolution: u32, + // The size of video screen in pixels in the Y dimension. + VerticalResolution: u32, + // Enumeration that defines the physical format of the pixel. A value of + // PixelBltOnly implies that a linear frame buffer is not available for this + // mode. + PixelFormat: EFI_GRAPHICS_PIXEL_FORMAT, + // This bit-mask is only valid if PixelFormat is set to PixelPixelBitMask. A + // bit being set defines what bits are used for what purpose such as Red, + // Green, Blue, or Reserved. + PixelInformation: EFI_PIXEL_BITMASK, + // Defines the number of pixel elements per video memory line. + PixelsPerScanLine: u32, +} + +// Returns information for an available graphics mode that the graphics device +// and the set of active video output devices supports. +// +// Inputs: +// - this: The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. +// - mode_number: The mode number to return information on. The current mode +// and valid modes are read-only values in the Mode structure of the +// EFI_GRAPHICS_OUTPUT_PROTOCOL. +// - size_of_info: A pointer to the size, in bytes, of the Info buffer. +// - info: A pointer to a callee allocated buffer that returns information +// about ModeNumber. +EfiGraphicsOutputProtocolQueryMode :: proc "c" ( + this: ^EFI_GRAPHICS_OUTPUT_PROTOCOL, + mode_number: u32, + size_of_info: ^uint, + info: ^^EFI_GRAPHICS_OUTPUT_MODE_INFORMATION, +) -> EfiStatus + +// Set the video device into the specified mode and clears the visible portions +// of the output display to black. +// +// Inputs: +// - this: The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. +// - mode_number: Abstraction that defines the current video mode. The current +// mode and valid modes are read-only values in the Mode structure of the +// EFI_GRAPHICS_OUTPUT_PROTOCOL. +EfiGraphicsOutputProtocolSetMode :: proc "c" ( + this: ^EFI_GRAPHICS_OUTPUT_PROTOCOL, + mode_number: u32, +) -> EfiStatus + +EFI_GRAPHICS_OUTPUT_BLT_PIXEL :: struct { + Blue: u8, + Green: u8, + Red: u8, + Reserved: u8, +} + +EFI_GRAPHICS_OUTPUT_BLT_OPERATION :: enum { + EfiBltVideoFill, + EfiBltVideoToBltBuffer, + EfiBltBufferToVideo, + EfiBltVideoToVideo, + EfiGraphicsOutputBltOperationMax, +} + +EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE :: struct { + max_mode: u32, + mode: u32, + info: ^EFI_GRAPHICS_OUTPUT_MODE_INFORMATION, + size_of_info: uint, + frame_buffer_base: uint, + frame_buffer_size: uint, +} + +// Blt a rectangle of pixels on the graphics screen. Blt stands for BLock +// Transfer. +// +// Inputs: +// - this: The EFI_GRAPHICS_OUTPUT_PROTOCOL instance. +// - blt_buffer: The data to transfer to the graphics screen. Size is at least +// Width*Height*sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL). +// - blt_operation: The operation to perform when copying BltBuffer on to the +// graphics screen. +// - source_x: The X coordinate of the source for the BltOperation. The origin +// of the screen is 0, 0 and that is the upper left-hand corner of the +// screen. +// - source_y: The Y coordinate of the source for the BltOperation. The origin +// of the screen is 0, 0 and that is the upper left-hand corner of the +// screen. +// - destination_x: The X coordinate of the destination for the BltOperation. +// The origin of the screen is 0, 0 and that is the upper left-hand corner of +// the screen. +// - destination_y: The Y coordinate of the destination for the BltOperation. +// The origin of the screen is 0, 0 and that is the upper left-hand corner of +// the screen. +// - width: The width of a rectangle in the blt rectangle in pixels. Each pixel +// is represented by an EFI_GRAPHICS_OUTPUT_BLT_PIXEL element. +// - height: The height of a rectangle in the blt rectangle in pixels. Each pixel +// is represented by an EFI_GRAPHICS_OUTPUT_BLT_PIXEL element. +// - delta: Not used for EfiBltVideoFill or the EfiBltVideoToVideo operation. +// If a Delta of zero is used, the entire BltBuffer is being operated on. If +// a subrectangle of the BltBuffer is being used then Delta represents the +// number of bytes in a row of the BltBuffer. +EfiGraphicsOutputProtocolBlt :: proc "c" ( + this: ^EFI_GRAPHICS_OUTPUT_PROTOCOL, + blt_buffer: ^EFI_GRAPHICS_OUTPUT_BLT_PIXEL, + blt_operation: EFI_GRAPHICS_OUTPUT_BLT_OPERATION, + source_x, source_y, destination_x, destination_y, width, height, delta: uint, +) + +EFI_GRAPHICS_OUTPUT_PROTOCOL :: struct { + query_mode: EfiGraphicsOutputProtocolQueryMode, + set_mode: EfiGraphicsOutputProtocolSetMode, + blt: EfiGraphicsOutputProtocolBlt, + mode: ^EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE, +} @@ -4,6 +4,37 @@ import "core:intrinsics" L :: intrinsics.constant_utf16_cstring +hsv2rgb::proc(H, S, V: f32) -> EFI_GRAPHICS_OUTPUT_BLT_PIXEL{ + r, g, b: f32; + + h :f32= H / 1; + s :f32= S / 1; + v :f32= V / 1; + + i :int= auto_cast (h * 6); + f :f32= h * 6 - f32(i); + p :f32= v * (1 - s); + q :f32= v * (1 - f * s); + t :f32= v * (1 - (1 - f) * s); + + switch (i % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } + + color :EFI_GRAPHICS_OUTPUT_BLT_PIXEL + color.Red = auto_cast (r * 255); + color.Green = auto_cast (g * 255); + color.Blue = auto_cast (b * 255); + color.Reserved = 0xff + + return color; +} + main_ :: proc(efi_handle: rawptr, system_table: ^EfiSystemTable) -> u64 { system_table.console_out.reset(system_table.console_out, true) system_table.console_out.output_string( @@ -11,6 +42,59 @@ main_ :: proc(efi_handle: rawptr, system_table: ^EfiSystemTable) -> u64 { L("Hello from Odin! Firmware vendor: "), ) system_table.console_out.output_string(system_table.console_out, system_table.firmware_vendor) + system_table.console_out.output_string(system_table.console_out, L("\nLoading GOP demo.\n")) + + gopUuid := EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID + gop: ^EFI_GRAPHICS_OUTPUT_PROTOCOL = nil + if system_table.boot_services.locate_protocol(&gopUuid, nil, auto_cast &gop) != EFI_SUCCESS { + system_table.console_out.output_string(system_table.console_out, L("Failed to locate GOP")) + for {} + } + + info: ^EFI_GRAPHICS_OUTPUT_MODE_INFORMATION = nil + size_of_info, num_modes, native_move: uint = 0, 0, 0 + status := gop.query_mode(gop, gop.mode == nil ? 0 : gop.mode.mode, &size_of_info, &info) + if status == EFI_NOT_STARTED { + status = gop.set_mode(gop, 0) + } + if status != EFI_SUCCESS { + system_table.console_out.output_string( + system_table.console_out, + L("Failed to get native mode"), + ) + for {} + } + + native_move = auto_cast gop.mode.mode + num_modes = auto_cast gop.mode.max_mode + + pixel: EFI_GRAPHICS_OUTPUT_BLT_PIXEL= { + 0xff, 0xff, 0xff, 0 + } + + hue :f32= 0.0 + for { + // Create new color + pixel = hsv2rgb(hue, 1, 1) + + system_table.boot_services.stall(50 * 000); + hue += 0.001 + if (hue > 1) {hue = 0} + + gop.blt( + gop, + &pixel, + EFI_GRAPHICS_OUTPUT_BLT_OPERATION.EfiBltVideoFill, + 0, + 0, + 0, + 0, + auto_cast gop.mode.info.HorizontalResolution, + auto_cast gop.mode.info.VerticalResolution, + 0, + ) + } + for {} return 0 } @@ -1,2 +1,2 @@ #!/bin/sh -qemu-system-x86_64 -drive format=raw,file=disk.img -m 4G -smp 4 -M q35,kernel-irqchip=split -device intel-iommu,intremap=on -bios /usr/share/edk2/x64/OVMF.fd +qemu-system-x86_64 -drive format=raw,file=disk.img -m 4G -smp 4 -M q35,kernel-irqchip=split -device intel-iommu,intremap=on -bios /usr/share/edk2/x64/OVMF.fd -vga std $@ |