Commit b3c72fc9 authored by Daniel Kiper's avatar Daniel Kiper Committed by Borislav Petkov

x86/boot: Introduce setup_indirect

The setup_data is a bit awkward to use for extremely large data objects,
both because the setup_data header has to be adjacent to the data object
and because it has a 32-bit length field. However, it is important that
intermediate stages of the boot process have a way to identify which
chunks of memory are occupied by kernel data. Thus introduce an uniform
way to specify such indirect data as setup_indirect struct and
SETUP_INDIRECT type.

And finally bump setup_header version in arch/x86/boot/header.S.
Suggested-by: default avatarH. Peter Anvin (Intel) <hpa@zytor.com>
Signed-off-by: default avatarDaniel Kiper <daniel.kiper@oracle.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Reviewed-by: default avatarRoss Philipson <ross.philipson@oracle.com>
Reviewed-by: default avatarH. Peter Anvin (Intel) <hpa@zytor.com>
Acked-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: ard.biesheuvel@linaro.org
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: dave.hansen@linux.intel.com
Cc: eric.snowberg@oracle.com
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Juergen Gross <jgross@suse.com>
Cc: kanth.ghatraju@oracle.com
Cc: linux-doc@vger.kernel.org
Cc: linux-efi <linux-efi@vger.kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: rdunlap@infradead.org
Cc: ross.philipson@oracle.com
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: x86-ml <x86@kernel.org>
Cc: xen-devel@lists.xenproject.org
Link: https://lkml.kernel.org/r/20191112134640.16035-4-daniel.kiper@oracle.com
parent 00cd1c15
...@@ -827,6 +827,47 @@ Protocol: 2.09+ ...@@ -827,6 +827,47 @@ Protocol: 2.09+
sure to consider the case where the linked list already contains sure to consider the case where the linked list already contains
entries. entries.
The setup_data is a bit awkward to use for extremely large data objects,
both because the setup_data header has to be adjacent to the data object
and because it has a 32-bit length field. However, it is important that
intermediate stages of the boot process have a way to identify which
chunks of memory are occupied by kernel data.
Thus setup_indirect struct and SETUP_INDIRECT type were introduced in
protocol 2.15.
struct setup_indirect {
__u32 type;
__u32 reserved; /* Reserved, must be set to zero. */
__u64 len;
__u64 addr;
};
The type member is a SETUP_INDIRECT | SETUP_* type. However, it cannot be
SETUP_INDIRECT itself since making the setup_indirect a tree structure
could require a lot of stack space in something that needs to parse it
and stack space can be limited in boot contexts.
Let's give an example how to point to SETUP_E820_EXT data using setup_indirect.
In this case setup_data and setup_indirect will look like this:
struct setup_data {
__u64 next = 0 or <addr_of_next_setup_data_struct>;
__u32 type = SETUP_INDIRECT;
__u32 len = sizeof(setup_data);
__u8 data[sizeof(setup_indirect)] = struct setup_indirect {
__u32 type = SETUP_INDIRECT | SETUP_E820_EXT;
__u32 reserved = 0;
__u64 len = <len_of_SETUP_E820_EXT_data>;
__u64 addr = <addr_of_SETUP_E820_EXT_data>;
}
}
.. note::
SETUP_INDIRECT | SETUP_NONE objects cannot be properly distinguished
from SETUP_INDIRECT itself. So, this kind of objects cannot be provided
by the bootloaders.
============ ============ ============ ============
Field name: pref_address Field name: pref_address
Type: read (reloc) Type: read (reloc)
...@@ -986,7 +1027,7 @@ Field name: setup_type_max ...@@ -986,7 +1027,7 @@ Field name: setup_type_max
Offset/size: 0x000c/4 Offset/size: 0x000c/4
============ ============== ============ ==============
This field contains maximal allowed type for setup_data. This field contains maximal allowed type for setup_data and setup_indirect structs.
The Image Checksum The Image Checksum
......
...@@ -459,6 +459,18 @@ static bool mem_avoid_overlap(struct mem_vector *img, ...@@ -459,6 +459,18 @@ static bool mem_avoid_overlap(struct mem_vector *img,
is_overlapping = true; is_overlapping = true;
} }
if (ptr->type == SETUP_INDIRECT &&
((struct setup_indirect *)ptr->data)->type != SETUP_INDIRECT) {
avoid.start = ((struct setup_indirect *)ptr->data)->addr;
avoid.size = ((struct setup_indirect *)ptr->data)->len;
if (mem_overlaps(img, &avoid) && (avoid.start < earliest)) {
*overlap = avoid;
earliest = overlap->start;
is_overlapping = true;
}
}
ptr = (struct setup_data *)(unsigned long)ptr->next; ptr = (struct setup_data *)(unsigned long)ptr->next;
} }
......
...@@ -14,7 +14,7 @@ kernel_info: ...@@ -14,7 +14,7 @@ kernel_info:
/* Size total. */ /* Size total. */
.long kernel_info_end - kernel_info .long kernel_info_end - kernel_info
/* Maximal allowed type for setup_data. */ /* Maximal allowed type for setup_data and setup_indirect structs. */
.long SETUP_TYPE_MAX .long SETUP_TYPE_MAX
kernel_info_var_len_data: kernel_info_var_len_data:
......
...@@ -300,7 +300,7 @@ _start: ...@@ -300,7 +300,7 @@ _start:
# Part 2 of the header, from the old setup.S # Part 2 of the header, from the old setup.S
.ascii "HdrS" # header signature .ascii "HdrS" # header signature
.word 0x020d # header version number (>= 0x0105) .word 0x020f # header version number (>= 0x0105)
# or else old loadlin-1.5 will fail) # or else old loadlin-1.5 will fail)
.globl realmode_swtch .globl realmode_swtch
realmode_swtch: .word 0, 0 # default_switch, SETUPSEG realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#ifndef _ASM_X86_BOOTPARAM_H #ifndef _ASM_X86_BOOTPARAM_H
#define _ASM_X86_BOOTPARAM_H #define _ASM_X86_BOOTPARAM_H
/* setup_data types */ /* setup_data/setup_indirect types */
#define SETUP_NONE 0 #define SETUP_NONE 0
#define SETUP_E820_EXT 1 #define SETUP_E820_EXT 1
#define SETUP_DTB 2 #define SETUP_DTB 2
...@@ -11,8 +11,10 @@ ...@@ -11,8 +11,10 @@
#define SETUP_APPLE_PROPERTIES 5 #define SETUP_APPLE_PROPERTIES 5
#define SETUP_JAILHOUSE 6 #define SETUP_JAILHOUSE 6
/* max(SETUP_*) */ #define SETUP_INDIRECT (1<<31)
#define SETUP_TYPE_MAX SETUP_JAILHOUSE
/* SETUP_INDIRECT | max(SETUP_*) */
#define SETUP_TYPE_MAX (SETUP_INDIRECT | SETUP_JAILHOUSE)
/* ram_size flags */ /* ram_size flags */
#define RAMDISK_IMAGE_START_MASK 0x07FF #define RAMDISK_IMAGE_START_MASK 0x07FF
...@@ -52,6 +54,14 @@ struct setup_data { ...@@ -52,6 +54,14 @@ struct setup_data {
__u8 data[0]; __u8 data[0];
}; };
/* extensible setup indirect data node */
struct setup_indirect {
__u32 type;
__u32 reserved; /* Reserved, must be set to zero. */
__u64 len;
__u64 addr;
};
struct setup_header { struct setup_header {
__u8 setup_sects; __u8 setup_sects;
__u16 root_flags; __u16 root_flags;
......
...@@ -999,6 +999,17 @@ void __init e820__reserve_setup_data(void) ...@@ -999,6 +999,17 @@ void __init e820__reserve_setup_data(void)
data = early_memremap(pa_data, sizeof(*data)); data = early_memremap(pa_data, sizeof(*data));
e820__range_update(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN); e820__range_update(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
e820__range_update_kexec(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN); e820__range_update_kexec(pa_data, sizeof(*data)+data->len, E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
if (data->type == SETUP_INDIRECT &&
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) {
e820__range_update(((struct setup_indirect *)data->data)->addr,
((struct setup_indirect *)data->data)->len,
E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
e820__range_update_kexec(((struct setup_indirect *)data->data)->addr,
((struct setup_indirect *)data->data)->len,
E820_TYPE_RAM, E820_TYPE_RESERVED_KERN);
}
pa_data = data->next; pa_data = data->next;
early_memunmap(data, sizeof(*data)); early_memunmap(data, sizeof(*data));
} }
......
...@@ -44,7 +44,12 @@ static ssize_t setup_data_read(struct file *file, char __user *user_buf, ...@@ -44,7 +44,12 @@ static ssize_t setup_data_read(struct file *file, char __user *user_buf,
if (count > node->len - pos) if (count > node->len - pos)
count = node->len - pos; count = node->len - pos;
pa = node->paddr + sizeof(struct setup_data) + pos; pa = node->paddr + pos;
/* Is it direct data or invalid indirect one? */
if (!(node->type & SETUP_INDIRECT) || node->type == SETUP_INDIRECT)
pa += sizeof(struct setup_data);
p = memremap(pa, count, MEMREMAP_WB); p = memremap(pa, count, MEMREMAP_WB);
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
...@@ -108,9 +113,17 @@ static int __init create_setup_data_nodes(struct dentry *parent) ...@@ -108,9 +113,17 @@ static int __init create_setup_data_nodes(struct dentry *parent)
goto err_dir; goto err_dir;
} }
node->paddr = pa_data; if (data->type == SETUP_INDIRECT &&
node->type = data->type; ((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) {
node->len = data->len; node->paddr = ((struct setup_indirect *)data->data)->addr;
node->type = ((struct setup_indirect *)data->data)->type;
node->len = ((struct setup_indirect *)data->data)->len;
} else {
node->paddr = pa_data;
node->type = data->type;
node->len = data->len;
}
create_setup_data_node(d, no, node); create_setup_data_node(d, no, node);
pa_data = data->next; pa_data = data->next;
......
...@@ -100,7 +100,12 @@ static int __init get_setup_data_size(int nr, size_t *size) ...@@ -100,7 +100,12 @@ static int __init get_setup_data_size(int nr, size_t *size)
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
if (nr == i) { if (nr == i) {
*size = data->len; if (data->type == SETUP_INDIRECT &&
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT)
*size = ((struct setup_indirect *)data->data)->len;
else
*size = data->len;
memunmap(data); memunmap(data);
return 0; return 0;
} }
...@@ -130,7 +135,10 @@ static ssize_t type_show(struct kobject *kobj, ...@@ -130,7 +135,10 @@ static ssize_t type_show(struct kobject *kobj,
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
ret = sprintf(buf, "0x%x\n", data->type); if (data->type == SETUP_INDIRECT)
ret = sprintf(buf, "0x%x\n", ((struct setup_indirect *)data->data)->type);
else
ret = sprintf(buf, "0x%x\n", data->type);
memunmap(data); memunmap(data);
return ret; return ret;
} }
...@@ -142,7 +150,7 @@ static ssize_t setup_data_data_read(struct file *fp, ...@@ -142,7 +150,7 @@ static ssize_t setup_data_data_read(struct file *fp,
loff_t off, size_t count) loff_t off, size_t count)
{ {
int nr, ret = 0; int nr, ret = 0;
u64 paddr; u64 paddr, len;
struct setup_data *data; struct setup_data *data;
void *p; void *p;
...@@ -157,19 +165,28 @@ static ssize_t setup_data_data_read(struct file *fp, ...@@ -157,19 +165,28 @@ static ssize_t setup_data_data_read(struct file *fp,
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
if (off > data->len) { if (data->type == SETUP_INDIRECT &&
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) {
paddr = ((struct setup_indirect *)data->data)->addr;
len = ((struct setup_indirect *)data->data)->len;
} else {
paddr += sizeof(*data);
len = data->len;
}
if (off > len) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
if (count > data->len - off) if (count > len - off)
count = data->len - off; count = len - off;
if (!count) if (!count)
goto out; goto out;
ret = count; ret = count;
p = memremap(paddr + sizeof(*data), data->len, MEMREMAP_WB); p = memremap(paddr, len, MEMREMAP_WB);
if (!p) { if (!p) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
......
...@@ -438,6 +438,12 @@ static void __init memblock_x86_reserve_range_setup_data(void) ...@@ -438,6 +438,12 @@ static void __init memblock_x86_reserve_range_setup_data(void)
while (pa_data) { while (pa_data) {
data = early_memremap(pa_data, sizeof(*data)); data = early_memremap(pa_data, sizeof(*data));
memblock_reserve(pa_data, sizeof(*data) + data->len); memblock_reserve(pa_data, sizeof(*data) + data->len);
if (data->type == SETUP_INDIRECT &&
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT)
memblock_reserve(((struct setup_indirect *)data->data)->addr,
((struct setup_indirect *)data->data)->len);
pa_data = data->next; pa_data = data->next;
early_memunmap(data, sizeof(*data)); early_memunmap(data, sizeof(*data));
} }
......
...@@ -626,6 +626,17 @@ static bool memremap_is_setup_data(resource_size_t phys_addr, ...@@ -626,6 +626,17 @@ static bool memremap_is_setup_data(resource_size_t phys_addr,
paddr_next = data->next; paddr_next = data->next;
len = data->len; len = data->len;
if ((phys_addr > paddr) && (phys_addr < (paddr + len))) {
memunmap(data);
return true;
}
if (data->type == SETUP_INDIRECT &&
((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) {
paddr = ((struct setup_indirect *)data->data)->addr;
len = ((struct setup_indirect *)data->data)->len;
}
memunmap(data); memunmap(data);
if ((phys_addr > paddr) && (phys_addr < (paddr + len))) if ((phys_addr > paddr) && (phys_addr < (paddr + len)))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment