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
biosmemdisk_detect(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