EFI: Can't install the graphics output interface.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

EFI: Can't install the graphics output interface.

Post by Roman »

To learn about EFI programming I decided to write a small implementation of a EFI C library for writing EFI applications.

Here's my EFI system table structure:

Code: Select all

typedef struct {
	efi_tab_hdr_t header;
	char16_t *vendor;
	uint32_t revision;

	void *con_in_handle;
	efi_txt_input_proto_t *con_in;

	void *con_out_handle;
	efi_txt_output_proto_t *con_out;

	void *con_err_handle;
	efi_txt_output_proto_t *con_err;

	efi_rtsrv_t *rtsrv;
	efi_bsrv_t *bsrv;
} efi_systab_t;
Here's the boot services:

Code: Select all

typedef struct {
	efi_tab_hdr_t hdr;

	efi_tpl_t (*raise_tpl)(efi_tpl_t);
	void (*rstor_tpl)(efi_tpl_t);

	efi_status_t (*alloc_pages)(efi_alloc_type_t, efi_memory_type_t, native_int_t pages, native_int_t *memory);
	efi_status_t (*free_pages)(native_int_t, native_int_t);

	efi_status_t (*get_mmap)(native_int_t*, efi_mem_t*, native_int_t*, native_int_t*, uint32_t*);

	efi_status_t (*alloc_pool)(efi_memory_type_t, native_int_t, void**);
	efi_status_t (*free_pool)(void*);

	efi_status_t (*create_evt)(uint32_t, efi_tpl_t, efi_evt_notify_t, void*, void**);
	efi_status_t (*set_timer)(void*, efi_timer_delay_t, uint64_t);
	efi_status_t (*wait_evt)(native_int_t, void**, native_int_t*);
	efi_status_t (*sig_evt)(void*);
	efi_status_t (*close_evt)(void*);
	efi_status_t (*check_evt)(void*);

	efi_status_t (*install_proto)(void*, efi_guid_t*, efi_interface_t, void*);
} efi_bsrv_t;
And the problem:

Code: Select all

	void *graphics_handle = 0;
	efi_guid_t graphics_guid = EFI_GRAPHICS_PROTO_GUID;
	efi_graphics_proto_t *graphics = malloc(sizeof(efi_graphics_proto_t));
	status = efi_systab->bsrv->install_proto(&graphics_handle, &graphics_guid, EFI_NATIVE_INTERFACE, graphics);
	if (status != EFI_SUCCESS)
	{
		puts(L"Failed to install the graphics interface.");
		halt();
	}

	puts(L"Successfully installed the graphics interface.\n\r");

	hexdump(graphics, sizeof(efi_graphics_proto_t));
	halt();
install_proto() returns EFI_SUCCESS, points the handle somewhere, but the hexdump() function outputs something strange (see in the attachments).
Attachments
Снимок экрана 2015-02-02 в 20.38.05.png
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
Icee
Member
Member
Posts: 100
Joined: Wed Jan 08, 2014 8:41 am
Location: Moscow, Russia

Re: EFI: Can't install the graphics output interface.

Post by Icee »

Wait, what are you trying to do here? As far as I understand, the InstallProtocolInterface API call does just what its name implies -- it _installs_ your implementation of a protocol onto a handle. The call is not supposed to fill the structure, it is its input, not output, parameter.

You probably want to use LocateProtocol instead.
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: EFI: Can't install the graphics output interface.

Post by Roman »

Icee wrote:Wait, what are you trying to do here? As far as I understand, the InstallProtocolInterface API call does just what its name implies -- it _installs_ your implementation of a protocol onto a handle. The call is not supposed to fill the structure, it is its input, not output, parameter.

You probably want to use LocateProtocol instead.
Thank you. But now I have another problem.

Code: Select all

        efi_guid_t graphics_proto_guid[] = EFI_GRAPHICS_PROTO_GUID;
        efi_graphics_proto_t *graphics;
        status = efi_systab->bsrv->locate_proto(graphics_proto_guid, 0, (void**) &graphics);
The call returns EFI_INVALID_PARAMETER. The Phoenix Wiki says, that such status is returned, when the third argument is null, but it is not.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
Icee
Member
Member
Posts: 100
Joined: Wed Jan 08, 2014 8:41 am
Location: Moscow, Russia

Re: EFI: Can't install the graphics output interface.

Post by Icee »

Roman wrote:The call returns EFI_INVALID_PARAMETER. The Phoenix Wiki says, that such status is returned, when the third argument is null, but it is not.
The official UEFI 2.4 spec says the same. Are you sure that you have no link / call convention problems? Are you targetting 32- or 64-bit UEFI? Which toolchain are you using? I suspect a relocation problem.
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: EFI: Can't install the graphics output interface.

Post by Roman »

Icee wrote:
Roman wrote:The call returns EFI_INVALID_PARAMETER. The Phoenix Wiki says, that such status is returned, when the third argument is null, but it is not.
The official UEFI 2.4 spec says the same. Are you sure that you have no link / call convention problems? Are you targetting 32- or 64-bit UEFI? Which toolchain are you using? I suspect a relocation problem.
Toolchain: x86_64-w64-mingw32
Firmware: OVMF
Machine: QEMU
CFLAGS: -ffreestanding -c -nostdinc -Iinclude -Iinclude/arch/$(ARCH) -masm=intel -std=$(STDC_VERSION) -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
LDFLAGS: -nostdlib -Wl,-dll -shared -Wl,--subsystem,10 -e efi_main
The updated boot services structure:

Code: Select all

typedef struct {
        efi_tab_hdr_t hdr;

        efi_tpl_t (*raise_tpl)(efi_tpl_t);
        void (*rstor_tpl)(efi_tpl_t);

        efi_status_t (*alloc_pages)(efi_alloc_type_t, efi_memory_type_t, native_int_t pages, native_int_t *memory);
        efi_status_t (*free_pages)(native_int_t, native_int_t);

        efi_status_t (*get_mmap)(native_int_t*, efi_mem_t*, native_int_t*, native_int_t*, uint32_t*);

        efi_status_t (*alloc_pool)(efi_memory_type_t, native_int_t, void**);
        efi_status_t (*free_pool)(void*);

        efi_status_t (*create_evt)(uint32_t, efi_tpl_t, efi_evt_notify_t, void*, void**);
        efi_status_t (*set_timer)(void*, efi_timer_delay_t, uint64_t);
        efi_status_t (*wait_evt)(native_int_t, void**, native_int_t*);
        efi_status_t (*sig_evt)(void*);
        efi_status_t (*close_evt)(void*);
        efi_status_t (*check_evt)(void*);

        efi_status_t (*install_proto)(void**, efi_guid_t*, efi_interface_t, void*);
        efi_status_t (*reinstall_proto)(void*, efi_guid_t*, void*, void*);
        efi_status_t (*uninstall_proto)(void*, efi_guid_t*, void*);
        efi_status_t (*handle_proto)(void*, efi_guid_t*, void**);

        void *reserved;

        efi_status_t (*reg_proto_notify)(efi_guid_t*, void*, void**);

        efi_status_t (*locate_handle)(efi_locate_search_t, efi_guid_t*, void*, native_int_t*, void**);
        efi_status_t (*locate_dev_path)(efi_guid_t*, efi_dev_path_proto_t**, void**);
        efi_status_t (*install_cfg_tab)(efi_guid_t*, void*);

        efi_status_t (*load_img)(bool, void*, efi_dev_path_proto_t*, void*, native_int_t, void**);
        efi_status_t (*start_img)(void*, native_int_t*, char16_t**);

        efi_status_t (*exit)(void*, efi_status_t, native_int_t, char16_t*);

        void *unknown_unload_img; // TODO: add unload_img here.

        efi_status_t (*exit_bsrv)(void*, native_int_t);

        efi_status_t (*get_next_monotonic_cnt)(uint64_t*);

        efi_status_t (*stall)(native_int_t);

        efi_status_t (*set_watchdog_timer)(native_int_t, uint64_t, native_int_t, char16_t*);

        // TODO: find information about these functions.
        void *unknown_connect_controller;
        void *unknown_disconnect_controller;

        efi_status_t (*open_proto)(void*, efi_guid_t*, void**, void*, void*, uint32_t);
        efi_status_t (*close_proto)(void*, efi_guid_t*, void*, void*);
        efi_status_t (*open_proto_info)(void*, efi_guid_t*, efi_proto_info_ent_t**, native_int_t*);

        void *unknown_locate_handle_buffer;
        efi_status_t (*locate_proto)(efi_guid_t*, void*, void**);
} efi_bsrv_t;
I should note, that some calls (e.g. stall, alloc_pool, free_pool, create_evt, set_timer...), I tested, work perfectly.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
Icee
Member
Member
Posts: 100
Joined: Wed Jan 08, 2014 8:41 am
Location: Moscow, Russia

Re: EFI: Can't install the graphics output interface.

Post by Icee »

Could you also post an objdump -dr disassembly of the function where locate_proto is called?

EDIT: disregard that. Your boot services structure is missing a field: before LocateHandleBuffer you forgot ProtocolsPerHandle, so you are essentially calling LocateHandleBuffer instead of LocateProtocol.
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: EFI: Can't install the graphics output interface.

Post by Roman »

Icee wrote:Could you also post an objdump -dr disassembly of the function where locate_proto is called?

EDIT: disregard that. Your boot services structure is missing a field: before LocateHandleBuffer you forgot ProtocolsPerHandle, so you are essentially calling LocateHandleBuffer instead of LocateProtocol.
Thank you again! I've made a stupid mistake! #-o

Edit: it looks like, it works!
Attachments
Снимок экрана 2015-02-03 в 23.03.03.png
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: EFI: Can't install the graphics output interface.

Post by Roman »

I have another question: what is the actual size of a memory descriptor?
My code outputs some realistic values for the first entry (see in the attachment), but for the second one it shows some strange ones (too big value for a physical address).

Code: Select all

	native_int_t mmap_sz = sizeof(efi_mem_t) * 100;
	efi_mem_t *mmap = malloc(mmap_sz);
	native_int_t mmap_key;
	native_int_t mmap_ent_sz;
	uint32_t ver;
	//efi_mem_t *mmap = get_mmap(&mmap_key, &mmap_ent_n, &mmap_ent_sz);
	status = efi_systab->bsrv->get_mmap(&mmap_sz, mmap, &mmap_key, &mmap_ent_sz, &ver);
	if (status != EFI_SUCCESS)
	{
		puts(L"Failed to get the memory map: ");
		puts(ulltowcs(status, 10));
		halt();
	}

	puts(L"Memory map is at: 0x");
	puts(ulltowcs((uint64_t) mmap, 0x10));

	puts(L"\n\rMemory map entry size: 0x");
	puts(ulltowcs(mmap_ent_sz, 0x10));
	//mmap += sizeof(efi_mem_t);
	//mmap += mmap_ent_sz;

	puts(L"\n\rType: ");
	puts(ulltowcs(mmap->type, 10));

	puts(L"\n\rPhysical start: 0x");
	puts(ulltowcs(mmap->pstart, 0x10));

	puts(L"\n\rVirtual start: 0x");
	puts(ulltowcs(mmap->vstart, 0x10));

	puts(L"\n\rPage number: 0x");
	puts(ulltowcs(mmap->pnum, 0x10));

	puts(L"\n\rAttributes: 0x");
	puts(ulltowcs(mmap->attr, 0x10));

	halt();
My memory descriptor structure:

Code: Select all

typedef struct {
        uint32_t type;
        uint32_t pad;
        native_int_t pstart;
        native_int_t vstart;
        uint64_t pnum;
        uint64_t attr;
} efi_mem_t;
Attachments
Снимок экрана 2015-02-04 в 21.18.12.png
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Re: EFI: Can't install the graphics output interface.

Post by jnc100 »

The current defined EFI_MEMORY_DESCRIPTOR is as you define it, with the exception of the padding member which you don't need (the MS x64 ABI will automatically align the next member, PhysicalStart, on an 8 byte boundary).

To iterate through them, though, you need to use the DescriptorSize value returned. I see (in one of your commented attempts) you add this to a value of type efi_mem_t *, which will not advance the pointer by that number of bytes, but rather than by DesciptorType * sizeof(efi_mem_t *) bytes.

You'd want something like:

Code: Select all

uintptr_t offset = 0;
while(offset < mmap_sz)
{
    efi_mem_t *cur_entry = (efi_mem_t *)((uintptr_t)mmap + offset);

    // printf statements

    offset += mmap_ent_sz;
}
Regards,
John.
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: EFI: Can't install the graphics output interface.

Post by Roman »

Thank you, jnc100. I've never used the += operator with pointers, I thought, it would increment by bytes.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
Post Reply