summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSlendi <slendi@socopon.com>2023-09-27 20:54:59 +0300
committerSlendi <slendi@socopon.com>2023-09-27 20:54:59 +0300
commit1a3b5f7e8027213f40f4afddeae6e6d63de85c42 (patch)
tree944312df892a4acf050b50dec03ab7f50147ae18
parentb98d83ae9fd330d1138b8a0768e8fc30f55ebcce (diff)
Add GOP and demo for it.
Signed-off-by: Slendi <slendi@socopon.com>
-rw-r--r--efi.odin31
-rw-r--r--efi_console.odin157
-rw-r--r--main.odin84
-rwxr-xr-xrun.sh2
4 files changed, 271 insertions, 3 deletions
diff --git a/efi.odin b/efi.odin
index 2a0fc62..1b7eac9 100644
--- a/efi.odin
+++ b/efi.odin
@@ -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,
+}
diff --git a/main.odin b/main.odin
index 25f5478..3916ea9 100644
--- a/main.odin
+++ b/main.odin
@@ -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
}
diff --git a/run.sh b/run.sh
index bf881bd..5838701 100755
--- a/run.sh
+++ b/run.sh
@@ -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 $@