From 8c5112fe0d1da5e906506618cf54a312f192253b Mon Sep 17 00:00:00 2001 From: Cass Sheng Date: Tue, 25 Aug 2026 19:27:47 -0500 Subject: [PATCH] protos/limine: Report framebuffer source devices Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Cass Sheng --- EXTENSIONS.md | 57 +++++++++++++++++++++++++++++++++++++++ common/drivers/gop.c | 48 +++++++++++++++++++++++++++++++++ common/lib/fb.h | 13 +++++++++ common/protos/limine.c | 41 ++++++++++++++++++++++++++++ common/protos/limine_mp.h | 40 +++++++++++++++++++++++++++ test/limine.c | 38 ++++++++++++++++++++++++++ 6 files changed, 237 insertions(+) create mode 100644 EXTENSIONS.md create mode 100644 common/protos/limine_mp.h diff --git a/EXTENSIONS.md b/EXTENSIONS.md new file mode 100644 index 00000000..7aa89d5b --- /dev/null +++ b/EXTENSIONS.md @@ -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. diff --git a/common/drivers/gop.c b/common/drivers/gop.c index f333c58a..a8a8f3dd 100644 --- a/common/drivers/gop.c +++ b/common/drivers/gop.c @@ -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) { @@ -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++; } diff --git a/common/lib/fb.h b/common/lib/fb.h index 1f9af77d..cb85a3e4 100644 --- a/common/lib/fb.h +++ b/common/lib/fb.h @@ -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; @@ -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; diff --git a/common/protos/limine.c b/common/protos/limine.c index 4d566394..eb5e2814 100644 --- a/common/protos/limine.c +++ b/common/protos/limine.c @@ -36,6 +36,7 @@ #define LIMINE_NO_POINTERS #include #include +#include enum executable_format { EXECUTABLE_FORMAT_ELF, @@ -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); diff --git a/common/protos/limine_mp.h b/common/protos/limine_mp.h new file mode 100644 index 00000000..43b20ad4 --- /dev/null +++ b/common/protos/limine_mp.h @@ -0,0 +1,40 @@ +#ifndef PROTOS__LIMINE_MP_H__ +#define PROTOS__LIMINE_MP_H__ + +#include + +#ifndef LIMINE_H +#error "include before " +#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 diff --git a/test/limine.c b/test/limine.c index f51241f7..3ad4269c 100644 --- a/test/limine.c +++ b/test/limine.c @@ -2,6 +2,7 @@ #include #include #include +#include "../common/protos/limine_mp.h" #include #include #include @@ -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; @@ -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) {