Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions EXTENSIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# MicroPerceptron Limine extensions

This fork implements optional Limine protocol features used by MicroPerceptron
systems. Unsupported bootloaders leave each extension request's response
untouched, as they do for any other unknown Limine request.

## Framebuffer source

The framebuffer source feature identifies the firmware device that produced
each entry in the standard Limine framebuffer response.

```c
#define LIMINE_MP_FRAMEBUFFER_SOURCE_REQUEST_ID \
{ LIMINE_COMMON_MAGIC, 0x28143af3420a1fff, 0xd1f11b7ac1c98b44 }

#define LIMINE_MP_FRAMEBUFFER_SOURCE_UNKNOWN 0
#define LIMINE_MP_FRAMEBUFFER_SOURCE_PCI 1

struct limine_mp_framebuffer_pci_source {
uint64_t segment;
uint64_t bus;
uint64_t device;
uint64_t function;
};

struct limine_mp_framebuffer_source {
uint64_t type;
void *data;
};

struct limine_mp_framebuffer_source_response {
uint64_t revision;
uint64_t entry_count;
struct limine_mp_framebuffer_source **entries;
};

struct limine_mp_framebuffer_source_request {
uint64_t id[4];
uint64_t revision;
struct limine_mp_framebuffer_source_response *response;
};
```

The request requires the standard Limine framebuffer request. The response is
omitted when that request is absent or has no framebuffers. `entry_count` equals
the standard response's `framebuffer_count`, and entries correspond by index.

For `LIMINE_MP_FRAMEBUFFER_SOURCE_UNKNOWN`, `data` is `NULL`. For
`LIMINE_MP_FRAMEBUFFER_SOURCE_PCI`, `data` points to a
`limine_mp_framebuffer_pci_source`. Segment, bus, device, and function use PCI
segment-group numbering. Values outside the PCI ranges are never reported as a
PCI source.

UEFI GOP handles are resolved through their device paths to the closest handle
supporting `EFI_PCI_IO_PROTOCOL`, whose `GetLocation()` method provides the PCI
coordinates. Resolution failure, a non-PCI provider, and BIOS VBE all produce
an unknown source rather than an inferred identity.
48 changes: 48 additions & 0 deletions common/drivers/gop.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,53 @@ static bool mode_to_fb_info(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *g

bool gop_force_16 = false;

static void get_framebuffer_source(struct fb_info *fb, EFI_HANDLE gop_handle) {
EFI_GUID device_path_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
EFI_DEVICE_PATH *device_path = NULL;

EFI_STATUS status = gBS->HandleProtocol(gop_handle, &device_path_guid,
(void **)&device_path);
if (status != EFI_SUCCESS) {
return;
}

EFI_GUID pci_io_guid = EFI_PCI_IO_PROTOCOL_GUID;
EFI_HANDLE pci_handle = NULL;
status = gBS->LocateDevicePath(&pci_io_guid, &device_path, &pci_handle);
if (status != EFI_SUCCESS) {
return;
}

EFI_PCI_IO_PROTOCOL *pci_io = NULL;
status = gBS->HandleProtocol(pci_handle, &pci_io_guid, (void **)&pci_io);
if (status != EFI_SUCCESS) {
return;
}

UINTN segment;
UINTN bus;
UINTN device;
UINTN function;
status = pci_io->GetLocation(pci_io, &segment, &bus, &device, &function);
if (status != EFI_SUCCESS
|| segment > UINT16_MAX
|| bus > UINT8_MAX
|| device > 31
|| function > 7) {
return;
}

fb->source_type = FB_SOURCE_PCI;
fb->pci_source.segment = segment;
fb->pci_source.bus = bus;
fb->pci_source.device = device;
fb->pci_source.function = function;

printv("gop: Framebuffer source PCI %x:%x:%x.%x\n",
(uint32_t)segment, (uint32_t)bus, (uint32_t)device,
(uint32_t)function);
}

static bool try_mode(struct fb_info *ret, EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
size_t mode, uint64_t width, uint64_t height, int bpp,
struct fb_info *fbs, size_t fbs_count) {
Expand Down Expand Up @@ -382,6 +429,7 @@ success:;
size_t mode_count;
fb->mode_list = get_mode_list(&mode_count, gop);
fb->mode_count = mode_count;
get_framebuffer_source(fb, handles[i]);

fbs_count++;
}
Expand Down
13 changes: 13 additions & 0 deletions common/lib/fb.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ struct resolution {
uint16_t bpp;
};

#define FB_SOURCE_NONE 0
#define FB_SOURCE_PCI 1

struct fb_pci_source {
uint16_t segment;
uint8_t bus;
uint8_t device;
uint8_t function;
};

struct fb_info {
uint64_t framebuffer_pitch;
uint64_t framebuffer_width;
Expand All @@ -31,6 +41,9 @@ struct fb_info {

uint64_t mode_count;
struct fb_info *mode_list;

uint8_t source_type;
struct fb_pci_source pci_source;
};

extern struct fb_info *fb_fbs;
Expand Down
41 changes: 41 additions & 0 deletions common/protos/limine.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#define LIMINE_NO_POINTERS
#include <protos/limine.h>
#include <limine.h>
#include <protos/limine_mp.h>

enum executable_format {
EXECUTABLE_FORMAT_ELF,
Expand Down Expand Up @@ -1631,6 +1632,46 @@ FEAT_START
framebuffer_request->response = reported_addr(framebuffer_response);
FEAT_END

// MicroPerceptron framebuffer source feature
FEAT_START
struct limine_mp_framebuffer_source_request *source_request =
get_request(source_request, LIMINE_MP_FRAMEBUFFER_SOURCE_REQUEST_ID);
if (source_request == NULL) {
break;
}

if (fbp == NULL || fbs_count == 0) {
break;
}

struct limine_mp_framebuffer_source *sources =
ext_mem_alloc_counted(fbs_count, sizeof(struct limine_mp_framebuffer_source));
struct limine_mp_framebuffer_pci_source *pci_sources =
ext_mem_alloc_counted(fbs_count, sizeof(struct limine_mp_framebuffer_pci_source));
uint64_t *source_list = ext_mem_alloc_counted(fbs_count, sizeof(uint64_t));

for (size_t i = 0; i < fbs_count; i++) {
if (fbs[i].source_type == FB_SOURCE_PCI) {
pci_sources[i].segment = fbs[i].pci_source.segment;
pci_sources[i].bus = fbs[i].pci_source.bus;
pci_sources[i].device = fbs[i].pci_source.device;
pci_sources[i].function = fbs[i].pci_source.function;

sources[i].type = LIMINE_MP_FRAMEBUFFER_SOURCE_PCI;
sources[i].data = reported_addr(&pci_sources[i]);
}

source_list[i] = reported_addr(&sources[i]);
}

struct limine_mp_framebuffer_source_response *source_response =
ext_mem_alloc(sizeof(struct limine_mp_framebuffer_source_response));
source_response->entry_count = fbs_count;
source_response->entries = reported_addr(source_list);

source_request->response = reported_addr(source_response);
FEAT_END

// Flanterm FB init params feature
FEAT_START
struct limine_flanterm_fb_init_params_request *fip_request = get_request(fip_request, LIMINE_FLANTERM_FB_INIT_PARAMS_REQUEST_ID);
Expand Down
40 changes: 40 additions & 0 deletions common/protos/limine_mp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef PROTOS__LIMINE_MP_H__
#define PROTOS__LIMINE_MP_H__

#include <stdint.h>

#ifndef LIMINE_H
#error "include <limine.h> before <protos/limine_mp.h>"
#endif

#define LIMINE_MP_FRAMEBUFFER_SOURCE_REQUEST_ID \
{ LIMINE_COMMON_MAGIC, 0x28143af3420a1fff, 0xd1f11b7ac1c98b44 }

#define LIMINE_MP_FRAMEBUFFER_SOURCE_UNKNOWN 0
#define LIMINE_MP_FRAMEBUFFER_SOURCE_PCI 1

struct limine_mp_framebuffer_pci_source {
uint64_t segment;
uint64_t bus;
uint64_t device;
uint64_t function;
};

struct limine_mp_framebuffer_source {
uint64_t type;
LIMINE_PTR(void *) data;
};

struct limine_mp_framebuffer_source_response {
uint64_t revision;
uint64_t entry_count;
LIMINE_PTR(struct limine_mp_framebuffer_source **) entries;
};

struct limine_mp_framebuffer_source_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_mp_framebuffer_source_response *) response;
};

#endif
38 changes: 38 additions & 0 deletions test/limine.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
#include "../common/protos/limine_mp.h"
#include <e9print.h>
#include <flanterm.h>
#include <flanterm_backends/fb.h>
Expand Down Expand Up @@ -218,6 +219,12 @@ static volatile struct limine_flanterm_fb_init_params_request fip_request = {
.revision = 0, .response = NULL,
};

__attribute__((section(".limine_requests")))
static volatile struct limine_mp_framebuffer_source_request source_request = {
.id = LIMINE_MP_FRAMEBUFFER_SOURCE_REQUEST_ID,
.revision = 0, .response = NULL,
};

__attribute__((used, section(".limine_requests_end_marker")))
static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER;

Expand Down Expand Up @@ -505,6 +512,37 @@ FEAT_START
}
FEAT_END

FEAT_START
printf("\n");
if (source_request.response == NULL) {
printf("Framebuffer sources not passed\n");
break;
}
struct limine_mp_framebuffer_source_response *source_response =
source_request.response;
printf("Framebuffer sources feature, revision %lu\n",
source_response->revision);
printf("%lu source(s)\n", source_response->entry_count);
if (source_response->entry_count
!= framebuffer_request.response->framebuffer_count) {
printf("Framebuffer source count mismatch\n");
break;
}
for (size_t i = 0; i < source_response->entry_count; i++) {
struct limine_mp_framebuffer_source *source = source_response->entries[i];
if (source->type == LIMINE_MP_FRAMEBUFFER_SOURCE_UNKNOWN) {
printf("Source %lu: unknown\n", i);
} else if (source->type == LIMINE_MP_FRAMEBUFFER_SOURCE_PCI
&& source->data != NULL) {
struct limine_mp_framebuffer_pci_source *pci = source->data;
printf("Source %lu: PCI %lx:%lx:%lx.%lx\n", i, pci->segment,
pci->bus, pci->device, pci->function);
} else {
printf("Source %lu: invalid type %lu\n", i, source->type);
}
}
FEAT_END

FEAT_START
printf("\n");
if (fip_request.response == NULL) {
Expand Down
Loading