unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Danny Milosavljevic <dannym@scratchpost.org>
To: David Craven <david@craven.ch>
Cc: guix-devel <guix-devel@gnu.org>
Subject: Guix with U-Boot
Date: Mon, 29 Aug 2016 12:31:49 +0200	[thread overview]
Message-ID: <20160829123149.0e260af6@scratchpost.org> (raw)
In-Reply-To: <CAL1_imn2JcqFo2TxM9jaQotUkR-fRQ9ncZqi8-QYv4qYiZB5og@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1373 bytes --]

Hi,

On Sun, 28 Aug 2016 14:28:31 +0200
David Craven <david@craven.ch> wrote:

> Thanks for your suggestions. I pushed the packages.
> 
> Is there a git repo somewhere I can pull your uboot stuff from? I'd
> like to test it =) 

There's no such repo right now.

I'd prefer merging into master soon to creating a fork.

For what it's worth, the changes I sent to the mailing list were tested on my machines, including that the Grub support still works.

If merging is deemed too risky, would it be possible to create a "wip-u-boot" branch on Savannah instead? - it seems that is how these bigger changes are handled. Then I could push the U-Boot parts there.

I've attached the UNFINISHED u-boot-install program which installs the U-Boot bootloader (comparable to grub-install). I plan to upstream it into the U-Boot main repository eventually. The other parts required for u-boot support were all already posted to our mailing list here.

gnu/packages/u-boot.scm could be merged now - it shouldn't affect anything because it's unused by master.

Other than that, it's mostly renaming "grub" -> "bootloader" in the Guix main source code that it creating a massive amount of noise in the patches I sent before. The actual functional change is a small part.

> I'm interested in starting a riscv port of guixsd at some point.

Nice! I think RISC-V is quite important to have.

[-- Attachment #2: u-boot-install.c --]
[-- Type: text/x-c++src, Size: 14678 bytes --]

/** u-boot-install.

SPDX GPLv3+

Installation methods are:

A20:
	/proc/cpuinfo:
		Processor	: ARMv7 Processor rev 4 (v7l)
		Hardware        : sun7i   "platform"; check the U-Boot config for this.
	U-Boot [def]config:
		CONFIG_ARCH_SUNXI=y
		CONFIG_MACH_SUN7I=y
		CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-olinuxino-lime2"
	Installer for Allwinner:
		dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=1024 seek=8 ; can be done by parted. ped_device_write, ped_device_sync, ...

Novena:
	"We ship a simple script called novena-install-spl. This simply does a dd of the file to the specified disk."
	disk=/dev/disk/by-path/platform-2198000.usdhc
	file=/boot/u-boot.spl
	if ! dd if="${file}" of="${disk}" bs=1024 seek=1 conv=notrunc 2> /dev/null

Raspberry Pi:
	mount /dev/sdb1 /mnt/tmp
	cp u-boot.bin /mnt/tmp/kernel.img
	umount /mnt/tmp

STMicroelectronics:
	http://www.stlinux.com/u-boot/install [from the U-Boot prompt]

Sheevaplug:
	http://www.cyrius.com/debian/kirkwood/sheevaplug/uboot-upgrade/

Xilinx:
	Downloading U-Boot to MicroBlaze

	http://www.wiki.xilinx.com/Build+U-Boot */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <parted/parted.h>
#include <sys/types.h>
#include <sys/stat.h>

/** skip_overlay_long_line: given a FILE, skips forward until the next '\n'. It also skips the '\n'.
  * @param f File to read */
static void skip_overly_long_line(FILE* f) {
	char buf[200];
	/* skip overly long lines */
	while (fgets(buf, sizeof(buf), f) != NULL && strchr(buf, '\n') == NULL)
		;
}

/** read_value_of_key_value_file: Read the value of a <key>=<value> file with the given key and return it.
 * @param separator separator between key and value
 * @param filename of file to read. File will be opened and closed automatically.
 * @param key key to search for. Whitespace after key in file is ignored.
 * @param value buffer to store value in. Will be NUL terminated if result != NULL.
 * @param valuesize size of buffer, including the NUL terminator.
 * @return value if successful, NULL otherwise.
 *
 * Cannot return values from lines which are longer than 399 Byte.
 */
static const char* read_value_of_key_value_file(char separator, const char* filename, const char* key, char* value, size_t valuesize) {
	char buf[400];
	char* result = NULL;
	FILE* f = fopen(filename, "r");
	if (f == NULL)
		return NULL;
	while (fgets(buf, sizeof(buf), f) != NULL) {
		if (strchr(buf, '\n') == NULL) {
			fprintf(stderr, "Warning: skipping overly long line that starts with \"%s\".\n", buf);
			skip_overly_long_line(f);
		} else {
			const char* xkey = buf;
			char* sep = strchr(buf, separator);
			if (sep != NULL) { /* we have key and value */
				*sep = 0; /* just in case separator is ' ' */
				const char* xvalue = sep + 1;
				/* strip leading whitespace from xvalue */
				while (*xvalue == ' ' || *xvalue == '\t')
					++xvalue;
				/* strip trailing whitespace from key */
				for (--sep; sep >= xkey; --sep) {
					if (*sep != ' ' && *sep != '\t')
						break;
					*sep = 0;
				}
				if (strcmp(key, xkey) == 0 && valuesize >= 1) {
					/* Note: be careful about '\n' in value */
					strncpy(value, xvalue, valuesize);
					if (value[valuesize - 1] == '\n')
						value[valuesize - 1] = 0;
					if (value[valuesize - 1] != 0) {
						fprintf(stderr, "Warning: skipping overly long value for entry that starts with %s=\"%s\".\n", xkey, xvalue);
					} else {
						result = strchr(value, '\n');
						if (result != NULL)
							*result = 0;
						result = value;
						break;
					}
				}
			}
		}
	}
	(void) fclose(f);
	return result;
}

static const char* read_proc_cpuinfo_hardware_platform(char* value, size_t valuesize) {
	return read_value_of_key_value_file(':', "/proc/cpuinfo", "Hardware", value, valuesize);
}

/** get_first_commandline_option_value: gets the value of the first (long) option with the specified name on the commandline and returns it.
  * @param argv The argument "vector" from main()
  * @param key The name of the option. Includes the "--" prefix
  * @return Value of the option. NULL if it was not found */
static const char* get_first_commandline_option_value(char* argv[], const char* key) {
	size_t keylen = strlen(key);
	for (++argv; *argv != NULL; ++argv) {
		const char* arg = *argv;
		if (strncmp(arg, "--", strlen("--")) != 0)
			break;
		if (strncmp(arg, key, keylen) == 0) {
			const char* value = &arg[keylen];
			if (key[0] && key[strlen(key) - 1] == '=') {
				return value;
			} else if (*value == 0)
				return value;
		}
		if (strcmp(arg, "--") == 0) /* end of options */
			break;
	}
	return NULL;
}

/** get_first_commandline_argument: gets the value of the first commandline argument (i.e. the first non-option).
  * @param argv The argument "vector" from main()
  * @return Value of the argument. NULL if none is there. */
static const char* get_first_commandline_argument(char* argv[]) {
	for (++argv; *argv != NULL; ++argv) {
		const char* arg = *argv;
		if (strncmp(arg, "--", strlen("--")) != 0)
			break;
		if (strcmp(arg, "--") == 0) /* end of options */ {
			++argv;
			break;
		}
	}
	return *argv;
}

/** print_usage: Prints usage information to stderr.
  * @param argv Argument "vector" from main() */
static void print_usage(char* argv[]) {
	fprintf(stderr, "Usage: %s [--source-image=<name>] [--platform=<platform>] <drive>\n", argv[0] ?: "u-boot-install");
	fprintf(stderr, "Usage: %s --list-platforms\n", argv[0] ?: "u-boot-install");
	fprintf(stderr, "Usage: %s --list-drives\n", argv[0] ?: "u-boot-install");
	fprintf(stderr, "If platform is not specified, it's autodetected by reading /proc/cpuinfo .\n");
	fprintf(stderr, "<drive> is usually a block device of the drive.\n");
}

/** ensure_nonclobbering_slot: Ensures a free slot at start_Sectors, size size_Sectors which doesn't clobber anything else (or fails)
  * @param drive Drive to use
  * @param start_Sectors The start (in Sectors)
  * @param size_Sectors The size (in Sectors) */
static void ensure_nonclobbering_slot(PedDevice* drive, PedSector start_Sectors, PedSector size_Sectors) {
	/* TODO print drive model just to make sure */
	/* TODO print char* size = ped_unit_format_byte (device, device->length * device->sector_size); */
	const char* drivename = drive->path;
	//long long sectorsize_Bytes = drive->sector_size;
	PedDisk* disk = ped_disk_new(drive);
	if (disk == NULL) {
		fprintf(stderr, "Error: could not read partition table from \"%s\".\n", drivename);
		exit(3);
	}
	for (PedPartition* part = ped_disk_next_partition(disk, NULL); part != NULL; part = ped_disk_next_partition(disk, part)) {
		if (part->num < 0)
			continue;
		/* example:
		1 2048 3563667087 ext4
		2 3563669504 6291456 
		3 3569960960 143360000 
		4 3713320960 193708175 ext4 */
		/* FIXME check unit */
		printf("%d\t%lld\t%lld\t%s\n", part->num, part->geom.start, part->geom.length, part->fs_type ? part->fs_type->name : "");
	}
	{
		int max_partition_count;
		/* FIXME what's the difference to ped_disk_get_max_primary_partition_count ? */
		if (!ped_disk_get_max_supported_partition_count(disk, &max_partition_count)) {
			/* FIXME print error message */
			abort();
		}
		if (max_partition_count > 56) { /* FIXME for disk type GPT;
			static PedDisk *
gpt_duplicate (const PedDisk *disk);

			see _generate_header which sets FirstUsableLBA = gpt_disk_data->data_area.start */
			/* TODO if GPT is too large, reduce size of GPT to max. 56 entries. If it even is GPT to begin with (see disk->type).
				That might entail copying all the things over to a new GPT.
			 */
			/* FirstUsableLBA is the first logical block that is used for contents */
			abort();
		}
		/* FIXME gpt_partition_align (PedPartition *part, const PedConstraint *constraint) -> boooool */
	}
	/* FIXME check active gpt_partition_set_flag (PedPartition *part, PedPartitionFlag flag, int state) PED_PARTITION_BOOT FIXME LEGACY_BOOT ?? */
	ped_disk_destroy(disk);
}

/** write_block_safely: first makes sure there's nothing at the specified location, then writes there.
  * @param drive Drive to write to
  * @param buffer Buffer to read
  * @param start_Bytes Location where to write to, in LBA bytes from the beginning of the disk
  * @param size_Bytes Size of buffer and also number of bytes to write */
static void write_block_safely(PedDevice* drive, const void* buffer, off_t start_Bytes, off_t size_Bytes) {
	const char* drivename = drive->path;
	long long sectorsize_Bytes = drive->sector_size;
	if (start_Bytes % sectorsize_Bytes != 0) {
		fprintf(stderr, "Error: %jd Bytes from the start of drive %s is not the start of a sector. Cannot install there.\n", (intmax_t) start_Bytes, drivename);
		exit(3);
	}
	PedSector start_Sectors = start_Bytes / sectorsize_Bytes;
	PedSector size_Sectors = size_Bytes / sectorsize_Bytes; // FIXME round up.
	ensure_nonclobbering_slot(drive, start_Sectors, size_Sectors);
	if (ped_device_write(drive, buffer, start_Sectors, size_Sectors) == 0) {
		/* FIXME print error message */
		exit(4);
	}
	if (ped_device_sync(drive) == 0) {
		/* FIXME print error message */
		exit(5);
	}
}

/** install_on_raw_drive: Installs U-Boot on Novena (i.MX6) devices
  * @param imagename Name of the image to read
  * @param drivename Name of the drive's block device (not partition)
  * @param position_Bytes position to write the image to */
static void install_on_raw_drive(const char* imagename, const char* drivename, off_t position_Bytes) {
	PedDevice* drive = ped_device_get(drivename);
	if (drive == NULL) {
		fprintf(stderr, "Error: could not open drive \"%s\".\n", drivename);
		exit(2);
	}
	FILE* f = fopen(imagename, "rb");
	long size_Bytes;
	if (f == NULL ||
	    fseek(f, 0, SEEK_END) == -1 ||
	    (size_Bytes = ftell(f)) == -1 ||
	    fseek(f, 0, SEEK_SET) == -1) {
		/* FIXME ferror, strerror */
		exit(1);
	}
	if (size_Bytes > 8000000U) { /* 8 MB */
		/* FIXME print Too big instead of allocating a huge buffer. */
		abort();
	}
	void* buffer = malloc(size_Bytes);
	if (buffer == NULL) {
		fprintf(stderr, "ERROR: memory allocation of %ju bytes failed.\n", (uintmax_t) size_Bytes);
		/* FIXME print error */
		abort();
	}
	clearerr(f);
	if (fread(buffer, size_Bytes, 1U, f) != 1) {
		/* FIXME ferror, strerror.
       fread() does not distinguish between end-of-file and error, and callers
       must use feof(3) and ferror(3) to determine which occurred. */
		abort();
	}
	write_block_safely(drive, buffer, position_Bytes, size_Bytes);
	free(buffer);
	(void) fclose(f);
	ped_device_destroy(drive);
}

/** install_on_sunxi: Installs U-Boot on Allwinner (sunxi) devices
  * @param argv Argument "vector" from main()
  * @param drivename Name of the drive */
static void install_on_sunxi(char* argv[], const char* drivename) {
	const char* imagename = get_first_commandline_option_value(argv, "--source-image=") ?: "u-boot-sunxi-with-spl.bin";
	install_on_raw_drive(imagename, drivename, 8192ULL);
}

/** install_on_novena: Installs U-Boot on Novena (Freescale i.MX6) devices
  * @param argv Argument "vector" from main()
  * @param drivename Name of the drive */
static void install_on_novena(char* argv[], const char* drivename) {
	const char* imagename = get_first_commandline_option_value(argv, "--source-image=") ?: "u-boot.spl";
	install_on_raw_drive(imagename, drivename, 1024ULL);
}

/** struct installer: Represents an installer for a platform */
struct installer {
	const char* platform;
	void (*install)(char* argv[], const char* drive);
};

/** installers: Registry for known installers */
static struct installer installers[] = {
	{.platform = "sun7i", install_on_sunxi},
	{.platform = "mxc" /* imx6 */, install_on_novena},
	// TODO {.platform = "uefi", install_on_uefi},
	{}, /* sentinel */
};

/** install: Given a platform and drive, uses the platform-specific installer to install U-Boot on the drive. 
  * @param argv Argument "vector" from main()
  * @param platform Name of the platform
  * @param drive Name of the drive */
static void install(char* argv[], const char* platform, const char* drive) {
	for (const struct installer* xinstallers = installers; xinstallers->platform != NULL; ++xinstallers) {
		const struct installer* xinstaller = xinstallers;
		const char* xplatform = xinstaller->platform;
		if (strcmp(xplatform, platform) == 0) {
			(*xinstaller->install)(argv, drive);
			return;
		}
	}
	fprintf(stderr, "Error: don't know how to install U-Boot on platform \"%s\".\n", platform);
	exit(1);
}

/** list_platforms: Lists supported platforms */
static void list_platforms(void) {
	for (const struct installer* xinstallers = installers; xinstallers->platform != NULL; ++xinstallers) {
		const struct installer* xinstaller = xinstallers;
		const char* xplatform = xinstaller->platform;
		if (puts(xplatform) == EOF) {
			/* FIXME print error message */
			exit(1);
		}
	}
}

/** list_drives: Lists available drives */
static void list_drives(void) {
	ped_device_probe_all();
	for (PedDevice* device = ped_device_get_next(NULL); device != NULL; device = ped_device_get_next(device)) {
		/* FIXME handle overflow. FIXME round properly. */
		if (printf("%s\t%'lld MB\n", device->path, device->length * device->sector_size / 1000000) == -1) {
			/* FIXME print error message */
			exit(1);
		}
	}
}

static const char* get_uefi_platform(void) {
	DIR* dir = opendir("/sys/firmware/efi/vars");
	if (dir != NULL) {
		closedir(dir);
		return "uefi";
	} else {
		if (errno != ENOENT) {
			perror("ERROR: /sys/firmware/efi/vars");
			exit(9);
		}
		return NULL;
	}
}

int main(int argc, char* argv[]) {
	/* FIXME maybe make the user specify a --remote for remote installation and a --local for local installation - both mandatory */
	char buf[200];
	if (get_first_commandline_option_value(argv, "--help") != NULL) {
		print_usage(argv);
		return 0;
	}
	const char* oplist_platforms = get_first_commandline_option_value(argv, "--list-platforms");
	if (oplist_platforms != NULL) {
		list_platforms();
		return 0;
	} else {
		const char* oplist_drives = get_first_commandline_option_value(argv, "--list-drives");
		if (oplist_drives != NULL) {
			list_drives();
			return 0;
		}
	}
	const char* platform = get_first_commandline_option_value(argv, "--platform=") ?: 
	                       get_uefi_platform() ?: 
	                       read_proc_cpuinfo_hardware_platform(buf, sizeof(buf));
	const char* drive = get_first_commandline_argument(argv);
	if (platform != NULL && drive != NULL) {
		printf("Platform %s\n", platform);
		printf("Drive %s\n", drive);
		//printf("Hardware %s\n", read_proc_cpuinfo_hardware_platform(buf, sizeof(buf)));
		install(argv, platform, drive);
	} else {
		print_usage(argv);
		return 1;
	}
	return 0;
}

  reply	other threads:[~2016-08-29 10:32 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-05 18:37 [PATCH 02/12] import: utils: Symbols from 'license->symbol' have a license: prefix David Craven
2016-08-05 18:37 ` [PATCH 03/12] gnu: python: Import guix licenses with #:prefix license: David Craven
2016-08-05 18:37 ` [PATCH 04/12] gnu: python: Add python-binaryornot David Craven
2016-08-08 20:37   ` Leo Famulari
2016-08-22 12:05     ` David Craven
2016-08-22 12:14       ` David Craven
2016-08-22 13:20         ` [PATCH 01/11] import: utils: Symbols from 'license->symbol' have a license: prefix David Craven
2016-08-22 13:20           ` [PATCH 02/11] gnu: python: Import guix licenses with #:prefix license: David Craven
2016-08-26 20:55             ` David Craven
2016-08-22 13:20           ` [PATCH 03/11] gnu: Add python-binaryornot David Craven
2016-08-22 13:20           ` [PATCH 04/11] gnu: Add python-nltk David Craven
2016-08-27  8:39             ` Danny Milosavljevic
2016-10-24 21:15             ` Leo Famulari
2016-08-22 13:20           ` [PATCH 05/11] gnu: Add python-pymongo David Craven
2016-08-22 13:20           ` [PATCH 06/11] gnu: Add python-sh David Craven
2016-08-27  8:42             ` Danny Milosavljevic
2016-08-28 12:28               ` David Craven
2016-08-29 10:31                 ` Danny Milosavljevic [this message]
2016-08-29 11:25                   ` Guix with U-Boot David Craven
2016-08-29 14:12                     ` David Craven
2016-08-29 14:19                       ` Danny Milosavljevic
2016-08-29 16:51                         ` David Craven
2016-08-29 17:20                           ` Danny Milosavljevic
2016-08-29 17:52                             ` Danny Milosavljevic
2016-08-29 18:10                               ` Danny Milosavljevic
2016-08-29 17:38                           ` Danny Milosavljevic
2016-08-29 18:16                           ` Danny Milosavljevic
2016-08-29 18:20                             ` David Craven
2016-08-29 18:56                               ` David Craven
2016-08-29 19:04                                 ` Danny Milosavljevic
2016-08-29 18:57                               ` How does install-grub work? Danny Milosavljevic
2016-08-29 20:29                                 ` David Craven
2016-08-29 20:50                                   ` Danny Milosavljevic
2016-08-29 20:54                                     ` David Craven
2016-08-29 21:19                                       ` David Craven
2016-08-29 21:57                                         ` Danny Milosavljevic
2016-08-29 22:00                                           ` David Craven
2016-08-29 22:14                                             ` Danny Milosavljevic
2016-08-30 15:33                                               ` David Craven
2016-08-29 21:45                                       ` Danny Milosavljevic
2016-08-29 22:04                                         ` Danny Milosavljevic
2016-08-22 13:20           ` [PATCH 07/11] gnu: Add python-schematics David Craven
2016-08-22 13:20           ` [PATCH 08/11] gnu: Add python-publicsuffix David Craven
2016-08-22 13:20           ` [PATCH 09/11] gnu: Add python-publicsuffix2 David Craven
2016-08-22 13:20           ` [PATCH 10/11] gnu: Add python-url David Craven
2016-08-22 13:20           ` [PATCH 11/11] gnu: Add python-ipaddress David Craven
2016-08-05 18:37 ` [PATCH 05/12] gnu: python: Add python-nltk David Craven
2016-08-05 18:37 ` [PATCH 06/12] gnu: python: Add python-pymongo David Craven
2016-08-08 20:36   ` Leo Famulari
2016-08-08 20:39     ` Leo Famulari
2016-08-05 18:37 ` [PATCH 07/12] gnu: python: Add python-sh David Craven
2016-08-05 18:37 ` [PATCH 08/12] gnu: python: Add python-schematics David Craven
2016-08-05 18:37 ` [PATCH 09/12] gnu: python: Add python-publicsuffix David Craven
2016-08-05 18:37 ` [PATCH 10/12] gnu: python: Add python-publicsuffix2 David Craven
2016-08-05 18:37 ` [PATCH 11/12] gnu: python: Add python-url David Craven
2016-08-05 18:37 ` [PATCH 12/12] gnu: python: Add python-ipaddress David Craven
2016-08-05 18:50   ` Leo Famulari
2016-08-06 12:52     ` David Craven
2016-08-06 15:57       ` Danny Milosavljevic
2016-08-07  0:20       ` Leo Famulari
2016-08-27  6:43 ` [PATCH 02/12] import: utils: Symbols from 'license->symbol' have a license: prefix Ricardo Wurmus
2016-08-27  8:31   ` David Craven
2016-08-30 19:07     ` Eric Bavier
2016-09-16  5:03       ` Eric Bavier
2016-09-16  7:29         ` David Craven
2016-09-16 15:40           ` Eric Bavier
2016-09-16 15:47             ` David Craven
2016-09-16 17:02               ` Eric Bavier
2016-09-19 13:06                 ` Ludovic Courtès

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://guix.gnu.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160829123149.0e260af6@scratchpost.org \
    --to=dannym@scratchpost.org \
    --cc=david@craven.ch \
    --cc=guix-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).