Page 1 of 1

EFI: Can't install the graphics output interface.

Posted: Mon Feb 02, 2015 11:49 am
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).

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

Posted: Tue Feb 03, 2015 1:47 am
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.

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

Posted: Tue Feb 03, 2015 12:52 pm
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.

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

Posted: Tue Feb 03, 2015 1:00 pm
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.

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

Posted: Tue Feb 03, 2015 1:07 pm
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.

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

Posted: Tue Feb 03, 2015 1:12 pm
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.

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

Posted: Tue Feb 03, 2015 1:53 pm
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!

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

Posted: Wed Feb 04, 2015 12:23 pm
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;

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

Posted: Wed Feb 04, 2015 12:50 pm
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.

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

Posted: Wed Feb 04, 2015 1:12 pm
by Roman
Thank you, jnc100. I've never used the += operator with pointers, I thought, it would increment by bytes.