iscsi_ibft: Fix finding the iBFT under Xen Dom 0

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>
This commit is contained in:
Ross Lagerwall 2023-06-05 11:28:40 +01:00 committed by Juergen Gross
parent 04d684875b
commit 9338c2233b
4 changed files with 46 additions and 20 deletions

View File

@ -796,7 +796,6 @@ static void __init early_reserve_memory(void)
memblock_x86_reserve_range_setup_data();
reserve_ibft_region();
reserve_bios_regions();
trim_snb_memory();
}
@ -1032,6 +1031,7 @@ void __init setup_arch(char **cmdline_p)
if (efi_enabled(EFI_BOOT))
efi_init();
reserve_ibft_region();
dmi_setup();
/*

View File

@ -6,6 +6,7 @@
*/
#include <linux/init.h>
#include <linux/iscsi_ibft.h>
#include <linux/sched.h>
#include <linux/kstrtox.h>
#include <linux/mm.h>
@ -764,6 +765,7 @@ char * __init xen_memory_setup(void)
BUG_ON(memmap.nr_entries == 0);
xen_e820_table.nr_entries = memmap.nr_entries;
if (xen_initial_domain()) {
/*
* Xen won't allow a 1:1 mapping to be created to UNUSABLE
* regions, so if we're using the machine memory map leave the
@ -772,9 +774,17 @@ char * __init xen_memory_setup(void)
* UNUSABLE regions in domUs are not handled and will need
* a patch in the future.
*/
if (xen_initial_domain())
xen_ignore_unusable();
#ifdef CONFIG_ISCSI_IBFT_FIND
/* Reserve 0.5 MiB to 1 MiB region so iBFT can be found */
xen_e820_table.entries[xen_e820_table.nr_entries].addr = IBFT_START;
xen_e820_table.entries[xen_e820_table.nr_entries].size = IBFT_END - IBFT_START;
xen_e820_table.entries[xen_e820_table.nr_entries].type = E820_TYPE_RESERVED;
xen_e820_table.nr_entries++;
#endif
}
/* Make sure the Xen-supplied memory map is well-ordered. */
e820__update_table(&xen_e820_table);

View File

@ -42,8 +42,6 @@ static const struct {
};
#define IBFT_SIGN_LEN 4
#define IBFT_START 0x80000 /* 512kB */
#define IBFT_END 0x100000 /* 1MB */
#define VGA_MEM 0xA0000 /* VGA buffer */
#define VGA_SIZE 0x20000 /* 128kB */
@ -52,9 +50,9 @@ static const struct {
*/
void __init reserve_ibft_region(void)
{
unsigned long pos;
unsigned long pos, virt_pos = 0;
unsigned int len = 0;
void *virt;
void *virt = NULL;
int i;
ibft_phys_addr = 0;
@ -70,13 +68,20 @@ void __init reserve_ibft_region(void)
* so skip that area */
if (pos == VGA_MEM)
pos += VGA_SIZE;
virt = isa_bus_to_virt(pos);
/* 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, ibft_signs[i].sign, IBFT_SIGN_LEN) ==
0) {
if (memcmp(virt + (pos - virt_pos), ibft_signs[i].sign,
IBFT_SIGN_LEN) == 0) {
unsigned long *addr =
(unsigned long *)isa_bus_to_virt(pos + 4);
(unsigned long *)(virt + pos - virt_pos + 4);
len = *addr;
/* if the length of the table extends past 1M,
* the table cannot be valid. */
@ -84,9 +89,12 @@ void __init reserve_ibft_region(void)
ibft_phys_addr = pos;
memblock_reserve(ibft_phys_addr, PAGE_ALIGN(len));
pr_info("iBFT found at %pa.\n", &ibft_phys_addr);
return;
goto out;
}
}
}
}
out:
early_memunmap(virt, PAGE_SIZE);
}

View File

@ -21,12 +21,20 @@
*/
extern phys_addr_t ibft_phys_addr;
#ifdef CONFIG_ISCSI_IBFT_FIND
/*
* Routine used to find and reserve the iSCSI Boot Format Table. The
* physical address is set in the ibft_phys_addr variable.
*/
#ifdef CONFIG_ISCSI_IBFT_FIND
void reserve_ibft_region(void);
/*
* Physical bounds to search for the iSCSI Boot Format Table.
*/
#define IBFT_START 0x80000 /* 512kB */
#define IBFT_END 0x100000 /* 1MB */
#else
static inline void reserve_ibft_region(void) {}
#endif