xref: /freebsd/stand/i386/libi386/biosmemdisk.c (revision 15925062e1ba75cb4908a68655b797870187ea57)
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