| 1 | #include <stdint.h> |
| 2 | #include <stddef.h> |
| 3 | #include <stdbool.h> |
| 4 | #include <limine.h> |
| 5 | #include <e9print.h> |
| 6 | #include <flanterm.h> |
| 7 | #include <flanterm_backends/fb.h> |
| 8 | |
| 9 | int memcmp(const void *, const void *, size_t); |
| 10 | |
| 11 | #ifdef ENABLE_QEMU_SHUTDOWN |
| 12 | static inline void outw(uint16_t port, uint16_t value) { |
| 13 | __asm volatile ("outw %%ax, %1" : : "a" (value), "Nd" (port) : "memory"); |
| 14 | } |
| 15 | #endif |
| 16 | |
| 17 | __attribute__((section(".limine_requests"))) |
| 18 | static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(6); |
| 19 | |
| 20 | static void limine_main(void); |
| 21 | |
| 22 | __attribute__((used, section(".limine_requests_start_marker"))) |
| 23 | static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER; |
| 24 | |
| 25 | __attribute__((used, section(".limine_requests"))) |
| 26 | static volatile struct limine_entry_point_request entry_point_request = { |
| 27 | .id = LIMINE_ENTRY_POINT_REQUEST_ID, |
| 28 | .revision = 0, .response = NULL, |
| 29 | |
| 30 | .entry = limine_main |
| 31 | }; |
| 32 | |
| 33 | __attribute__((section(".limine_requests"))) |
| 34 | static volatile struct limine_framebuffer_request framebuffer_request = { |
| 35 | .id = LIMINE_FRAMEBUFFER_REQUEST_ID, |
| 36 | .revision = 0, .response = NULL |
| 37 | }; |
| 38 | |
| 39 | __attribute__((section(".limine_requests"))) |
| 40 | static volatile struct limine_bootloader_info_request bootloader_info_request = { |
| 41 | .id = LIMINE_BOOTLOADER_INFO_REQUEST_ID, |
| 42 | .revision = 0, .response = NULL |
| 43 | }; |
| 44 | |
| 45 | __attribute__((section(".limine_requests"))) |
| 46 | static volatile struct limine_executable_cmdline_request executable_cmdline_request = { |
| 47 | .id = LIMINE_EXECUTABLE_CMDLINE_REQUEST_ID, |
| 48 | .revision = 0, .response = NULL |
| 49 | }; |
| 50 | |
| 51 | __attribute__((section(".limine_requests"))) |
| 52 | static volatile struct limine_firmware_type_request firmware_type_request = { |
| 53 | .id = LIMINE_FIRMWARE_TYPE_REQUEST_ID, |
| 54 | .revision = 0, .response = NULL |
| 55 | }; |
| 56 | |
| 57 | __attribute__((section(".limine_requests"))) |
| 58 | static volatile struct limine_hhdm_request hhdm_request = { |
| 59 | .id = LIMINE_HHDM_REQUEST_ID, |
| 60 | .revision = 0, .response = NULL |
| 61 | }; |
| 62 | |
| 63 | __attribute__((section(".limine_requests"))) |
| 64 | static volatile struct limine_memmap_request memmap_request = { |
| 65 | .id = LIMINE_MEMMAP_REQUEST_ID, |
| 66 | .revision = 0, .response = NULL |
| 67 | }; |
| 68 | |
| 69 | __attribute__((section(".limine_requests"))) |
| 70 | static volatile struct limine_executable_file_request exec_file_request = { |
| 71 | .id = LIMINE_EXECUTABLE_FILE_REQUEST_ID, |
| 72 | .revision = 0, .response = NULL |
| 73 | }; |
| 74 | |
| 75 | struct limine_internal_module internal_module1 = { |
| 76 | .path = "/boot/test.elf", |
| 77 | .string = "First internal module" |
| 78 | }; |
| 79 | |
| 80 | struct limine_internal_module internal_module2 = { |
| 81 | .path = "test.elf", |
| 82 | .string = "Second internal module" |
| 83 | }; |
| 84 | |
| 85 | struct limine_internal_module internal_module3 = { |
| 86 | .path = "./limine.conf", |
| 87 | .string = "Third internal module" |
| 88 | /* gzip test depends on this name to find |
| 89 | the original to compare against. */ |
| 90 | }; |
| 91 | |
| 92 | #ifdef ENABLE_GZIP_TEST |
| 93 | struct limine_internal_module internal_module4 = { |
| 94 | .path = "./limine.conf.gz", |
| 95 | .string = "gzip-compressed limine.conf", |
| 96 | .flags = LIMINE_INTERNAL_MODULE_COMPRESSED |
| 97 | }; |
| 98 | #endif |
| 99 | |
| 100 | struct limine_internal_module *internal_modules[] = { |
| 101 | &internal_module1, |
| 102 | &internal_module2, |
| 103 | &internal_module3, |
| 104 | #ifdef ENABLE_GZIP_TEST |
| 105 | &internal_module4, |
| 106 | #endif |
| 107 | }; |
| 108 | |
| 109 | __attribute__((section(".limine_requests"))) |
| 110 | static volatile struct limine_module_request module_request = { |
| 111 | .id = LIMINE_MODULE_REQUEST_ID, |
| 112 | .revision = 1, .response = NULL, |
| 113 | |
| 114 | .internal_module_count = sizeof(internal_modules) / sizeof(internal_modules[0]), |
| 115 | .internal_modules = internal_modules |
| 116 | }; |
| 117 | |
| 118 | __attribute__((section(".limine_requests"))) |
| 119 | static volatile struct limine_rsdp_request rsdp_request = { |
| 120 | .id = LIMINE_RSDP_REQUEST_ID, |
| 121 | .revision = 0, .response = NULL |
| 122 | }; |
| 123 | |
| 124 | __attribute__((section(".limine_requests"))) |
| 125 | static volatile struct limine_smbios_request smbios_request = { |
| 126 | .id = LIMINE_SMBIOS_REQUEST_ID, |
| 127 | .revision = 0, .response = NULL |
| 128 | }; |
| 129 | |
| 130 | __attribute__((section(".limine_requests"))) |
| 131 | static volatile struct limine_efi_system_table_request est_request = { |
| 132 | .id = LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID, |
| 133 | .revision = 0, .response = NULL |
| 134 | }; |
| 135 | |
| 136 | __attribute__((section(".limine_requests"))) |
| 137 | static volatile struct limine_tpm_event_log_request tpm_event_log_request = { |
| 138 | .id = LIMINE_TPM_EVENT_LOG_REQUEST_ID, |
| 139 | .revision = 0, .response = NULL |
| 140 | }; |
| 141 | |
| 142 | __attribute__((section(".limine_requests"))) |
| 143 | static volatile struct limine_efi_memmap_request efi_memmap_request = { |
| 144 | .id = LIMINE_EFI_MEMMAP_REQUEST_ID, |
| 145 | .revision = 0, .response = NULL |
| 146 | }; |
| 147 | |
| 148 | __attribute__((section(".limine_requests"))) |
| 149 | static volatile struct limine_date_at_boot_request date_at_boot_request = { |
| 150 | .id = LIMINE_DATE_AT_BOOT_REQUEST_ID, |
| 151 | .revision = 0, .response = NULL |
| 152 | }; |
| 153 | |
| 154 | __attribute__((section(".limine_requests"))) |
| 155 | static volatile struct limine_executable_address_request executable_address_request = { |
| 156 | .id = LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, |
| 157 | .revision = 0, .response = NULL |
| 158 | }; |
| 159 | |
| 160 | __attribute__((section(".limine_requests"))) |
| 161 | static volatile struct limine_mp_request _mp_request = { |
| 162 | .id = LIMINE_MP_REQUEST_ID, |
| 163 | .revision = 0, .response = NULL |
| 164 | }; |
| 165 | |
| 166 | __attribute__((section(".limine_requests"))) |
| 167 | static volatile struct limine_dtb_request _dtb_request = { |
| 168 | .id = LIMINE_DTB_REQUEST_ID, |
| 169 | .revision = 0, .response = NULL |
| 170 | }; |
| 171 | |
| 172 | __attribute__((section(".limine_requests"))) |
| 173 | static volatile struct limine_paging_mode_request _pm_request = { |
| 174 | .id = LIMINE_PAGING_MODE_REQUEST_ID, |
| 175 | .revision = 1, .response = NULL, |
| 176 | #if defined (__x86_64__) |
| 177 | .mode = LIMINE_PAGING_MODE_X86_64_5LVL, |
| 178 | .max_mode = LIMINE_PAGING_MODE_X86_64_5LVL, |
| 179 | .min_mode = LIMINE_PAGING_MODE_X86_64_MIN |
| 180 | #elif defined (__aarch64__) |
| 181 | .mode = LIMINE_PAGING_MODE_AARCH64_5LVL, |
| 182 | .max_mode = LIMINE_PAGING_MODE_AARCH64_5LVL, |
| 183 | .min_mode = LIMINE_PAGING_MODE_AARCH64_MIN |
| 184 | #elif defined (__riscv) |
| 185 | .mode = LIMINE_PAGING_MODE_RISCV_SV57, |
| 186 | .max_mode = LIMINE_PAGING_MODE_RISCV_SV57, |
| 187 | .min_mode = LIMINE_PAGING_MODE_RISCV_MIN, |
| 188 | #elif defined (__loongarch__) |
| 189 | .mode = LIMINE_PAGING_MODE_LOONGARCH_DEFAULT, |
| 190 | .max_mode = LIMINE_PAGING_MODE_LOONGARCH_DEFAULT, |
| 191 | .min_mode = LIMINE_PAGING_MODE_LOONGARCH_MIN |
| 192 | #endif |
| 193 | }; |
| 194 | |
| 195 | #ifdef __riscv |
| 196 | __attribute__((section(".limine_requests"))) |
| 197 | static volatile struct limine_riscv_bsp_hartid_request _bsp_request = { |
| 198 | .id = LIMINE_RISCV_BSP_HARTID_REQUEST_ID, |
| 199 | .revision = 0, .response = NULL, |
| 200 | }; |
| 201 | #endif |
| 202 | |
| 203 | __attribute__((section(".limine_requests"))) |
| 204 | static volatile struct limine_tsc_frequency_request tsc_freq_request = { |
| 205 | .id = LIMINE_TSC_FREQUENCY_REQUEST_ID, |
| 206 | .revision = 0, .response = NULL, |
| 207 | }; |
| 208 | |
| 209 | __attribute__((section(".limine_requests"))) |
| 210 | static volatile struct limine_bootloader_performance_request _perf_request = { |
| 211 | .id = LIMINE_BOOTLOADER_PERFORMANCE_REQUEST_ID, |
| 212 | .revision = 0, .response = NULL, |
| 213 | }; |
| 214 | |
| 215 | __attribute__((section(".limine_requests"))) |
| 216 | static volatile struct limine_flanterm_fb_init_params_request fip_request = { |
| 217 | .id = LIMINE_FLANTERM_FB_INIT_PARAMS_REQUEST_ID, |
| 218 | .revision = 0, .response = NULL, |
| 219 | }; |
| 220 | |
| 221 | __attribute__((used, section(".limine_requests_end_marker"))) |
| 222 | static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; |
| 223 | |
| 224 | static char *get_memmap_type(uint64_t type) { |
| 225 | switch (type) { |
| 226 | case LIMINE_MEMMAP_USABLE: |
| 227 | return "Usable"; |
| 228 | case LIMINE_MEMMAP_RESERVED: |
| 229 | return "Reserved"; |
| 230 | case LIMINE_MEMMAP_RESERVED_MAPPED: |
| 231 | return "Reserved (Mapped)"; |
| 232 | case LIMINE_MEMMAP_ACPI_RECLAIMABLE: |
| 233 | return "ACPI reclaimable"; |
| 234 | case LIMINE_MEMMAP_ACPI_NVS: |
| 235 | return "ACPI NVS"; |
| 236 | case LIMINE_MEMMAP_BAD_MEMORY: |
| 237 | return "Bad memory"; |
| 238 | case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: |
| 239 | return "Bootloader reclaimable"; |
| 240 | case LIMINE_MEMMAP_EXECUTABLE_AND_MODULES: |
| 241 | return "Executable and modules"; |
| 242 | case LIMINE_MEMMAP_FRAMEBUFFER: |
| 243 | return "Framebuffer"; |
| 244 | default: |
| 245 | return "???"; |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | static char *firmware_type_str(uint64_t t) { |
| 250 | switch (t) { |
| 251 | case LIMINE_FIRMWARE_TYPE_X86BIOS: |
| 252 | return "x86 BIOS"; |
| 253 | case LIMINE_FIRMWARE_TYPE_EFI32: |
| 254 | return "32-bit EFI"; |
| 255 | case LIMINE_FIRMWARE_TYPE_EFI64: |
| 256 | return "64-bit EFI"; |
| 257 | default: |
| 258 | return "???"; |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | static void print_file(struct limine_file *file) { |
| 263 | e9_printf("File->Revision: %d", file->revision); |
| 264 | e9_printf("File->Address: %x", file->address); |
| 265 | e9_printf("File->Size: %x", file->size); |
| 266 | e9_printf("File->Path: %s", file->path); |
| 267 | e9_printf("File->String: %s", file->string); |
| 268 | e9_printf("File->MediaType: %d", file->media_type); |
| 269 | e9_printf("File->PartIndex: %d", file->partition_index); |
| 270 | e9_printf("File->TFTPIP: %d.%d.%d.%d", |
| 271 | (file->tftp_ip & (0xff << 0)) >> 0, |
| 272 | (file->tftp_ip & (0xff << 8)) >> 8, |
| 273 | (file->tftp_ip & (0xff << 16)) >> 16, |
| 274 | (file->tftp_ip & (0xff << 24)) >> 24); |
| 275 | e9_printf("File->TFTPPort: %d", file->tftp_port); |
| 276 | e9_printf("File->MBRDiskId: %x", file->mbr_disk_id); |
| 277 | e9_printf("File->GPTDiskUUID: %x-%x-%x-%x", |
| 278 | file->gpt_disk_uuid.a, |
| 279 | file->gpt_disk_uuid.b, |
| 280 | file->gpt_disk_uuid.c, |
| 281 | *(uint64_t *)file->gpt_disk_uuid.d); |
| 282 | e9_printf("File->GPTPartUUID: %x-%x-%x-%x", |
| 283 | file->gpt_part_uuid.a, |
| 284 | file->gpt_part_uuid.b, |
| 285 | file->gpt_part_uuid.c, |
| 286 | *(uint64_t *)file->gpt_part_uuid.d); |
| 287 | e9_printf("File->PartUUID: %x-%x-%x-%x", |
| 288 | file->part_uuid.a, |
| 289 | file->part_uuid.b, |
| 290 | file->part_uuid.c, |
| 291 | *(uint64_t *)file->part_uuid.d); |
| 292 | } |
| 293 | |
| 294 | uint32_t ctr = 0; |
| 295 | |
| 296 | void ap_entry(struct limine_mp_info *info) { |
| 297 | e9_printf("Hello from AP!"); |
| 298 | |
| 299 | #if defined (__x86_64__) |
| 300 | e9_printf("My LAPIC ID: %x", info->lapic_id); |
| 301 | #elif defined (__aarch64__) |
| 302 | e9_printf("My MPIDR: %x", info->mpidr); |
| 303 | #elif defined (__riscv) |
| 304 | e9_printf("My Hart ID: %x", info->hartid); |
| 305 | #elif defined (__loongarch__) |
| 306 | e9_printf("My Phys ID: %x", info->phys_id); |
| 307 | #endif |
| 308 | |
| 309 | __atomic_fetch_add(&ctr, 1, __ATOMIC_SEQ_CST); |
| 310 | |
| 311 | while (1); |
| 312 | } |
| 313 | |
| 314 | #define FEAT_START do { |
| 315 | #define FEAT_END } while (0); |
| 316 | |
| 317 | extern char executable_start[]; |
| 318 | |
| 319 | struct flanterm_context *ft_ctx = NULL; |
| 320 | |
| 321 | static uint8_t alloc_pool[16 * 1024 * 1024]; |
| 322 | static size_t alloc_off = 0; |
| 323 | |
| 324 | static void *simple_malloc(size_t size) { |
| 325 | size = (size + 15) & ~(size_t)15; |
| 326 | if (alloc_off + size > sizeof(alloc_pool)) { |
| 327 | return NULL; |
| 328 | } |
| 329 | void *p = &alloc_pool[alloc_off]; |
| 330 | alloc_off += size; |
| 331 | return p; |
| 332 | } |
| 333 | |
| 334 | static void simple_free(void *ptr, size_t size) { |
| 335 | (void)ptr; |
| 336 | (void)size; |
| 337 | } |
| 338 | |
| 339 | static void limine_main(void) { |
| 340 | e9_printf("\nWe're alive"); |
| 341 | |
| 342 | if (LIMINE_LOADED_BASE_REVISION_VALID(limine_base_revision) == true) { |
| 343 | e9_printf("Bootloader has loaded us using base revision %d", |
| 344 | LIMINE_LOADED_BASE_REVISION(limine_base_revision)); |
| 345 | } |
| 346 | |
| 347 | if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) { |
| 348 | e9_printf("Limine base revision not supported"); |
| 349 | for (;;); |
| 350 | } |
| 351 | |
| 352 | e9_printf(""); |
| 353 | |
| 354 | struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0]; |
| 355 | |
| 356 | struct limine_flanterm_fb_init_params *fip = NULL; |
| 357 | if (fip_request.response != NULL && fip_request.response->entry_count > 0) { |
| 358 | fip = fip_request.response->entries[0]; |
| 359 | } |
| 360 | |
| 361 | if (fip != NULL) { |
| 362 | ft_ctx = flanterm_fb_init( |
| 363 | simple_malloc, |
| 364 | simple_free, |
| 365 | fb->address, fb->width, fb->height, fb->pitch, |
| 366 | fb->red_mask_size, fb->red_mask_shift, |
| 367 | fb->green_mask_size, fb->green_mask_shift, |
| 368 | fb->blue_mask_size, fb->blue_mask_shift, |
| 369 | fip->canvas, |
| 370 | fip->ansi_colours, fip->ansi_bright_colours, |
| 371 | &fip->default_bg, &fip->default_fg, |
| 372 | &fip->default_bg_bright, &fip->default_fg_bright, |
| 373 | fip->font, fip->font_width, fip->font_height, fip->font_spacing, |
| 374 | fip->font_scale_x, fip->font_scale_y, |
| 375 | fip->margin, |
| 376 | fip->rotation |
| 377 | ); |
| 378 | } else { |
| 379 | ft_ctx = flanterm_fb_init( |
| 380 | NULL, |
| 381 | NULL, |
| 382 | fb->address, fb->width, fb->height, fb->pitch, |
| 383 | fb->red_mask_size, fb->red_mask_shift, |
| 384 | fb->green_mask_size, fb->green_mask_shift, |
| 385 | fb->blue_mask_size, fb->blue_mask_shift, |
| 386 | NULL, |
| 387 | NULL, NULL, |
| 388 | NULL, NULL, |
| 389 | NULL, NULL, |
| 390 | NULL, 0, 0, 1, |
| 391 | 0, 0, |
| 392 | 0, |
| 393 | FLANTERM_FB_ROTATE_0 |
| 394 | ); |
| 395 | } |
| 396 | |
| 397 | uint64_t executable_slide = (uint64_t)executable_start - 0xffffffff80000000; |
| 398 | |
| 399 | e9_printf("Executable start: %x", executable_start); |
| 400 | e9_printf("Executable slide: %x", executable_slide); |
| 401 | |
| 402 | FEAT_START |
| 403 | e9_printf(""); |
| 404 | if (bootloader_info_request.response == NULL) { |
| 405 | e9_printf("Bootloader info not passed"); |
| 406 | break; |
| 407 | } |
| 408 | struct limine_bootloader_info_response *bootloader_info_response = bootloader_info_request.response; |
| 409 | e9_printf("Bootloader info feature, revision %d", bootloader_info_response->revision); |
| 410 | e9_printf("Bootloader name: %s", bootloader_info_response->name); |
| 411 | e9_printf("Bootloader version: %s", bootloader_info_response->version); |
| 412 | FEAT_END |
| 413 | |
| 414 | FEAT_START |
| 415 | e9_printf(""); |
| 416 | if (executable_cmdline_request.response == NULL) { |
| 417 | e9_printf("Executable command line not passed"); |
| 418 | break; |
| 419 | } |
| 420 | struct limine_executable_cmdline_response *executable_cmdline_response = executable_cmdline_request.response; |
| 421 | e9_printf("Executable command line feature, revision %d", executable_cmdline_response->revision); |
| 422 | e9_printf("Command line: %s", executable_cmdline_response->cmdline); |
| 423 | FEAT_END |
| 424 | |
| 425 | FEAT_START |
| 426 | e9_printf(""); |
| 427 | if (firmware_type_request.response == NULL) { |
| 428 | e9_printf("Firmware type not passed"); |
| 429 | break; |
| 430 | } |
| 431 | struct limine_firmware_type_response *firmware_type_response = firmware_type_request.response; |
| 432 | e9_printf("Firmware type feature, revision %d", firmware_type_response->revision); |
| 433 | e9_printf("Firmware type: %s", firmware_type_str(firmware_type_response->firmware_type)); |
| 434 | FEAT_END |
| 435 | |
| 436 | FEAT_START |
| 437 | e9_printf(""); |
| 438 | if (executable_address_request.response == NULL) { |
| 439 | e9_printf("Executable address not passed"); |
| 440 | break; |
| 441 | } |
| 442 | struct limine_executable_address_response *exec_addr_response = executable_address_request.response; |
| 443 | e9_printf("Executable address feature, revision %d", exec_addr_response->revision); |
| 444 | e9_printf("Physical base: %x", exec_addr_response->physical_base); |
| 445 | e9_printf("Virtual base: %x", exec_addr_response->virtual_base); |
| 446 | FEAT_END |
| 447 | |
| 448 | FEAT_START |
| 449 | e9_printf(""); |
| 450 | if (hhdm_request.response == NULL) { |
| 451 | e9_printf("HHDM not passed"); |
| 452 | break; |
| 453 | } |
| 454 | struct limine_hhdm_response *hhdm_response = hhdm_request.response; |
| 455 | e9_printf("HHDM feature, revision %d", hhdm_response->revision); |
| 456 | e9_printf("Higher half direct map at: %x", hhdm_response->offset); |
| 457 | FEAT_END |
| 458 | |
| 459 | FEAT_START |
| 460 | e9_printf(""); |
| 461 | if (memmap_request.response == NULL) { |
| 462 | e9_printf("Memory map not passed"); |
| 463 | break; |
| 464 | } |
| 465 | struct limine_memmap_response *memmap_response = memmap_request.response; |
| 466 | e9_printf("Memory map feature, revision %d", memmap_response->revision); |
| 467 | e9_printf("%d memory map entries", memmap_response->entry_count); |
| 468 | for (size_t i = 0; i < memmap_response->entry_count; i++) { |
| 469 | struct limine_memmap_entry *e = memmap_response->entries[i]; |
| 470 | e9_printf("%x->%x %s", e->base, e->base + e->length, get_memmap_type(e->type)); |
| 471 | } |
| 472 | FEAT_END |
| 473 | |
| 474 | FEAT_START |
| 475 | e9_printf(""); |
| 476 | if (framebuffer_request.response == NULL) { |
| 477 | e9_printf("Framebuffer not passed"); |
| 478 | break; |
| 479 | } |
| 480 | struct limine_framebuffer_response *fb_response = framebuffer_request.response; |
| 481 | e9_printf("Framebuffers feature, revision %d", fb_response->revision); |
| 482 | e9_printf("%d framebuffer(s)", fb_response->framebuffer_count); |
| 483 | for (size_t i = 0; i < fb_response->framebuffer_count; i++) { |
| 484 | struct limine_framebuffer *fb = fb_response->framebuffers[i]; |
| 485 | e9_printf("Address: %x", fb->address); |
| 486 | e9_printf("Width: %d", fb->width); |
| 487 | e9_printf("Height: %d", fb->height); |
| 488 | e9_printf("Pitch: %d", fb->pitch); |
| 489 | e9_printf("BPP: %d", fb->bpp); |
| 490 | e9_printf("Memory model: %d", fb->memory_model); |
| 491 | e9_printf("Red mask size: %d", fb->red_mask_size); |
| 492 | e9_printf("Red mask shift: %d", fb->red_mask_shift); |
| 493 | e9_printf("Green mask size: %d", fb->green_mask_size); |
| 494 | e9_printf("Green mask shift: %d", fb->green_mask_shift); |
| 495 | e9_printf("Blue mask size: %d", fb->blue_mask_size); |
| 496 | e9_printf("Blue mask shift: %d", fb->blue_mask_shift); |
| 497 | e9_printf("EDID size: %d", fb->edid_size); |
| 498 | e9_printf("EDID at: %x", fb->edid); |
| 499 | e9_printf("Video modes:"); |
| 500 | for (size_t j = 0; j < fb->mode_count; j++) { |
| 501 | e9_printf(" %dx%dx%d", fb->modes[j]->width, fb->modes[j]->height, fb->modes[j]->bpp); |
| 502 | } |
| 503 | } |
| 504 | FEAT_END |
| 505 | |
| 506 | FEAT_START |
| 507 | e9_printf(""); |
| 508 | if (fip_request.response == NULL) { |
| 509 | e9_printf("Flanterm FB init params not passed"); |
| 510 | break; |
| 511 | } |
| 512 | struct limine_flanterm_fb_init_params_response *fip_response = fip_request.response; |
| 513 | e9_printf("Flanterm FB init params feature, revision %d", fip_response->revision); |
| 514 | e9_printf("%d entry/entries", fip_response->entry_count); |
| 515 | for (size_t i = 0; i < fip_response->entry_count; i++) { |
| 516 | struct limine_flanterm_fb_init_params *p = fip_response->entries[i]; |
| 517 | e9_printf("--- Entry %d ---", i); |
| 518 | e9_printf("Canvas: %x (size: %x)", p->canvas, p->canvas_size); |
| 519 | e9_printf("Default BG: %x, FG: %x", p->default_bg, p->default_fg); |
| 520 | e9_printf("Default BG bright: %x, FG bright: %x", p->default_bg_bright, p->default_fg_bright); |
| 521 | e9_printf("ANSI colours: %x %x %x %x %x %x %x %x", |
| 522 | p->ansi_colours[0], p->ansi_colours[1], p->ansi_colours[2], p->ansi_colours[3], |
| 523 | p->ansi_colours[4], p->ansi_colours[5], p->ansi_colours[6], p->ansi_colours[7]); |
| 524 | e9_printf("ANSI bright: %x %x %x %x %x %x %x %x", |
| 525 | p->ansi_bright_colours[0], p->ansi_bright_colours[1], p->ansi_bright_colours[2], p->ansi_bright_colours[3], |
| 526 | p->ansi_bright_colours[4], p->ansi_bright_colours[5], p->ansi_bright_colours[6], p->ansi_bright_colours[7]); |
| 527 | e9_printf("Font: %x (%dx%d)", p->font, p->font_width, p->font_height); |
| 528 | e9_printf("Font spacing: %d, scale: %dx%d", p->font_spacing, p->font_scale_x, p->font_scale_y); |
| 529 | e9_printf("Margin: %d, Rotation: %d", p->margin, p->rotation); |
| 530 | } |
| 531 | FEAT_END |
| 532 | |
| 533 | FEAT_START |
| 534 | e9_printf(""); |
| 535 | if (exec_file_request.response == NULL) { |
| 536 | e9_printf("Executable file not passed"); |
| 537 | break; |
| 538 | } |
| 539 | struct limine_executable_file_response *exec_file_response = exec_file_request.response; |
| 540 | e9_printf("Executable file feature, revision %d", exec_file_response->revision); |
| 541 | print_file(exec_file_response->executable_file); |
| 542 | FEAT_END |
| 543 | |
| 544 | FEAT_START |
| 545 | e9_printf(""); |
| 546 | if (module_request.response == NULL) { |
| 547 | e9_printf("Modules not passed"); |
| 548 | break; |
| 549 | } |
| 550 | struct limine_module_response *module_response = module_request.response; |
| 551 | e9_printf("Modules feature, revision %d", module_response->revision); |
| 552 | e9_printf("%d module(s)", module_response->module_count); |
| 553 | for (size_t i = 0; i < module_response->module_count; i++) { |
| 554 | struct limine_file *f = module_response->modules[i]; |
| 555 | e9_printf("---"); |
| 556 | print_file(f); |
| 557 | } |
| 558 | |
| 559 | #ifdef ENABLE_GZIP_TEST |
| 560 | /* Gzip decompression test: compare internal_module3 (plain limine.conf) |
| 561 | against internal_module4 (limine.conf.gz, decompressed by bootloader). */ |
| 562 | { |
| 563 | struct limine_file *plain = NULL, *decompressed = NULL; |
| 564 | for (size_t i = 0; i < module_response->module_count; i++) { |
| 565 | struct limine_file *f = module_response->modules[i]; |
| 566 | if (f->string != NULL) { |
| 567 | /* Match by the module string we assigned. */ |
| 568 | bool is_third = f->string[0] == 'T' && f->string[1] == 'h' |
| 569 | && f->string[2] == 'i' && f->string[3] == 'r' |
| 570 | && f->string[4] == 'd'; |
| 571 | bool is_gz = f->string[0] == 'g' && f->string[1] == 'z'; |
| 572 | if (is_third) plain = f; |
| 573 | if (is_gz) decompressed = f; |
| 574 | } |
| 575 | } |
| 576 | if (plain == NULL) { |
| 577 | e9_printf("gzip: FAIL (plain module not found)"); |
| 578 | } else if (decompressed == NULL) { |
| 579 | e9_printf("gzip: FAIL (decompressed module not found)"); |
| 580 | } else if (plain->size != decompressed->size) { |
| 581 | e9_printf("gzip: FAIL (size mismatch: plain=%x, decompressed=%x)", |
| 582 | plain->size, decompressed->size); |
| 583 | } else if (memcmp(plain->address, decompressed->address, plain->size) != 0) { |
| 584 | e9_printf("gzip: FAIL (content mismatch, size=%x)", plain->size); |
| 585 | } else { |
| 586 | e9_printf("gzip: pass (size=%x)", plain->size); |
| 587 | } |
| 588 | } |
| 589 | #endif |
| 590 | FEAT_END |
| 591 | |
| 592 | FEAT_START |
| 593 | e9_printf(""); |
| 594 | if (rsdp_request.response == NULL) { |
| 595 | e9_printf("RSDP not passed"); |
| 596 | break; |
| 597 | } |
| 598 | struct limine_rsdp_response *rsdp_response = rsdp_request.response; |
| 599 | e9_printf("RSDP feature, revision %d", rsdp_response->revision); |
| 600 | e9_printf("RSDP at: %x", rsdp_response->address); |
| 601 | FEAT_END |
| 602 | |
| 603 | FEAT_START |
| 604 | e9_printf(""); |
| 605 | if (smbios_request.response == NULL) { |
| 606 | e9_printf("SMBIOS not passed"); |
| 607 | break; |
| 608 | } |
| 609 | struct limine_smbios_response *smbios_response = smbios_request.response; |
| 610 | e9_printf("SMBIOS feature, revision %d", smbios_response->revision); |
| 611 | e9_printf("SMBIOS 32-bit entry at: %x", smbios_response->entry_32); |
| 612 | e9_printf("SMBIOS 64-bit entry at: %x", smbios_response->entry_64); |
| 613 | FEAT_END |
| 614 | |
| 615 | FEAT_START |
| 616 | e9_printf(""); |
| 617 | if (est_request.response == NULL) { |
| 618 | e9_printf("EFI system table not passed"); |
| 619 | break; |
| 620 | } |
| 621 | struct limine_efi_system_table_response *est_response = est_request.response; |
| 622 | e9_printf("EFI system table feature, revision %d", est_response->revision); |
| 623 | e9_printf("EFI system table at: %x", est_response->address); |
| 624 | FEAT_END |
| 625 | |
| 626 | FEAT_START |
| 627 | e9_printf(""); |
| 628 | if (tpm_event_log_request.response == NULL) { |
| 629 | e9_printf("TPM event log not passed"); |
| 630 | break; |
| 631 | } |
| 632 | struct limine_tpm_event_log_response *tpm_event_log_response = tpm_event_log_request.response; |
| 633 | e9_printf("TPM event log feature, revision %d", tpm_event_log_response->revision); |
| 634 | e9_printf("Format: %d (TCG_%s)", tpm_event_log_response->format, |
| 635 | tpm_event_log_response->format == LIMINE_TPM_EVENT_LOG_FORMAT_TCG_2 ? "2" : "1.2"); |
| 636 | e9_printf("Size: %x bytes", tpm_event_log_response->size); |
| 637 | e9_printf("Address: %x", tpm_event_log_response->address); |
| 638 | FEAT_END |
| 639 | |
| 640 | FEAT_START |
| 641 | e9_printf(""); |
| 642 | if (efi_memmap_request.response == NULL) { |
| 643 | e9_printf("EFI memory map not passed"); |
| 644 | break; |
| 645 | } |
| 646 | struct limine_efi_memmap_response *efi_memmap_response = efi_memmap_request.response; |
| 647 | e9_printf("EFI memory map feature, revision %d", efi_memmap_response->revision); |
| 648 | e9_printf("EFI memory map at: %x", efi_memmap_response->memmap); |
| 649 | e9_printf("EFI memory map size: %x", efi_memmap_response->memmap_size); |
| 650 | e9_printf("EFI memory descriptor size: %x", efi_memmap_response->desc_size); |
| 651 | e9_printf("EFI memory descriptor version: %d", efi_memmap_response->desc_version); |
| 652 | FEAT_END |
| 653 | |
| 654 | FEAT_START |
| 655 | e9_printf(""); |
| 656 | if (date_at_boot_request.response == NULL) { |
| 657 | e9_printf("Boot time not passed"); |
| 658 | break; |
| 659 | } |
| 660 | struct limine_date_at_boot_response *date_at_boot_response = date_at_boot_request.response; |
| 661 | e9_printf("Date at boot feature, revision %d", date_at_boot_response->revision); |
| 662 | e9_printf("Timestamp: %d", date_at_boot_response->timestamp); |
| 663 | FEAT_END |
| 664 | |
| 665 | FEAT_START |
| 666 | e9_printf(""); |
| 667 | if (_mp_request.response == NULL) { |
| 668 | e9_printf("MP info not passed"); |
| 669 | break; |
| 670 | } |
| 671 | struct limine_mp_response *mp_response = _mp_request.response; |
| 672 | e9_printf("MP feature, revision %d", mp_response->revision); |
| 673 | e9_printf("Flags: %x", mp_response->flags); |
| 674 | #if defined (__x86_64__) |
| 675 | e9_printf("BSP LAPIC ID: %x", mp_response->bsp_lapic_id); |
| 676 | #elif defined (__aarch64__) |
| 677 | e9_printf("BSP MPIDR: %x", mp_response->bsp_mpidr); |
| 678 | #elif defined (__riscv) |
| 679 | e9_printf("BSP Hart ID: %x", mp_response->bsp_hartid); |
| 680 | #elif defined (__loongarch__) |
| 681 | e9_printf("BSP Phys ID: %x", mp_response->bsp_phys_id); |
| 682 | #endif |
| 683 | e9_printf("CPU count: %d", mp_response->cpu_count); |
| 684 | for (size_t i = 0; i < mp_response->cpu_count; i++) { |
| 685 | struct limine_mp_info *cpu = mp_response->cpus[i]; |
| 686 | e9_printf("Processor ID: %x", cpu->processor_id); |
| 687 | #if defined (__x86_64__) |
| 688 | e9_printf("LAPIC ID: %x", cpu->lapic_id); |
| 689 | #elif defined (__aarch64__) |
| 690 | e9_printf("MPIDR: %x", cpu->mpidr); |
| 691 | #elif defined (__riscv) |
| 692 | e9_printf("Hart ID: %x", cpu->hartid); |
| 693 | #elif defined (__loongarch__) |
| 694 | e9_printf("Phys ID: %x", cpu->phys_id); |
| 695 | #endif |
| 696 | |
| 697 | |
| 698 | #if defined (__x86_64__) |
| 699 | if (cpu->lapic_id != mp_response->bsp_lapic_id) { |
| 700 | #elif defined (__aarch64__) |
| 701 | if (cpu->mpidr != mp_response->bsp_mpidr) { |
| 702 | #elif defined (__riscv) |
| 703 | if (cpu->hartid != mp_response->bsp_hartid) { |
| 704 | #elif defined (__loongarch__) |
| 705 | if (cpu->phys_id != mp_response->bsp_phys_id) { |
| 706 | #endif |
| 707 | uint32_t old_ctr = __atomic_load_n(&ctr, __ATOMIC_SEQ_CST); |
| 708 | |
| 709 | __atomic_store_n(&cpu->goto_address, ap_entry, __ATOMIC_SEQ_CST); |
| 710 | |
| 711 | while (__atomic_load_n(&ctr, __ATOMIC_SEQ_CST) == old_ctr) |
| 712 | ; |
| 713 | } |
| 714 | } |
| 715 | FEAT_END |
| 716 | |
| 717 | FEAT_START |
| 718 | e9_printf(""); |
| 719 | if (_dtb_request.response == NULL) { |
| 720 | e9_printf("Device tree blob not passed"); |
| 721 | break; |
| 722 | } |
| 723 | struct limine_dtb_response *dtb_response = _dtb_request.response; |
| 724 | e9_printf("Device tree blob feature, revision %d", dtb_response->revision); |
| 725 | e9_printf("Device tree blob pointer: %x", dtb_response->dtb_ptr); |
| 726 | uint32_t dtb_magic = *(uint32_t*)dtb_response->dtb_ptr; |
| 727 | e9_printf("Device tree header magic: %x", dtb_magic); |
| 728 | FEAT_END |
| 729 | |
| 730 | FEAT_START |
| 731 | e9_printf(""); |
| 732 | if (_pm_request.response == NULL) { |
| 733 | e9_printf("Paging mode not passed"); |
| 734 | break; |
| 735 | } |
| 736 | struct limine_paging_mode_response *pm_response = _pm_request.response; |
| 737 | e9_printf("Paging mode feature, revision %d", pm_response->revision); |
| 738 | e9_printf(" mode: %d", pm_response->mode); |
| 739 | FEAT_END |
| 740 | |
| 741 | #if defined (__riscv) |
| 742 | FEAT_START |
| 743 | e9_printf(""); |
| 744 | struct limine_riscv_bsp_hartid_response *bsp_response = _bsp_request.response; |
| 745 | if (bsp_response == NULL) { |
| 746 | e9_printf("RISC-V BSP Hart ID was not passed"); |
| 747 | break; |
| 748 | } |
| 749 | e9_printf("RISC-V BSP Hart ID: %x", bsp_response->bsp_hartid); |
| 750 | FEAT_END |
| 751 | #endif |
| 752 | |
| 753 | FEAT_START |
| 754 | e9_printf(""); |
| 755 | struct limine_tsc_frequency_response *tsc_freq_response = tsc_freq_request.response; |
| 756 | if (tsc_freq_response == NULL) { |
| 757 | e9_printf("TSC frequency not passed"); |
| 758 | break; |
| 759 | } |
| 760 | e9_printf("TSC frequency feature, revision %d", tsc_freq_response->revision); |
| 761 | e9_printf("Frequency: %d Hz", tsc_freq_response->frequency); |
| 762 | FEAT_END |
| 763 | |
| 764 | FEAT_START |
| 765 | e9_printf(""); |
| 766 | struct limine_bootloader_performance_response *perf_response = _perf_request.response; |
| 767 | if (perf_response == NULL) { |
| 768 | e9_printf("Bootloader performance not passed"); |
| 769 | break; |
| 770 | } |
| 771 | e9_printf("Bootloader performance feature, revision %d", perf_response->revision); |
| 772 | e9_printf("Reset time: %d usec", perf_response->reset_usec); |
| 773 | e9_printf("Init time: %d usec", perf_response->init_usec); |
| 774 | e9_printf("Exec time: %d usec", perf_response->exec_usec); |
| 775 | FEAT_END |
| 776 | |
| 777 | #ifdef ENABLE_QEMU_SHUTDOWN |
| 778 | outw(0x604, 0x2000); /* QEMU-specific shutdown, used by automated tests. */ |
| 779 | #endif |
| 780 | for (;;); |
| 781 | } |