This commit is contained in:
parent
167e7f25b9
commit
0cfc6920ea
386
kexec-tools-edd-support
Normal file
386
kexec-tools-edd-support
Normal file
@ -0,0 +1,386 @@
|
||||
From: Bernhard Walle <bwalle@suse.de>
|
||||
Subject: [PATCH] Implement EDD support for 32 bit boot
|
||||
To: kexec@lists.infradead.org
|
||||
|
||||
This patch implements EDD support. The information is read from
|
||||
/sys/firmware/edd (the edd driver must be loaded for this) and is written into
|
||||
the zero page of the 32 bit boot protocol.
|
||||
|
||||
I successfully tested the patch on a x86_64 machine with the x86_64 kernel.
|
||||
|
||||
This fixes a hardware detection problem, discovered in
|
||||
https://bugzilla.novell.com/show_bug.cgi?id=383210.
|
||||
|
||||
The last patch that updates the E820MAX constant is required to use that patch.
|
||||
|
||||
Signed-off-by: Bernhard Walle <bwalle@suse.de>
|
||||
|
||||
---
|
||||
doc/linux-i386-zero-page.txt | 3
|
||||
include/x86/x86-linux.h | 41 +++++-
|
||||
kexec/arch/i386/x86-linux-setup.c | 244 ++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 279 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/doc/linux-i386-zero-page.txt
|
||||
+++ b/doc/linux-i386-zero-page.txt
|
||||
@@ -75,5 +75,4 @@ Offset Type Description
|
||||
0x224 unsigned short setup.S heap end pointer
|
||||
0x290 - 0x2cf EDD_MBR_SIG_BUFFER (edd.S)
|
||||
0x2d0 - 0x600 E820MAP
|
||||
-0x600 - 0x7ff EDDBUF (edd.S) for disk signature read sector
|
||||
-0x600 - 0x7eb EDDBUF (edd.S) for edd data
|
||||
+0xd00 - 0xeec EDDBUF (edd.S) for edd data
|
||||
--- a/include/x86/x86-linux.h
|
||||
+++ b/include/x86/x86-linux.h
|
||||
@@ -4,7 +4,7 @@
|
||||
#define TENATIVE 0 /* Code that is tenatively correct but hasn't yet been officially accepted */
|
||||
|
||||
#define E820MAP 0x2d0 /* our map */
|
||||
-#define E820MAX 32 /* number of entries in E820MAP */
|
||||
+#define E820MAX 128 /* number of entries in E820MAP */
|
||||
#define E820NR 0x1e8 /* # entries in E820MAP */
|
||||
|
||||
#ifndef ASSEMBLY
|
||||
@@ -43,6 +43,29 @@ struct apm_bios_info {
|
||||
uint8_t reserved[44]; /* 0x54 */
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * EDD stuff
|
||||
+ */
|
||||
+
|
||||
+#define EDD_MBR_SIG_MAX 16
|
||||
+#define EDDMAXNR 6 /* number of edd_info structs starting at EDDBUF */
|
||||
+
|
||||
+#define EDD_EXT_FIXED_DISK_ACCESS (1 << 0)
|
||||
+#define EDD_EXT_DEVICE_LOCKING_AND_EJECTING (1 << 1)
|
||||
+#define EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT (1 << 2)
|
||||
+#define EDD_EXT_64BIT_EXTENSIONS (1 << 3)
|
||||
+
|
||||
+#define EDD_DEVICE_PARAM_SIZE 74
|
||||
+
|
||||
+struct edd_info {
|
||||
+ uint8_t device;
|
||||
+ uint8_t version;
|
||||
+ uint16_t interface_support;
|
||||
+ uint16_t legacy_max_cylinder;
|
||||
+ uint8_t legacy_max_head;
|
||||
+ uint8_t legacy_sectors_per_track;
|
||||
+ uint8_t edd_device_params[EDD_DEVICE_PARAM_SIZE];
|
||||
+} __attribute__ ((packed));
|
||||
|
||||
struct x86_linux_param_header {
|
||||
uint8_t orig_x; /* 0x00 */
|
||||
@@ -87,7 +110,9 @@ struct x86_linux_param_header {
|
||||
uint32_t alt_mem_k; /* 0x1e0 */
|
||||
uint8_t reserved5[4]; /* 0x1e4 */
|
||||
uint8_t e820_map_nr; /* 0x1e8 */
|
||||
- uint8_t reserved6[8]; /* 0x1e9 */
|
||||
+ uint8_t eddbuf_entries; /* 0x1e9 */
|
||||
+ uint8_t edd_mbr_sig_buf_entries; /* 0x1ea */
|
||||
+ uint8_t reserved6[6]; /* 0x1eb */
|
||||
uint8_t setup_sects; /* 0x1f1 */
|
||||
uint16_t mount_root_rdonly; /* 0x1f2 */
|
||||
uint16_t syssize; /* 0x1f4 */
|
||||
@@ -148,18 +173,20 @@ struct x86_linux_param_header {
|
||||
uint32_t cmdline_size; /* 0x238 */
|
||||
uint32_t hardware_subarch; /* 0x23C */
|
||||
uint64_t hardware_subarch_data; /* 0x240 */
|
||||
- uint8_t reserved16[0x2d0 - 0x248]; /* 0x248 */
|
||||
+ uint8_t reserved16[0x290 - 0x248]; /* 0x248 */
|
||||
+ uint32_t edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 0x290 */
|
||||
#endif
|
||||
- struct e820entry e820_map[E820MAX]; /* 0x2d0 */
|
||||
- /* 0x550 */
|
||||
+ struct e820entry e820_map[E820MAX]; /* 0x2d0 */
|
||||
+ uint8_t _pad8[48]; /* 0xcd0 */
|
||||
+ struct edd_info eddbuf[EDDMAXNR]; /* 0xd00 */
|
||||
+ /* 0xeec */
|
||||
#define COMMAND_LINE_SIZE 2048
|
||||
};
|
||||
|
||||
struct x86_linux_faked_param_header {
|
||||
struct x86_linux_param_header hdr; /* 0x00 */
|
||||
- uint8_t reserved17[0xab0]; /* 0x550 */
|
||||
uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x1000 */
|
||||
- uint8_t reserved18[0x200]; /* 0x1800 - 0x2000 */
|
||||
+ // uint8_t reserved18[0x200]; /* 0x1800 - 0x2000 */
|
||||
};
|
||||
|
||||
struct x86_linux_header {
|
||||
--- a/kexec/arch/i386/x86-linux-setup.c
|
||||
+++ b/kexec/arch/i386/x86-linux-setup.c
|
||||
@@ -13,10 +13,12 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
+/* #define DEBUG 1 */
|
||||
#define _GNU_SOURCE
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
+#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
@@ -27,6 +29,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <unistd.h>
|
||||
+#include <dirent.h>
|
||||
#include <x86/x86-linux.h>
|
||||
#include "../../kexec.h"
|
||||
#include "kexec-x86.h"
|
||||
@@ -157,6 +160,244 @@ int setup_linux_vesafb(struct x86_linux_
|
||||
return -1;
|
||||
}
|
||||
|
||||
+#define EDD_SYFS_DIR "/sys/firmware/edd"
|
||||
+
|
||||
+#define EDD_EXT_FIXED_DISK_ACCESS (1 << 0)
|
||||
+#define EDD_EXT_DEVICE_LOCKING_AND_EJECTING (1 << 1)
|
||||
+#define EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT (1 << 2)
|
||||
+#define EDD_EXT_64BIT_EXTENSIONS (1 << 3)
|
||||
+
|
||||
+/*
|
||||
+ * Scans one line from a given filename. Returns on success the number of
|
||||
+ * items written (same like scanf()).
|
||||
+ */
|
||||
+static int file_scanf(const char *dir, const char *file, const char *scanf_line, ...)
|
||||
+{
|
||||
+ va_list argptr;
|
||||
+ FILE *fp;
|
||||
+ int retno;
|
||||
+ char filename[PATH_MAX];
|
||||
+
|
||||
+ snprintf(filename, PATH_MAX, "%s/%s", dir, file);
|
||||
+ filename[PATH_MAX-1] = 0;
|
||||
+
|
||||
+ fp = fopen(filename, "r");
|
||||
+ if (!fp) {
|
||||
+ return -errno;
|
||||
+ }
|
||||
+
|
||||
+ va_start(argptr, scanf_line);
|
||||
+ retno = vfscanf(fp, scanf_line, argptr);
|
||||
+ va_end(argptr);
|
||||
+
|
||||
+ fclose(fp);
|
||||
+
|
||||
+ return retno;
|
||||
+}
|
||||
+
|
||||
+static int parse_edd_extensions(const char *dir, struct edd_info *edd_info)
|
||||
+{
|
||||
+ char filename[PATH_MAX];
|
||||
+ char line[1024];
|
||||
+ uint16_t flags = 0;
|
||||
+ FILE *fp;
|
||||
+
|
||||
+ snprintf(filename, PATH_MAX, "%s/%s", dir, "extensions");
|
||||
+ filename[PATH_MAX-1] = 0;
|
||||
+
|
||||
+ fp = fopen(filename, "r");
|
||||
+ if (!fp) {
|
||||
+ return -errno;
|
||||
+ }
|
||||
+
|
||||
+ while (fgets(line, 1024, fp)) {
|
||||
+ /*
|
||||
+ * strings are in kernel source, function edd_show_extensions()
|
||||
+ * drivers/firmware/edd.c
|
||||
+ */
|
||||
+ if (strstr(line, "Fixed disk access") == line)
|
||||
+ flags |= EDD_EXT_FIXED_DISK_ACCESS;
|
||||
+ else if (strstr(line, "Device locking and ejecting") == line)
|
||||
+ flags |= EDD_EXT_DEVICE_LOCKING_AND_EJECTING;
|
||||
+ else if (strstr(line, "Enhanced Disk Drive support") == line)
|
||||
+ flags |= EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT;
|
||||
+ else if (strstr(line, "64-bit extensions") == line)
|
||||
+ flags |= EDD_EXT_64BIT_EXTENSIONS;
|
||||
+ }
|
||||
+
|
||||
+ fclose(fp);
|
||||
+
|
||||
+ edd_info->interface_support = flags;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int read_edd_raw_data(const char *dir, struct edd_info *edd_info)
|
||||
+{
|
||||
+ char filename[PATH_MAX];
|
||||
+ FILE *fp;
|
||||
+ size_t read_chars;
|
||||
+ uint16_t len;
|
||||
+
|
||||
+ snprintf(filename, PATH_MAX, "%s/%s", dir, "raw_data");
|
||||
+ filename[PATH_MAX-1] = 0;
|
||||
+
|
||||
+ fp = fopen(filename, "r");
|
||||
+ if (!fp) {
|
||||
+ return -errno;
|
||||
+ }
|
||||
+
|
||||
+ memset(edd_info->edd_device_params, 0, EDD_DEVICE_PARAM_SIZE);
|
||||
+ read_chars = fread(edd_info->edd_device_params, sizeof(uint8_t),
|
||||
+ EDD_DEVICE_PARAM_SIZE, fp);
|
||||
+ fclose(fp);
|
||||
+
|
||||
+ len = ((uint16_t *)edd_info->edd_device_params)[0];
|
||||
+ dbgprintf("EDD raw data has length %d\n", len);
|
||||
+
|
||||
+ if (read_chars != len) {
|
||||
+ fprintf(stderr, "BIOS reported EDD length of %hd but only "
|
||||
+ "%d chars read.", len, (int)read_chars);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int add_edd_entry(struct x86_linux_param_header *real_mode,
|
||||
+ const char *sysfs_name, int *current_edd, int *current_mbr)
|
||||
+{
|
||||
+ uint8_t devnum, version;
|
||||
+ uint32_t mbr_sig;
|
||||
+ struct edd_info *edd_info;
|
||||
+
|
||||
+ if (!current_mbr || !current_edd) {
|
||||
+ fprintf(stderr, "%s: current_edd and current_edd "
|
||||
+ "must not be NULL", __FUNCTION__);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ edd_info = &real_mode->eddbuf[*current_edd];
|
||||
+ memset(edd_info, 0, sizeof(struct edd_info));
|
||||
+
|
||||
+ /* extract the device number */
|
||||
+ if (sscanf(basename(sysfs_name), "int13_dev%hhx", &devnum) != 1) {
|
||||
+ fprintf(stderr, "Invalid format of int13_dev dir "
|
||||
+ "entry: %s\n", basename(sysfs_name));
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* if there's a MBR signature, then add it */
|
||||
+ if (file_scanf(sysfs_name, "mbr_signature", "0x%x", &mbr_sig) == 1) {
|
||||
+ real_mode->edd_mbr_sig_buffer[*current_mbr] = mbr_sig;
|
||||
+ (*current_mbr)++;
|
||||
+ dbgprintf("EDD Device 0x%x: mbr_sig=0x%x\n", devnum, mbr_sig);
|
||||
+ }
|
||||
+
|
||||
+ /* set the device number */
|
||||
+ edd_info->device = devnum;
|
||||
+
|
||||
+ /* set the version */
|
||||
+ if (file_scanf(sysfs_name, "version", "0x%hhx", &version) != 1)
|
||||
+ return -1;
|
||||
+
|
||||
+ edd_info->version = version;
|
||||
+
|
||||
+ /* if version == 0, that's some kind of dummy entry */
|
||||
+ if (version != 0) {
|
||||
+ /* legacy_max_cylinder */
|
||||
+ if (file_scanf(sysfs_name, "legacy_max_cylinder", "%hu",
|
||||
+ &edd_info->legacy_max_cylinder) != 1) {
|
||||
+ fprintf(stderr, "Reading legacy_max_cylinder failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* legacy_max_head */
|
||||
+ if (file_scanf(sysfs_name, "legacy_max_head", "%hhu",
|
||||
+ &edd_info->legacy_max_head) != 1) {
|
||||
+ fprintf(stderr, "Reading legacy_max_head failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* legacy_sectors_per_track */
|
||||
+ if (file_scanf(sysfs_name, "legacy_sectors_per_track", "%hhu",
|
||||
+ &edd_info->legacy_sectors_per_track) != 1) {
|
||||
+ fprintf(stderr, "Reading legacy_sectors_per_track failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* Parse the EDD extensions */
|
||||
+ if (parse_edd_extensions(sysfs_name, edd_info) != 0) {
|
||||
+ fprintf(stderr, "Parsing EDD extensions failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* Parse the raw info */
|
||||
+ if (read_edd_raw_data(sysfs_name, edd_info) != 0) {
|
||||
+ fprintf(stderr, "Reading EDD raw data failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ (*current_edd)++;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void zero_edd(struct x86_linux_param_header *real_mode)
|
||||
+{
|
||||
+ real_mode->eddbuf_entries = 0;
|
||||
+ real_mode->edd_mbr_sig_buf_entries = 0;
|
||||
+ memset(real_mode->eddbuf, 0,
|
||||
+ EDDMAXNR * sizeof(struct edd_info));
|
||||
+ memset(real_mode->edd_mbr_sig_buffer, 0,
|
||||
+ EDD_MBR_SIG_MAX * sizeof(uint32_t));
|
||||
+}
|
||||
+
|
||||
+void setup_edd_info(struct x86_linux_param_header *real_mode,
|
||||
+ unsigned long kexec_flags)
|
||||
+{
|
||||
+ DIR *edd_dir;
|
||||
+ struct dirent *cursor;
|
||||
+ int current_edd = 0;
|
||||
+ int current_mbr = 0;
|
||||
+
|
||||
+ edd_dir = opendir(EDD_SYFS_DIR);
|
||||
+ if (!edd_dir) {
|
||||
+ dbgprintf(EDD_SYFS_DIR " does not exist.\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ zero_edd(real_mode);
|
||||
+ while ((cursor = readdir(edd_dir))) {
|
||||
+ char full_dir_name[PATH_MAX];
|
||||
+
|
||||
+ /* only read the entries that start with "int13_dev" */
|
||||
+ if (strstr(cursor->d_name, "int13_dev") != cursor->d_name)
|
||||
+ continue;
|
||||
+
|
||||
+ snprintf(full_dir_name, PATH_MAX, "%s/%s",
|
||||
+ EDD_SYFS_DIR, cursor->d_name);
|
||||
+ full_dir_name[PATH_MAX-1] = 0;
|
||||
+
|
||||
+ if (add_edd_entry(real_mode, full_dir_name, ¤t_edd,
|
||||
+ ¤t_mbr) != 0) {
|
||||
+ zero_edd(real_mode);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ real_mode->eddbuf_entries = current_edd;
|
||||
+ real_mode->edd_mbr_sig_buf_entries = current_mbr;
|
||||
+
|
||||
+out:
|
||||
+ closedir(edd_dir);
|
||||
+
|
||||
+ dbgprintf("Added %d EDD MBR entries and %d EDD entries.\n",
|
||||
+ real_mode->edd_mbr_sig_buf_entries,
|
||||
+ real_mode->eddbuf_entries);
|
||||
+}
|
||||
+
|
||||
void setup_linux_system_parameters(struct x86_linux_param_header *real_mode,
|
||||
unsigned long kexec_flags)
|
||||
{
|
||||
@@ -240,4 +481,7 @@ void setup_linux_system_parameters(struc
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ /* fill the EDD information */
|
||||
+ setup_edd_info(real_mode, kexec_flags);
|
||||
}
|
@ -1,3 +1,8 @@
|
||||
-------------------------------------------------------------------
|
||||
Tue May 13 15:19:58 CEST 2008 - bwalle@suse.de
|
||||
|
||||
- implement EDD (bnc#383210)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Mar 24 23:38:05 CET 2008 - bwalle@suse.de
|
||||
|
||||
|
@ -23,7 +23,7 @@ Requires: %insserv_prereq %fillup_prereq
|
||||
AutoReqProv: on
|
||||
Summary: Tools for fast kernel loading
|
||||
Version: 1.101
|
||||
Release: 184
|
||||
Release: 195
|
||||
Source: %{name}-%{package_version}.tar.bz2
|
||||
Source1: README.SUSE
|
||||
Url: http://ftp.kernel.org/pub/linux/kernel/people/horms/kexec-tools/
|
||||
@ -31,6 +31,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
BuildRequires: zlib-devel
|
||||
Patch1: kexec-tools.ppc32-64bit-purgatory.patch
|
||||
Patch2: kexec-tools.gcc-bug.patch
|
||||
Patch3: kexec-tools-edd-support
|
||||
|
||||
%description
|
||||
Kexec is a user space utility for loading another kernel and asking the
|
||||
@ -53,6 +54,7 @@ Authors:
|
||||
%setup -q -n kexec-tools-%{package_version}
|
||||
%patch1 -p1
|
||||
%patch2 -p1
|
||||
%patch3 -p1
|
||||
|
||||
%build
|
||||
%{?suse_update_config -f}
|
||||
@ -88,6 +90,8 @@ install -c -m 0644 kexec/kexec.8 $RPM_BUILD_ROOT%{_mandir}/man8
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue May 13 2008 bwalle@suse.de
|
||||
- implement EDD (bnc#383210)
|
||||
* Tue Mar 25 2008 bwalle@suse.de
|
||||
- update to kexec-tools-testing v20080324
|
||||
o tarball update (version), no functional changes between
|
||||
@ -172,7 +176,7 @@ install -c -m 0644 kexec/kexec.8 $RPM_BUILD_ROOT%{_mandir}/man8
|
||||
package
|
||||
* Wed Aug 29 2007 bwalle@suse.de
|
||||
- add reset_devices kernel parameter as default
|
||||
* Sat Aug 25 2007 olh@suse.de
|
||||
* Sun Aug 26 2007 olh@suse.de
|
||||
- do not require kdump-helpers on s390
|
||||
* Fri Jul 27 2007 bwalle@suse.de
|
||||
- update documentation for deleting all dumps (#302257)
|
||||
|
Loading…
Reference in New Issue
Block a user