1 /*- 2 * Copyright (c) 2020 Richard Russo <russor@ruka.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 /* 8 * Source of information: https://repo.or.cz/syslinux.git 9 * 10 * Implements the MEMDISK protocol from syslinux, found in doc/memdisk.txt 11 * (search MEMDISK info structure). Since we validate the pointer to the mBFT, a 12 * minimum version of 3.85 is needed. Note: All this could be done in the 13 * kernel, since we don't have hooks to use this inside the boot loader. The 14 * details of these structures can be found in memdisk/memdisk.inc (search 15 * for mBFT). 16 * 17 * The kernel could just grab the mBFT table, but instead relies on us finding 18 * it and setting the right env variables. 19 */ 20 #include <stand.h> 21 #include <machine/stdarg.h> 22 #include <bootstrap.h> 23 #include <btxv86.h> 24 #include "libi386.h" 25 26 #include "platform/acfreebsd.h" 27 #include "acconfig.h" 28 #define ACPI_SYSTEM_XFACE 29 #include "actypes.h" 30 #include "actbl.h" 31 32 struct memdisk_info { 33 uint32_t mdi_13h_hook_ptr; /* not included in mdi_length! */ 34 uint16_t mdi_length; 35 uint8_t mdi_minor; 36 uint8_t mdi_major; 37 uint32_t mdi_disk_ptr; 38 uint32_t mdi_disk_sectors; 39 uint32_t mdi_far_ptr_cmdline; 40 uint32_t mdi_old_int13h; 41 uint32_t mdi_old_int15h; 42 uint16_t mdi_dos_mem_before; 43 uint8_t mdi_boot_loader_id; 44 uint8_t mdi_sector_size; /* Code below assumes this is last */ 45 } __attribute__((packed)); 46 47 struct safe_13h_hook { 48 char sh_jmp[3]; 49 char sh_id[8]; 50 char sh_vendor[8]; 51 uint16_t sh_next_offset; 52 uint16_t sh_next_segment; 53 uint32_t sh_flags; 54 uint32_t sh_mbft; 55 } __attribute__((packed)); 56 57 /* 58 * Maximum length of INT 13 entries we'll chase. Real disks are on this list, 59 * potentially, so we may have to look through them to find the memdisk. 60 */ 61 #define MEMDISK_MAX 32 62 63 /* 64 * Scan for MEMDISK virtual block devices 65 */ 66 void 67 biosmemdisk_detect(void) 68 { 69 char line[80], scratch[80]; 70 int hook = 0, count = 0, sector_size; 71 uint16_t segment, offset; 72 struct safe_13h_hook *probe; 73 ACPI_TABLE_HEADER *mbft; 74 uint8_t *cp, sum; 75 struct memdisk_info *mdi; 76 77 /* 78 * Walk through the int13 handler linked list, looking for possible 79 * MEMDISKs. 80 * 81 * The max is arbitrary to ensure termination. 82 */ 83 offset = *(uint16_t *)PTOV(0x13 * 4); 84 segment = *(uint16_t *)PTOV(0x13 * 4 + 2); 85 while (hook < MEMDISK_MAX && !(segment == 0 && offset == 0)) { 86 /* 87 * Walk the linked list, making sure each node has the right 88 * signature and only looking at MEMDISK nodes. 89 */ 90 probe = (struct safe_13h_hook *)PTOV(segment * 16 + offset); 91 if (memcmp(probe->sh_id, "$INT13SF", sizeof(probe->sh_id)) != 0) { 92 printf("Found int 13h unsafe hook at %p (%x:%x)\n", 93 probe, segment, offset); 94 break; 95 } 96 if (memcmp(probe->sh_vendor, "MEMDISK ", sizeof(probe->sh_vendor)) != 0) 97 goto end_of_loop; 98 99 /* 100 * If it is a memdisk, make sure the mBFT signature is correct 101 * and its checksum is right. 102 */ 103 mbft = (ACPI_TABLE_HEADER *)PTOV(probe->sh_mbft); 104 if (memcmp(mbft->Signature, "mBFT", sizeof(mbft->Signature)) != 0) 105 goto end_of_loop; 106 sum = 0; 107 cp = (uint8_t *)mbft; 108 for (int idx = 0; idx < mbft->Length; ++idx) 109 sum += *(cp + idx); 110 if (sum != 0) 111 goto end_of_loop; 112 113 /* 114 * The memdisk info follows the ACPI_TABLE_HEADER in the mBFT 115 * section. If the sector size is present and non-zero use it 116 * otherwise assume 512. 117 */ 118 mdi = (struct memdisk_info *)PTOV(probe->sh_mbft + sizeof(*mbft)); 119 sector_size = 512; 120 if (mdi->mdi_length + sizeof(mdi->mdi_13h_hook_ptr) >= sizeof(*mdi) && 121 mdi->mdi_sector_size != 0) 122 sector_size = 1 << mdi->mdi_sector_size; 123 124 printf("memdisk %d.%d disk at %#x (%d sectors = %d bytes)\n", 125 mdi->mdi_major, mdi->mdi_minor, mdi->mdi_disk_ptr, 126 mdi->mdi_disk_sectors, mdi->mdi_disk_sectors * sector_size); 127 128 snprintf(line, sizeof(line), "hint.md.%d.physaddr", count); 129 snprintf(scratch, sizeof(scratch), "0x%08x", mdi->mdi_disk_ptr); 130 setenv(line, scratch, 1); 131 snprintf(line, sizeof(line), "hint.md.%d.len", count); 132 snprintf(scratch, sizeof(scratch), "%d", mdi->mdi_disk_sectors * sector_size); 133 setenv(line, scratch, 1); 134 count++; 135 end_of_loop: 136 hook++; 137 offset = probe->sh_next_offset; 138 segment = probe->sh_next_segment; 139 } 140 } 141