Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 539a5fe2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-linus' of...

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86-bigbox-bootparam

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86-bigbox-bootparam:
  x86, boot: Document for linked list of struct setup_data
  x86, boot: export linked list of struct setup_data via debugfs
  x86, boot: add linked list of struct setup_data
  x86, boot: add free_early to early reservation machanism
parents 0124cecf fb884381
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ Protocol 2.05: (Kernel 2.6.20) Make protected mode kernel relocatable.
Protocol 2.06:	(Kernel 2.6.22) Added a field that contains the size of
		the boot command line

Protocol 2.09:	(kernel 2.6.26) Added a field of 64-bit physical
		pointer to single linked list of struct	setup_data.

**** MEMORY LAYOUT

@@ -172,6 +174,8 @@ Offset Proto Name Meaning
0240/8	2.07+	hardware_subarch_data Subarchitecture-specific data
0248/4	2.08+	payload_offset	Offset of kernel payload
024C/4	2.08+	payload_length	Length of kernel payload
0250/8	2.09+	setup_data	64-bit physical pointer to linked list
				of struct setup_data

(1) For backwards compatibility, if the setup_sects field contains 0, the
    real value is 4.
@@ -572,6 +576,28 @@ command line is entered using the following protocol:
	covered by setup_move_size, so you may need to adjust this
	field.

Field name:	setup_data
Type:		write (obligatory)
Offset/size:	0x250/8
Protocol:	2.09+

  The 64-bit physical pointer to NULL terminated single linked list of
  struct setup_data. This is used to define a more extensible boot
  parameters passing mechanism. The definition of struct setup_data is
  as follow:

  struct setup_data {
	  u64 next;
	  u32 type;
	  u32 len;
	  u8  data[0];
  };

  Where, the next is a 64-bit physical pointer to the next node of
  linked list, the next field of the last node is 0; the type is used
  to identify the contents of data; the len is the length of data
  field; the data holds the real payload.


**** MEMORY LAYOUT OF THE REAL-MODE CODE

+5 −1
Original line number Diff line number Diff line
@@ -120,7 +120,7 @@ _start:
	# Part 2 of the header, from the old setup.S

		.ascii	"HdrS"		# header signature
		.word	0x0208		# header version number (>= 0x0105)
		.word	0x0209		# header version number (>= 0x0105)
					# or else old loadlin-1.5 will fail)
		.globl realmode_swtch
realmode_swtch:	.word	0, 0		# default_switch, SETUPSEG
@@ -227,6 +227,10 @@ hardware_subarch_data: .quad 0
payload_offset:		.long input_data
payload_length:		.long input_data_end-input_data

setup_data:		.quad 0			# 64-bit physical pointer to
						# single linked list of
						# struct setup_data

# End of setup header #####################################################

	.section ".inittext", "ax"
+22 −0
Original line number Diff line number Diff line
@@ -84,6 +84,28 @@ void __init reserve_early(unsigned long start, unsigned long end, char *name)
		strncpy(r->name, name, sizeof(r->name) - 1);
}

void __init free_early(unsigned long start, unsigned long end)
{
	struct early_res *r;
	int i, j;

	for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
		r = &early_res[i];
		if (start == r->start && end == r->end)
			break;
	}
	if (i >= MAX_EARLY_RES || !early_res[i].end)
		panic("free_early on not reserved area: %lx-%lx!", start, end);

	for (j = i + 1; j < MAX_EARLY_RES && early_res[j].end; j++)
		;

	memcpy(&early_res[i], &early_res[i + 1],
	       (j - 1 - i) * sizeof(struct early_res));

	early_res[j - 1].end = 0;
}

void __init early_res_to_bootmem(void)
{
	int i;
+20 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/string.h>
#include <linux/percpu.h>
#include <linux/start_kernel.h>
#include <linux/io.h>

#include <asm/processor.h>
#include <asm/proto.h>
@@ -100,6 +101,24 @@ static void __init reserve_ebda_region(void)
	reserve_early(lowmem, 0x100000, "BIOS reserved");
}

static void __init reserve_setup_data(void)
{
	struct setup_data *data;
	unsigned long pa_data;
	char buf[32];

	if (boot_params.hdr.version < 0x0209)
		return;
	pa_data = boot_params.hdr.setup_data;
	while (pa_data) {
		data = early_ioremap(pa_data, sizeof(*data));
		sprintf(buf, "setup data %x", data->type);
		reserve_early(pa_data, pa_data+sizeof(*data)+data->len, buf);
		pa_data = data->next;
		early_iounmap(data, sizeof(*data));
	}
}

void __init x86_64_start_kernel(char * real_mode_data)
{
	int i;
@@ -156,6 +175,7 @@ void __init x86_64_start_kernel(char * real_mode_data)
#endif

	reserve_ebda_region();
	reserve_setup_data();

	/*
	 * At this point everything still needed from the boot loader
+158 −5
Original line number Diff line number Diff line
@@ -6,14 +6,162 @@
 *
 * This file is released under the GPLv2.
 */

#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mm.h>

#include <asm/setup.h>

#ifdef CONFIG_DEBUG_BOOT_PARAMS
struct setup_data_node {
	u64 paddr;
	u32 type;
	u32 len;
};

static ssize_t
setup_data_read(struct file *file, char __user *user_buf, size_t count,
		loff_t *ppos)
{
	struct setup_data_node *node = file->private_data;
	unsigned long remain;
	loff_t pos = *ppos;
	struct page *pg;
	void *p;
	u64 pa;

	if (pos < 0)
		return -EINVAL;
	if (pos >= node->len)
		return 0;

	if (count > node->len - pos)
		count = node->len - pos;
	pa = node->paddr + sizeof(struct setup_data) + pos;
	pg = pfn_to_page((pa + count - 1) >> PAGE_SHIFT);
	if (PageHighMem(pg)) {
		p = ioremap_cache(pa, count);
		if (!p)
			return -ENXIO;
	} else {
		p = __va(pa);
	}

	remain = copy_to_user(user_buf, p, count);

	if (PageHighMem(pg))
		iounmap(p);

	if (remain)
		return -EFAULT;

	*ppos = pos + count;

	return count;
}

static int setup_data_open(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	return 0;
}

static const struct file_operations fops_setup_data = {
	.read =		setup_data_read,
	.open =		setup_data_open,
};

static int __init
create_setup_data_node(struct dentry *parent, int no,
		       struct setup_data_node *node)
{
	struct dentry *d, *type, *data;
	char buf[16];
	int error;

	sprintf(buf, "%d", no);
	d = debugfs_create_dir(buf, parent);
	if (!d) {
		error = -ENOMEM;
		goto err_return;
	}
	type = debugfs_create_x32("type", S_IRUGO, d, &node->type);
	if (!type) {
		error = -ENOMEM;
		goto err_dir;
	}
	data = debugfs_create_file("data", S_IRUGO, d, node, &fops_setup_data);
	if (!data) {
		error = -ENOMEM;
		goto err_type;
	}
	return 0;

err_type:
	debugfs_remove(type);
err_dir:
	debugfs_remove(d);
err_return:
	return error;
}

static int __init create_setup_data_nodes(struct dentry *parent)
{
	struct setup_data_node *node;
	struct setup_data *data;
	int error, no = 0;
	struct dentry *d;
	struct page *pg;
	u64 pa_data;

	d = debugfs_create_dir("setup_data", parent);
	if (!d) {
		error = -ENOMEM;
		goto err_return;
	}

	pa_data = boot_params.hdr.setup_data;

	while (pa_data) {
		node = kmalloc(sizeof(*node), GFP_KERNEL);
		if (!node) {
			error = -ENOMEM;
			goto err_dir;
		}
		pg = pfn_to_page((pa_data+sizeof(*data)-1) >> PAGE_SHIFT);
		if (PageHighMem(pg)) {
			data = ioremap_cache(pa_data, sizeof(*data));
			if (!data) {
				error = -ENXIO;
				goto err_dir;
			}
		} else {
			data = __va(pa_data);
		}

		node->paddr = pa_data;
		node->type = data->type;
		node->len = data->len;
		error = create_setup_data_node(d, no, node);
		pa_data = data->next;

		if (PageHighMem(pg))
			iounmap(data);
		if (error)
			goto err_dir;
		no++;
	}
	return 0;

err_dir:
	debugfs_remove(d);
err_return:
	return error;
}

static struct debugfs_blob_wrapper boot_params_blob = {
	.data		= &boot_params,
	.size		= sizeof(boot_params),
@@ -21,8 +169,8 @@ static struct debugfs_blob_wrapper boot_params_blob = {

static int __init boot_params_kdebugfs_init(void)
{
	int error;
	struct dentry *dbp, *version, *data;
	int error;

	dbp = debugfs_create_dir("boot_params", NULL);
	if (!dbp) {
@@ -41,7 +189,13 @@ static int __init boot_params_kdebugfs_init(void)
		error = -ENOMEM;
		goto err_version;
	}
	error = create_setup_data_nodes(dbp);
	if (error)
		goto err_data;
	return 0;

err_data:
	debugfs_remove(data);
err_version:
	debugfs_remove(version);
err_dir:
@@ -61,5 +215,4 @@ static int __init arch_kdebugfs_init(void)

	return error;
}

arch_initcall(arch_kdebugfs_init);
Loading