mirror of
https://github.com/torvalds/linux
synced 2024-09-06 09:51:23 +00:00
![Ross Lagerwall](/assets/img/avatar_default.png)
To facilitate diskless iSCSI boot, the firmware can place a table of configuration details in memory called the iBFT. The presence of this table is not specified, nor is the precise location (and it's not in the E820) so the kernel has to search for a magic marker to find it. When running under Xen, Dom 0 does not have access to the entire host's memory, only certain regions which are identity-mapped which means that the pseudo-physical address in Dom0 == real host physical address. Add the iBFT search bounds as a reserved region which causes it to be identity-mapped in xen_set_identity_and_remap_chunk() which allows Dom0 access to the specific physical memory to correctly search for the iBFT magic marker (and later access the full table). This necessitates moving the call to reserve_ibft_region() somewhat later so that it is called after e820__memory_setup() which is when the Xen identity mapping adjustments are applied. The precise location of the call is not too important so I've put it alongside dmi_setup() which does similar scanning of memory for configuration tables. Finally in the iBFT find code, instead of using isa_bus_to_virt() which doesn't do the right thing under Xen, use early_memremap() like the dmi_setup() code does. The result of these changes is that it is possible to boot a diskless Xen + Dom0 running off an iSCSI disk whereas previously it would fail to find the iBFT and consequently, the iSCSI root disk. Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com> Reviewed-by: Juergen Gross <jgross@suse.com> Acked-by: Konrad Rzeszutek Wilk <konrad@darnok.org> Acked-by: Dave Hansen <dave.hansen@linux.intel.com> # for x86 Link: https://lore.kernel.org/r/20230605102840.1521549-1-ross.lagerwall@citrix.com Signed-off-by: Juergen Gross <jgross@suse.com>
101 lines
2.3 KiB
C
101 lines
2.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright 2007-2010 Red Hat, Inc.
|
|
* by Peter Jones <pjones@redhat.com>
|
|
* Copyright 2007 IBM, Inc.
|
|
* by Konrad Rzeszutek <konradr@linux.vnet.ibm.com>
|
|
* Copyright 2008
|
|
* by Konrad Rzeszutek <ketuzsezr@darnok.org>
|
|
*
|
|
* This code finds the iSCSI Boot Format Table.
|
|
*/
|
|
|
|
#include <linux/memblock.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/device.h>
|
|
#include <linux/efi.h>
|
|
#include <linux/err.h>
|
|
#include <linux/init.h>
|
|
#include <linux/limits.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/iscsi_ibft.h>
|
|
|
|
#include <asm/mmzone.h>
|
|
|
|
/*
|
|
* Physical location of iSCSI Boot Format Table.
|
|
*/
|
|
phys_addr_t ibft_phys_addr;
|
|
EXPORT_SYMBOL_GPL(ibft_phys_addr);
|
|
|
|
static const struct {
|
|
char *sign;
|
|
} ibft_signs[] = {
|
|
{ "iBFT" },
|
|
{ "BIFT" }, /* Broadcom iSCSI Offload */
|
|
};
|
|
|
|
#define IBFT_SIGN_LEN 4
|
|
#define VGA_MEM 0xA0000 /* VGA buffer */
|
|
#define VGA_SIZE 0x20000 /* 128kB */
|
|
|
|
/*
|
|
* Routine used to find and reserve the iSCSI Boot Format Table
|
|
*/
|
|
void __init reserve_ibft_region(void)
|
|
{
|
|
unsigned long pos, virt_pos = 0;
|
|
unsigned int len = 0;
|
|
void *virt = NULL;
|
|
int i;
|
|
|
|
ibft_phys_addr = 0;
|
|
|
|
/* iBFT 1.03 section 1.4.3.1 mandates that UEFI machines will
|
|
* only use ACPI for this
|
|
*/
|
|
if (efi_enabled(EFI_BOOT))
|
|
return;
|
|
|
|
for (pos = IBFT_START; pos < IBFT_END; pos += 16) {
|
|
/* The table can't be inside the VGA BIOS reserved space,
|
|
* so skip that area */
|
|
if (pos == VGA_MEM)
|
|
pos += VGA_SIZE;
|
|
|
|
/* Map page by page */
|
|
if (offset_in_page(pos) == 0) {
|
|
if (virt)
|
|
early_memunmap(virt, PAGE_SIZE);
|
|
virt = early_memremap_ro(pos, PAGE_SIZE);
|
|
virt_pos = pos;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ibft_signs); i++) {
|
|
if (memcmp(virt + (pos - virt_pos), ibft_signs[i].sign,
|
|
IBFT_SIGN_LEN) == 0) {
|
|
unsigned long *addr =
|
|
(unsigned long *)(virt + pos - virt_pos + 4);
|
|
len = *addr;
|
|
/* if the length of the table extends past 1M,
|
|
* the table cannot be valid. */
|
|
if (pos + len <= (IBFT_END-1)) {
|
|
ibft_phys_addr = pos;
|
|
memblock_reserve(ibft_phys_addr, PAGE_ALIGN(len));
|
|
pr_info("iBFT found at %pa.\n", &ibft_phys_addr);
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
early_memunmap(virt, PAGE_SIZE);
|
|
}
|