The branch main has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=15925062e1ba75cb4908a68655b797870187ea57
commit 15925062e1ba75cb4908a68655b797870187ea57 Author: Richard Russo <rus...@ruka.org> AuthorDate: 2025-07-23 00:08:31 +0000 Commit: Warner Losh <i...@freebsd.org> CommitDate: 2025-07-23 00:10:25 +0000 biosboot: Detect memory disks from PXE Walk through the disk driver entries chained off of INT13. MEMDISK is part of the Syslinux project; it loads disk images into memory, sets an int 13h hook and then does a BIOS boot from the image; this can be used as part of a PXE boot environment to load installer disks, however the disks are not accessible from inside the FreeBSD kernel because it doesn't access disks through BIOS APIs. This patch detects the disk images in the loader, and passes their address and length as a driver hint. When the md driver sees the hint, it maps the image, and presents it to the system. (rebased and reworked from https://reviews.freebsd.org/D27349) Feedback from: kib, bapt, olce Differential Revision: https://reviews.freebsd.org/D45404 --- stand/i386/libi386/Makefile | 1 + stand/i386/libi386/biosmemdisk.c | 140 +++++++++++++++++++++++++++++++++++++++ stand/i386/libi386/libi386.h | 2 + stand/i386/loader/main.c | 3 + sys/dev/md/md.c | 23 +++++++ 5 files changed, 169 insertions(+) diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile index 038557c6a826..7205d3a61988 100644 --- a/stand/i386/libi386/Makefile +++ b/stand/i386/libi386/Makefile @@ -7,6 +7,7 @@ SRCS+= bio.c SRCS+= biosacpi.c SRCS+= biosdisk.c SRCS+= biosmem.c +SRCS+= biosmemdisk.c SRCS+= biospci.c SRCS+= biospnp.c SRCS+= biossmap.c diff --git a/stand/i386/libi386/biosmemdisk.c b/stand/i386/libi386/biosmemdisk.c new file mode 100644 index 000000000000..208ae289950a --- /dev/null +++ b/stand/i386/libi386/biosmemdisk.c @@ -0,0 +1,140 @@ +/*- + * Copyright (c) 2020 Richard Russo <rus...@ruka.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * Source of information: https://repo.or.cz/syslinux.git + * + * Implements the MEMDISK protocol from syslinux, found in doc/memdisk.txt + * (search MEMDISK info structure). Since we validate the pointer to the mBFT, a + * minimum version of 3.85 is needed. Note: All this could be done in the + * kernel, since we don't have hooks to use this inside the boot loader. The + * details of these structures can be found in memdisk/memdisk.inc (search + * for mBFT). + * + * The kernel could just grab the mBFT table, but instead relies on us finding + * it and setting the right env variables. + */ +#include <stand.h> +#include <machine/stdarg.h> +#include <bootstrap.h> +#include <btxv86.h> +#include "libi386.h" + +#include "platform/acfreebsd.h" +#include "acconfig.h" +#define ACPI_SYSTEM_XFACE +#include "actypes.h" +#include "actbl.h" + +struct memdisk_info { + uint32_t mdi_13h_hook_ptr; /* not included in mdi_length! */ + uint16_t mdi_length; + uint8_t mdi_minor; + uint8_t mdi_major; + uint32_t mdi_disk_ptr; + uint32_t mdi_disk_sectors; + uint32_t mdi_far_ptr_cmdline; + uint32_t mdi_old_int13h; + uint32_t mdi_old_int15h; + uint16_t mdi_dos_mem_before; + uint8_t mdi_boot_loader_id; + uint8_t mdi_sector_size; /* Code below assumes this is last */ +} __attribute__((packed)); + +struct safe_13h_hook { + char sh_jmp[3]; + char sh_id[8]; + char sh_vendor[8]; + uint16_t sh_next_offset; + uint16_t sh_next_segment; + uint32_t sh_flags; + uint32_t sh_mbft; +} __attribute__((packed)); + +/* + * Maximum length of INT 13 entries we'll chase. Real disks are on this list, + * potentially, so we may have to look through them to find the memdisk. + */ +#define MEMDISK_MAX 32 + +/* + * Scan for MEMDISK virtual block devices + */ +void +biosmemdisk_detect(void) +{ + char line[80], scratch[80]; + int hook = 0, count = 0, sector_size; + uint16_t segment, offset; + struct safe_13h_hook *probe; + ACPI_TABLE_HEADER *mbft; + uint8_t *cp, sum; + struct memdisk_info *mdi; + + /* + * Walk through the int13 handler linked list, looking for possible + * MEMDISKs. + * + * The max is arbitrary to ensure termination. + */ + offset = *(uint16_t *)PTOV(0x13 * 4); + segment = *(uint16_t *)PTOV(0x13 * 4 + 2); + while (hook < MEMDISK_MAX && !(segment == 0 && offset == 0)) { + /* + * Walk the linked list, making sure each node has the right + * signature and only looking at MEMDISK nodes. + */ + probe = (struct safe_13h_hook *)PTOV(segment * 16 + offset); + if (memcmp(probe->sh_id, "$INT13SF", sizeof(probe->sh_id)) != 0) { + printf("Found int 13h unsafe hook at %p (%x:%x)\n", + probe, segment, offset); + break; + } + if (memcmp(probe->sh_vendor, "MEMDISK ", sizeof(probe->sh_vendor)) != 0) + goto end_of_loop; + + /* + * If it is a memdisk, make sure the mBFT signature is correct + * and its checksum is right. + */ + mbft = (ACPI_TABLE_HEADER *)PTOV(probe->sh_mbft); + if (memcmp(mbft->Signature, "mBFT", sizeof(mbft->Signature)) != 0) + goto end_of_loop; + sum = 0; + cp = (uint8_t *)mbft; + for (int idx = 0; idx < mbft->Length; ++idx) + sum += *(cp + idx); + if (sum != 0) + goto end_of_loop; + + /* + * The memdisk info follows the ACPI_TABLE_HEADER in the mBFT + * section. If the sector size is present and non-zero use it + * otherwise assume 512. + */ + mdi = (struct memdisk_info *)PTOV(probe->sh_mbft + sizeof(*mbft)); + sector_size = 512; + if (mdi->mdi_length + sizeof(mdi->mdi_13h_hook_ptr) >= sizeof(*mdi) && + mdi->mdi_sector_size != 0) + sector_size = 1 << mdi->mdi_sector_size; + + printf("memdisk %d.%d disk at %#x (%d sectors = %d bytes)\n", + mdi->mdi_major, mdi->mdi_minor, mdi->mdi_disk_ptr, + mdi->mdi_disk_sectors, mdi->mdi_disk_sectors * sector_size); + + snprintf(line, sizeof(line), "hint.md.%d.physaddr", count); + snprintf(scratch, sizeof(scratch), "0x%08x", mdi->mdi_disk_ptr); + setenv(line, scratch, 1); + snprintf(line, sizeof(line), "hint.md.%d.len", count); + snprintf(scratch, sizeof(scratch), "%d", mdi->mdi_disk_sectors * sector_size); + setenv(line, scratch, 1); + count++; +end_of_loop: + hook++; + offset = probe->sh_next_offset; + segment = probe->sh_next_segment; + } +} diff --git a/stand/i386/libi386/libi386.h b/stand/i386/libi386/libi386.h index d456ef58d7c2..caf565dd0656 100644 --- a/stand/i386/libi386/libi386.h +++ b/stand/i386/libi386/libi386.h @@ -149,3 +149,5 @@ int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend, int add_smap); void pxe_enable(void *pxeinfo); + +void biosmemdisk_detect(void); diff --git a/stand/i386/loader/main.c b/stand/i386/loader/main.c index a7dfb2dde762..fd95cf5243cf 100644 --- a/stand/i386/loader/main.c +++ b/stand/i386/loader/main.c @@ -251,6 +251,9 @@ main(void) initial_bootinfo->bi_extmem = bios_extmem / 1024; } + /* detect MEMDISK virtual disks */ + biosmemdisk_detect(); + /* detect SMBIOS for future reference */ smbios_detect(NULL); diff --git a/sys/dev/md/md.c b/sys/dev/md/md.c index 29dc0c880e3a..ec1664fac701 100644 --- a/sys/dev/md/md.c +++ b/sys/dev/md/md.c @@ -89,6 +89,8 @@ #include <sys/unistd.h> #include <sys/vnode.h> #include <sys/disk.h> +#include <sys/param.h> +#include <sys/bus.h> #include <geom/geom.h> #include <geom/geom_int.h> @@ -2082,8 +2084,10 @@ g_md_init(struct g_class *mp __unused) { caddr_t mod; u_char *ptr, *name, *type; + u_char scratch[40]; unsigned len; int i; + vm_offset_t paddr; /* figure out log2(NINDIR) */ for (i = NINDIR, nshift = -1; i; nshift++) @@ -2123,6 +2127,25 @@ g_md_init(struct g_class *mp __unused) sx_xunlock(&md_sx); } } + + /* + * Load up to 32 pre-loaded disks + */ + for (int i = 0; i < 32; i++) { + if (resource_long_value("md", i, "physaddr", + (long *) &paddr) != 0 || + resource_int_value("md", i, "len", &len) != 0) + break; + ptr = (char *)pmap_map(NULL, paddr, paddr + len, VM_PROT_READ); + if (ptr != NULL && len != 0) { + sprintf(scratch, "preload%d 0x%016jx", i, + (uintmax_t)paddr); + sx_xlock(&md_sx); + md_preloaded(ptr, len, scratch); + sx_xunlock(&md_sx); + } + } + status_dev = make_dev(&mdctl_cdevsw, INT_MAX, UID_ROOT, GID_WHEEL, 0600, MDCTL_NAME); g_topology_lock();