diff --git a/kexec-tools-edd-support b/kexec-tools-edd-support new file mode 100644 index 0000000..acf3347 --- /dev/null +++ b/kexec-tools-edd-support @@ -0,0 +1,386 @@ +From: Bernhard Walle +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 + +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -27,6 +29,7 @@ + #include + #include + #include ++#include + #include + #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); + } diff --git a/kexec-tools.changes b/kexec-tools.changes index 8538174..8f6da4f 100644 --- a/kexec-tools.changes +++ b/kexec-tools.changes @@ -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 diff --git a/kexec-tools.spec b/kexec-tools.spec index b95d2fd..dee184d 100644 --- a/kexec-tools.spec +++ b/kexec-tools.spec @@ -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)